diff --git a/.gitignore b/.gitignore index 1bc915c5cb..2bdc199561 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,73 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user +*.userosscache *.sln.docstates -# Build results +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +# Build results [Dd]ebug/ +[Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ -build/ +x86/ +bld/ [Bb]in/ [Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ -# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets -!packages/*/build/ +# Visual Studio 2017 auto generated files +Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c +*_i.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -43,21 +82,34 @@ build/ *.vssscc .builds *.pidb -*.log +*.svclog *.scc +# Chutzpah Test files +_Chutzpah* + # Visual C++ cache files ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ # Guidance Automation Toolkit *.gpState @@ -65,6 +117,10 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode # TeamCity is a build add-in _TeamCity* @@ -72,9 +128,25 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + # NCrunch -*.ncrunch* +_NCrunch_* .*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ # Installshield output folder [Ee]xpress/ @@ -93,64 +165,170 @@ DocProject/Help/html publish/ # Publish Web Output -*.Publish.xml +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets -# Windows Azure Build Output -csx +# Microsoft Azure Build Output +csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl -*.[Pp]ublish.xml +*.dbproj.schemaview +*.jfm *.pfx *.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # RIA/Silverlight projects Generated_Code/ -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files -App_Data/*.mdf -App_Data/*.ldf +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs +# OpenCover UI analysis results +OpenCover/ -#LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml +# Azure Stream Analytics local run output +ASALocalRun/ -# ========================= -# Windows detritus -# ========================= +# MSBuild Binary and Structured Log +*.binlog -# Windows image file caches -Thumbs.db -ehthumbs.db +# NVidia Nsight GPU debugger configuration file +*.nvuser -# Folder config file -Desktop.ini +# MFractors (Xamarin productivity tool) working folder +.mfractor/ -# Recycle Bin used on file shares -$RECYCLE.BIN/ +!DS4WinWPF/libs/x64/ +!DS4WinWPF/libs/x86/ -# Mac desktop service store files -.DS_Store diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config deleted file mode 100644 index c2abfb4801..0000000000 --- a/.vs/config/applicationhost.config +++ /dev/null @@ -1,1030 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DS4Windows.sln b/DS4Windows.sln deleted file mode 100644 index 916f340568..0000000000 --- a/DS4Windows.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DS4Windows", "DS4Windows\DS4Windows.csproj", "{7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Debug|x64.ActiveCfg = Debug|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Debug|x64.Build.0 = Debug|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Release|Any CPU.Build.0 = Release|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Release|x64.ActiveCfg = Release|Any CPU - {7B9354BF-AF82-4CCB-A83D-4BEB1E9D8C96}.Release|x64.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/DS4Windows/App.xaml b/DS4Windows/App.xaml new file mode 100644 index 0000000000..fd36568217 --- /dev/null +++ b/DS4Windows/App.xaml @@ -0,0 +1,25 @@ + + + + + + diff --git a/DS4Windows/App.xaml.cs b/DS4Windows/App.xaml.cs new file mode 100644 index 0000000000..dd75997041 --- /dev/null +++ b/DS4Windows/App.xaml.cs @@ -0,0 +1,510 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Linq; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Interop; +using WPFLocalizeExtension.Engine; +using NLog; +using System.Windows.Media; + +namespace DS4WinWPF +{ + /// + /// Interaction logic for App.xaml + /// + [System.Security.SuppressUnmanagedCodeSecurity] + public partial class App : Application + { + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll", EntryPoint = "FindWindow")] + private static extern IntPtr FindWindow(string sClass, string sWindow); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] + private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref COPYDATASTRUCT lParam); + + [StructLayout(LayoutKind.Sequential)] + public struct COPYDATASTRUCT + { + public IntPtr dwData; + public int cbData; + public IntPtr lpData; + } + + private Thread controlThread; + public static DS4Windows.ControlService rootHub; + public static HttpClient requestClient; + private bool skipSave; + private bool runShutdown; + private bool exitApp; + private Thread testThread; + private bool exitComThread = false; + private const string SingleAppComEventName = "{a52b5b20-d9ee-4f32-8518-307fa14aa0c6}"; + private EventWaitHandle threadComEvent = null; + private Timer collectTimer; + private static LoggerHolder logHolder; + + private MemoryMappedFile ipcClassNameMMF = null; // MemoryMappedFile for inter-process communication used to hold className of DS4Form window + private MemoryMappedViewAccessor ipcClassNameMMA = null; + + private void Application_Startup(object sender, StartupEventArgs e) + { + runShutdown = true; + skipSave = true; + + ArgumentParser parser = new ArgumentParser(); + parser.Parse(e.Args); + CheckOptions(parser); + + if (exitApp) + { + return; + } + + try + { + Process.GetCurrentProcess().PriorityClass = + ProcessPriorityClass.High; + } + catch { } // Ignore problems raising the priority. + + // Force Normal IO Priority + IntPtr ioPrio = new IntPtr(2); + DS4Windows.Util.NtSetInformationProcess(Process.GetCurrentProcess().Handle, + DS4Windows.Util.PROCESS_INFORMATION_CLASS.ProcessIoPriority, ref ioPrio, 4); + + // Force Normal Page Priority + IntPtr pagePrio = new IntPtr(5); + DS4Windows.Util.NtSetInformationProcess(Process.GetCurrentProcess().Handle, + DS4Windows.Util.PROCESS_INFORMATION_CLASS.ProcessPagePriority, ref pagePrio, 4); + + try + { + // another instance is already running if OpenExisting succeeds. + threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName, + System.Security.AccessControl.EventWaitHandleRights.Synchronize | + System.Security.AccessControl.EventWaitHandleRights.Modify); + threadComEvent.Set(); // signal the other instance. + threadComEvent.Close(); + Current.Shutdown(); // Quit temp instance + return; + } + catch { /* don't care about errors */ } + + // Create the Event handle + threadComEvent = new EventWaitHandle(false, EventResetMode.ManualReset, SingleAppComEventName); + CreateTempWorkerThread(); + + CreateControlService(); + RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; + + DS4Windows.Global.FindConfigLocation(); + bool firstRun = DS4Windows.Global.firstRun; + if (firstRun) + { + DS4Forms.SaveWhere savewh = new DS4Forms.SaveWhere(false); + savewh.ShowDialog(); + } + + DS4Windows.Global.Load(); + if (!CreateConfDirSkeleton()) + { + MessageBox.Show($"Cannot create config folder structure in {DS4Windows.Global.appdatapath}. Exiting", + "DS4Windows", MessageBoxButton.OK, MessageBoxImage.Error); + Current.Shutdown(1); + } + + logHolder = new LoggerHolder(rootHub); + DispatcherUnhandledException += App_DispatcherUnhandledException; + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Logger logger = logHolder.Logger; + string version = DS4Windows.Global.exeversion; + logger.Info($"DS4Windows version {version}"); + //logger.Info("DS4Windows version 2.0"); + logger.Info("Logger created"); + + //DS4Windows.Global.ProfilePath[0] = "mixed"; + //DS4Windows.Global.LoadProfile(0, false, rootHub, false, false); + if (firstRun) + { + logger.Info("No config found. Creating default config"); + //Directory.CreateDirectory(DS4Windows.Global.appdatapath); + AttemptSave(); + + //Directory.CreateDirectory(DS4Windows.Global.appdatapath + @"\Profiles\"); + //Directory.CreateDirectory(DS4Windows.Global.appdatapath + @"\Macros\"); + DS4Windows.Global.SaveAsNewProfile(0, "Default"); + DS4Windows.Global.ProfilePath[0] = DS4Windows.Global.OlderProfilePath[0] = "Default"; + /*DS4Windows.Global.ProfilePath[1] = DS4Windows.Global.OlderProfilePath[1] = "Default"; + DS4Windows.Global.ProfilePath[2] = DS4Windows.Global.OlderProfilePath[2] = "Default"; + DS4Windows.Global.ProfilePath[3] = DS4Windows.Global.OlderProfilePath[3] = "Default"; + */ + logger.Info("Default config created"); + } + + skipSave = false; + + if (!DS4Windows.Global.LoadActions()) + { + DS4Windows.Global.CreateStdActions(); + } + + SetUICulture(DS4Windows.Global.UseLang); + DS4Windows.Global.LoadLinkedProfiles(); + DS4Forms.MainWindow window = new DS4Forms.MainWindow(parser); + MainWindow = window; + window.Show(); + HwndSource source = PresentationSource.FromVisual(window) as HwndSource; + CreateIPCClassNameMMF(source.Handle); + + window.CheckMinStatus(); + rootHub.LaunchHidGuardHelper(); + window.LateChecks(parser); + } + + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (!Current.Dispatcher.CheckAccess()) + { + Logger logger = logHolder.Logger; + Exception exp = e.ExceptionObject as Exception; + logger.Error($"Thread App Crashed with message {exp.Message}"); + logger.Error(exp.ToString()); + //LogManager.Flush(); + //LogManager.Shutdown(); + if (e.IsTerminating) + { + Dispatcher.Invoke(() => + { + rootHub?.PrepareAbort(); + CleanShutdown(); + }); + } + } + } + + private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + //Console.WriteLine("App Crashed"); + //Console.WriteLine(e.Exception.StackTrace); + Logger logger = logHolder.Logger; + logger.Error($"App Crashed with message {e.Exception.Message}"); + logger.Error(e.Exception.ToString()); + LogManager.Flush(); + LogManager.Shutdown(); + } + + private bool CreateConfDirSkeleton() + { + bool result = true; + try + { + Directory.CreateDirectory(DS4Windows.Global.appdatapath); + Directory.CreateDirectory(DS4Windows.Global.appdatapath + @"\Profiles\"); + Directory.CreateDirectory(DS4Windows.Global.appdatapath + @"\Logs\"); + //Directory.CreateDirectory(DS4Windows.Global.appdatapath + @"\Macros\"); + } + catch (UnauthorizedAccessException) + { + result = false; + } + + + return result; + } + + private void AttemptSave() + { + if (!DS4Windows.Global.Save()) //if can't write to file + { + if (MessageBox.Show("Cannot write at current location\nCopy Settings to appdata?", "DS4Windows", + MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) + { + try + { + Directory.CreateDirectory(DS4Windows.Global.appDataPpath); + File.Copy(DS4Windows.Global.exedirpath + "\\Profiles.xml", + DS4Windows.Global.appDataPpath + "\\Profiles.xml"); + File.Copy(DS4Windows.Global.exedirpath + "\\Auto Profiles.xml", + DS4Windows.Global.appDataPpath + "\\Auto Profiles.xml"); + Directory.CreateDirectory(DS4Windows.Global.appDataPpath + "\\Profiles"); + foreach (string s in Directory.GetFiles(DS4Windows.Global.exedirpath + "\\Profiles")) + { + File.Copy(s, DS4Windows.Global.appDataPpath + "\\Profiles\\" + Path.GetFileName(s)); + } + } + catch { } + MessageBox.Show("Copy complete, please relaunch DS4Windows and remove settings from Program Directory", + "DS4Windows"); + } + else + { + MessageBox.Show("DS4Windows cannot edit settings here, This will now close", + "DS4Windows"); + } + + DS4Windows.Global.appdatapath = null; + skipSave = true; + Current.Shutdown(); + return; + } + } + + private void CheckOptions(ArgumentParser parser) + { + if (parser.HasErrors) + { + runShutdown = false; + exitApp = true; + Current.Shutdown(1); + } + else if (parser.Driverinstall) + { + CreateBaseThread(); + DS4Forms.WelcomeDialog dialog = new DS4Forms.WelcomeDialog(true); + dialog.ShowDialog(); + runShutdown = false; + exitApp = true; + Current.Shutdown(); + } + else if (parser.ReenableDevice) + { + DS4Windows.DS4Devices.reEnableDevice(parser.DeviceInstanceId); + runShutdown = false; + exitApp = true; + Current.Shutdown(); + } + else if (parser.Runtask) + { + StartupMethods.LaunchOldTask(); + runShutdown = false; + exitApp = true; + Current.Shutdown(); + } + else if (parser.Command) + { + IntPtr hWndDS4WindowsForm = IntPtr.Zero; + hWndDS4WindowsForm = FindWindow(ReadIPCClassNameMMF(), "DS4Windows"); + if (hWndDS4WindowsForm != IntPtr.Zero) + { + COPYDATASTRUCT cds; + cds.lpData = IntPtr.Zero; + + try + { + cds.dwData = IntPtr.Zero; + cds.cbData = parser.CommandArgs.Length; + cds.lpData = Marshal.StringToHGlobalAnsi(parser.CommandArgs); + SendMessage(hWndDS4WindowsForm, DS4Forms.MainWindow.WM_COPYDATA, IntPtr.Zero, ref cds); + } + finally + { + if (cds.lpData != IntPtr.Zero) + Marshal.FreeHGlobal(cds.lpData); + } + } + + runShutdown = false; + exitApp = true; + Current.Shutdown(); + } + } + + private void CreateControlService() + { + controlThread = new Thread(() => { + rootHub = new DS4Windows.ControlService(); + DS4Windows.Program.rootHub = rootHub; + requestClient = new HttpClient(); + collectTimer = new Timer(GarbageTask, null, 30000, 30000); + + }); + controlThread.Priority = ThreadPriority.Normal; + controlThread.IsBackground = true; + controlThread.Start(); + while (controlThread.IsAlive) + Thread.SpinWait(500); + } + + private void CreateBaseThread() + { + controlThread = new Thread(() => { + DS4Windows.Program.rootHub = rootHub; + requestClient = new HttpClient(); + collectTimer = new Timer(GarbageTask, null, 30000, 30000); + }); + controlThread.Priority = ThreadPriority.Normal; + controlThread.IsBackground = true; + controlThread.Start(); + while (controlThread.IsAlive) + Thread.SpinWait(500); + } + + private void GarbageTask(object state) + { + GC.Collect(0, GCCollectionMode.Forced, false); + } + + private void CreateTempWorkerThread() + { + testThread = new Thread(SingleAppComThread_DoWork); + testThread.Priority = ThreadPriority.Lowest; + testThread.IsBackground = true; + testThread.Start(); + } + + private void SingleAppComThread_DoWork() + { + while (!exitComThread) + { + // check for a signal. + if (threadComEvent.WaitOne()) + { + threadComEvent.Reset(); + // The user tried to start another instance. We can't allow that, + // so bring the other instance back into view and enable that one. + // That form is created in another thread, so we need some thread sync magic. + if (!exitComThread) + { + Dispatcher.BeginInvoke((Action)(() => + { + MainWindow.Show(); + MainWindow.WindowState = WindowState.Normal; + })); + } + } + } + } + + public void CreateIPCClassNameMMF(IntPtr hWnd) + { + if (ipcClassNameMMA != null) return; // Already holding a handle to MMF file. No need to re-write the data + + try + { + StringBuilder wndClassNameStr = new StringBuilder(128); + if (GetClassName(hWnd, wndClassNameStr, wndClassNameStr.Capacity) != 0 && wndClassNameStr.Length > 0) + { + byte[] buffer = ASCIIEncoding.ASCII.GetBytes(wndClassNameStr.ToString()); + + ipcClassNameMMF = MemoryMappedFile.CreateNew("DS4Windows_IPCClassName.dat", 128); + ipcClassNameMMA = ipcClassNameMMF.CreateViewAccessor(0, buffer.Length); + ipcClassNameMMA.WriteArray(0, buffer, 0, buffer.Length); + // The MMF file is alive as long this process holds the file handle open + } + } + catch (Exception) + { + /* Eat all exceptions because errors here are not fatal for DS4Win */ + } + } + + private string ReadIPCClassNameMMF() + { + MemoryMappedFile mmf = null; + MemoryMappedViewAccessor mma = null; + + try + { + byte[] buffer = new byte[128]; + mmf = MemoryMappedFile.OpenExisting("DS4Windows_IPCClassName.dat"); + mma = mmf.CreateViewAccessor(0, 128); + mma.ReadArray(0, buffer, 0, buffer.Length); + return ASCIIEncoding.ASCII.GetString(buffer); + } + catch (Exception) + { + // Eat all exceptions + } + finally + { + if (mma != null) mma.Dispose(); + if (mmf != null) mmf.Dispose(); + } + + return null; + } + + private void SetUICulture(string culture) + { + try + { + //CultureInfo ci = new CultureInfo("ja"); + CultureInfo ci = CultureInfo.GetCultureInfo(culture); + LocalizeDictionary.Instance.SetCurrentThreadCulture = true; + LocalizeDictionary.Instance.Culture = ci; + // fixes the culture in threads + CultureInfo.DefaultThreadCurrentCulture = ci; + CultureInfo.DefaultThreadCurrentUICulture = ci; + //DS4WinWPF.Properties.Resources.Culture = ci; + Thread.CurrentThread.CurrentCulture = ci; + Thread.CurrentThread.CurrentUICulture = ci; + } + catch (CultureNotFoundException) { /* Skip setting culture that we cannot set */ } + } + + private void Application_Exit(object sender, ExitEventArgs e) + { + if (runShutdown) + { + Logger logger = logHolder.Logger; + logger.Info("Request App Shutdown"); + CleanShutdown(); + } + } + + private void Application_SessionEnding(object sender, SessionEndingCancelEventArgs e) + { + Logger logger = logHolder.Logger; + logger.Info("User Session Ending"); + CleanShutdown(); + } + + private void CleanShutdown() + { + if (runShutdown) + { + if (rootHub != null) + { + Task.Run(() => + { + if (rootHub.running) + rootHub.Stop(); + }).Wait(); + } + + if (!skipSave) + { + DS4Windows.Global.Save(); + } + + exitComThread = true; + if (threadComEvent != null) + { + threadComEvent.Set(); // signal the other instance. + while (testThread.IsAlive) + Thread.SpinWait(500); + threadComEvent.Close(); + } + + if (ipcClassNameMMA != null) ipcClassNameMMA.Dispose(); + if (ipcClassNameMMF != null) ipcClassNameMMF.Dispose(); + + LogManager.Flush(); + LogManager.Shutdown(); + } + } + } +} diff --git a/DS4Windows/ArgumentParser.cs b/DS4Windows/ArgumentParser.cs new file mode 100644 index 0000000000..37a7693dd6 --- /dev/null +++ b/DS4Windows/ArgumentParser.cs @@ -0,0 +1,97 @@ + +using System.Collections.Generic; + +namespace DS4WinWPF +{ + public class ArgumentParser + { + private bool mini; + private bool stop; + private bool driverinstall; + private bool reenableDevice; + private string deviceInstanceId; + private bool runtask; + private bool command; + private string commandArgs; + + private Dictionary errors = + new Dictionary(); + + public bool Mini { get => mini; } + public bool Stop { get => stop; } + public bool Driverinstall { get => driverinstall; } + public bool ReenableDevice { get => reenableDevice; } + public bool Runtask { get => runtask; } + public bool Command { get => command; } + public string DeviceInstanceId { get => deviceInstanceId; } + public string CommandArgs { get => commandArgs; } + public Dictionary Errors { get => errors; } + + public bool HasErrors => errors.Count > 0; + + public void Parse(string[] args) + { + errors.Clear(); + //foreach (string arg in args) + for (int i = 0; i < args.Length; i++) + { + string arg = args[i]; + switch(arg) + { + case "driverinstall": + case "-driverinstall": + driverinstall = true; + break; + + case "re-enabledevice": + case "-re-enabledevice": + reenableDevice = true; + if (i + 1 < args.Length) + { + deviceInstanceId = args[++i]; + } + + break; + + case "runtask": + case "-runtask": + runtask = true; + break; + + case "-stop": + stop = true; + break; + + case "-m": + mini = true; + break; + + case "command": + case "-command": + command = true; + if (i + 1 < args.Length) + { + i++; + string temp = args[i]; + if (temp.Length > 0 && temp.Length <= 256) + { + commandArgs = temp; + } + else + { + command = false; + errors["Command"] = "Command length is invalid"; + } + } + else + { + errors["Command"] = "Command string not given"; + } + break; + + default: break; + } + } + } + } +} diff --git a/DS4Windows/AutoProfileChecker.cs b/DS4Windows/AutoProfileChecker.cs new file mode 100644 index 0000000000..8dea7e575a --- /dev/null +++ b/DS4Windows/AutoProfileChecker.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Diagnostics; // StopWatch +using System.Threading; // Sleep +using System.Threading.Tasks; +using DS4Windows; + +namespace DS4WinWPF +{ + [SuppressUnmanagedCodeSecurity] + public class AutoProfileChecker + { + private AutoProfileHolder profileHolder; + private IntPtr prevForegroundWnd = IntPtr.Zero; + private uint prevForegroundProcessID; + private string prevForegroundProcessName = string.Empty; + private string prevForegroundWndTitleName = string.Empty; + private StringBuilder autoProfileCheckTextBuilder = new StringBuilder(1000); + private int autoProfileDebugLogLevel = 0; + private bool turnOffTemp; + private AutoProfileEntity tempAutoProfile; + private bool running; + + public int AutoProfileDebugLogLevel { get => autoProfileDebugLogLevel; set => autoProfileDebugLogLevel = value; } + public bool Running { get => running; set => running = value; } + + public delegate void ChangeServiceHandler(AutoProfileChecker sender, bool state); + public event ChangeServiceHandler RequestServiceChange; + + public AutoProfileChecker(AutoProfileHolder holder) + { + profileHolder = holder; + } + + public void Process() + { + string topProcessName, topWindowTitle; + bool turnOffDS4WinApp = false; + AutoProfileEntity matchedProfileEntity = null; + + if (GetTopWindowName(out topProcessName, out topWindowTitle)) + { + // Find a profile match based on autoprofile program path and wnd title list. + // The same program may set different profiles for each of the controllers, so we need an array of newProfileName[controllerIdx] values. + for (int i = 0, pathsLen = profileHolder.AutoProfileColl.Count; i < pathsLen; i++) + { + AutoProfileEntity tempEntity = profileHolder.AutoProfileColl[i]; + if (tempEntity.IsMatch(topProcessName, topWindowTitle)) + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. Rule#{i + 1} Path={tempEntity.path} Title={tempEntity.title}", false); + + // Matching autoprofile rule found + turnOffDS4WinApp = tempEntity.Turnoff; + matchedProfileEntity = tempEntity; + break; + } + } + + if (matchedProfileEntity != null) + { + bool forceLoadProfile = false; + + if (!turnOffDS4WinApp && turnOffTemp) + { + // DS4Win was temporarily turned off by another auto-profile rule. Turn DS4Win on before trying to load a new profile because otherwise the new profile won't do anything. + // Force load the profile when DS4Win service afer waking up DS4Win service to make sure that the new profile will be active. + turnOffTemp = false; + SetAndWaitServiceStatus(true); + forceLoadProfile = true; + } + + // Program match found. Check if the new profile is different than current profile of the controller. Load the new profile only if it is not already loaded. + for (int j = 0; j < 4; j++) + { + string tempname = matchedProfileEntity.ProfileNames[j]; + if (tempname != string.Empty && tempname != "(none)") + { + if ((Global.useTempProfile[j] && tempname != Global.tempprofilename[j]) || + (!Global.useTempProfile[j] && tempname != Global.ProfilePath[j]) || + forceLoadProfile) + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. LoadProfile Controller {j + 1}={tempname}", false); + + Global.LoadTempProfile(j, tempname, true, Program.rootHub); // j is controller index, i is filename + //if (LaunchProgram[j] != string.Empty) Process.Start(LaunchProgram[j]); + } + else + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. LoadProfile Controller {j + 1}={tempname} (already loaded)", false); + } + } + } + + if (turnOffDS4WinApp) + { + turnOffTemp = true; + if (App.rootHub.running) + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. Turning DS4Windows temporarily off", false); + + SetAndWaitServiceStatus(false); + } + } + + tempAutoProfile = matchedProfileEntity; + } + else if (tempAutoProfile != null) + { + if (turnOffTemp && DS4Windows.Global.AutoProfileRevertDefaultProfile) + { + turnOffTemp = false; + if (!App.rootHub.running) + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. Turning DS4Windows on before reverting to default profile", false); + + SetAndWaitServiceStatus(true); + } + } + + tempAutoProfile = null; + for (int j = 0; j < 4; j++) + { + if (Global.useTempProfile[j]) + { + if (DS4Windows.Global.AutoProfileRevertDefaultProfile) + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. Unknown process. Reverting to default profile. Controller {j + 1}={Global.ProfilePath[j]} (default)", false); + + Global.LoadProfile(j, false, Program.rootHub); + } + else + { + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. Unknown process. Existing profile left as active. Controller {j + 1}={Global.tempprofilename[j]}", false); + } + } + } + } + } + } + + private bool GetTopWindowName(out string topProcessName, out string topWndTitleName) + { + IntPtr hWnd = GetForegroundWindow(); + if (hWnd == IntPtr.Zero) + { + // Top window unknown or cannot acquire a handle. Return FALSE and return unknown process and wndTitle values + prevForegroundWnd = IntPtr.Zero; + prevForegroundProcessID = 0; + topProcessName = topWndTitleName = String.Empty; + return false; + } + + // + // If this function was called from "auto-profile watcher timer" then check cached "previous hWnd handle". If the current hWnd is the same + // as during the previous check then return cached previous wnd and name values (ie. foreground app and window are assumed to be the same, so no need to re-query names). + // This should optimize the auto-profile timer check process and causes less burden to .NET GC collector because StringBuffer is not re-allocated every second. + // + // Note! hWnd handles may be re-cycled but not during the lifetime of the window. This "cache" optimization still works because when an old window is closed + // then foreground window changes to something else and the cached prevForgroundWnd variable is updated to store the new hWnd handle. + // It doesn't matter even when the previously cached handle is recycled by WinOS to represent some other window (it is no longer used as a cached value anyway). + // + if (hWnd == prevForegroundWnd) + { + // The active window is still the same. Return cached process and wndTitle values and FALSE to indicate caller that no changes since the last call of this method + topProcessName = prevForegroundProcessName; + topWndTitleName = prevForegroundWndTitleName; + return false; + } + + prevForegroundWnd = hWnd; + + IntPtr hProcess = IntPtr.Zero; + uint lpdwProcessId = 0; + GetWindowThreadProcessId(hWnd, out lpdwProcessId); + + if (lpdwProcessId == prevForegroundProcessID) + { + topProcessName = prevForegroundProcessName; + } + else + { + prevForegroundProcessID = lpdwProcessId; + + hProcess = OpenProcess(0x0410, false, lpdwProcessId); + if (hProcess != IntPtr.Zero) GetModuleFileNameEx(hProcess, IntPtr.Zero, autoProfileCheckTextBuilder, autoProfileCheckTextBuilder.Capacity); + else autoProfileCheckTextBuilder.Clear(); + + prevForegroundProcessName = topProcessName = autoProfileCheckTextBuilder.Replace('/', '\\').ToString().ToLower(); + } + + GetWindowText(hWnd, autoProfileCheckTextBuilder, autoProfileCheckTextBuilder.Capacity); + prevForegroundWndTitleName = topWndTitleName = autoProfileCheckTextBuilder.ToString().ToLower(); + + + if (hProcess != IntPtr.Zero) CloseHandle(hProcess); + + if (autoProfileDebugLogLevel > 0) + DS4Windows.AppLogger.LogToGui($"DEBUG: Auto-Profile. PID={lpdwProcessId} Path={topProcessName} | WND={hWnd} Title={topWndTitleName}", false); + + return true; + } + + private void SetAndWaitServiceStatus(bool serviceRunningStatus) + { + // Start or Stop the service only if it is not already in the requested state + if (App.rootHub.running != serviceRunningStatus) + { + RequestServiceChange?.Invoke(this, serviceRunningStatus); + + // Wait until DS4Win app service is running or stopped (as requested by serviceRunningStatus value) or timeout. + // LoadProfile call fails if a new profile is loaded while DS4Win service is still in stopped state (ie the loaded temp profile doesn't do anything). + Stopwatch sw = new Stopwatch(); + sw.Start(); + while (App.rootHub.running != serviceRunningStatus && sw.Elapsed.TotalSeconds < 10) + { + Thread.SpinWait(1000); + } + Thread.SpinWait(1000); + } + } + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("kernel32.dll")] + private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); + + [DllImport("kernel32.dll")] + private static extern bool CloseHandle(IntPtr handle); + + [DllImport("psapi.dll")] + private static extern uint GetModuleFileNameEx(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nSize); + } +} diff --git a/DS4Windows/AutoProfileHolder.cs b/DS4Windows/AutoProfileHolder.cs new file mode 100644 index 0000000000..9152e2a778 --- /dev/null +++ b/DS4Windows/AutoProfileHolder.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Xml; + +namespace DS4WinWPF +{ + public class AutoProfileHolder + { + private object _colLockobj = new object(); + private ObservableCollection autoProfileColl; + public ObservableCollection AutoProfileColl { get => autoProfileColl; } + //public Dictionary AutoProfileDict { get => autoProfileDict; } + + //private Dictionary autoProfileDict; + + public AutoProfileHolder() + { + autoProfileColl = new ObservableCollection(); + //autoProfileDict = new Dictionary(); + Load(); + + BindingOperations.EnableCollectionSynchronization(autoProfileColl, _colLockobj); + } + + private void Load() + { + try + { + XmlDocument doc = new XmlDocument(); + + if (!File.Exists(DS4Windows.Global.appdatapath + "\\Auto Profiles.xml")) + return; + + doc.Load(DS4Windows.Global.appdatapath + "\\Auto Profiles.xml"); + XmlNodeList programslist = doc.SelectNodes("Programs/Program"); + foreach (XmlNode x in programslist) + { + string path = x.Attributes["path"]?.Value ?? string.Empty; + string title = x.Attributes["title"]?.Value ?? string.Empty; + AutoProfileEntity autoprof = new AutoProfileEntity(path, title); + + XmlNode item; + for (int i = 0; i < 4; i++) + { + item = x.SelectSingleNode($"Controller{i+1}"); + if (item != null) + { + autoprof.ProfileNames[i] = item.InnerText; + } + } + + item = x.SelectSingleNode($"TurnOff"); + if (item != null && bool.TryParse(item.InnerText, out bool turnoff)) + { + autoprof.Turnoff = turnoff; + } + + autoProfileColl.Add(autoprof); + //autoProfileDict.Add(path, autoprof); + } + } + catch (Exception) { } + } + + public bool Save(string m_Profile) + { + XmlDocument doc = new XmlDocument(); + XmlNode Node; + bool saved = true; + try + { + Node = doc.CreateXmlDeclaration("1.0", "utf-8", string.Empty); + doc.AppendChild(Node); + + Node = doc.CreateComment(string.Format(" Auto-Profile Configuration Data. {0} ", DateTime.Now)); + doc.AppendChild(Node); + + Node = doc.CreateWhitespace("\r\n"); + doc.AppendChild(Node); + + Node = doc.CreateNode(XmlNodeType.Element, "Programs", ""); + doc.AppendChild(Node); + foreach (AutoProfileEntity entity in autoProfileColl) + { + XmlElement el = doc.CreateElement("Program"); + el.SetAttribute("path", entity.Path); + if (!string.IsNullOrEmpty(entity.Title)) + { + el.SetAttribute("title", entity.Title); + } + + el.AppendChild(doc.CreateElement("Controller1")).InnerText = entity.ProfileNames[0]; + el.AppendChild(doc.CreateElement("Controller2")).InnerText = entity.ProfileNames[1]; + el.AppendChild(doc.CreateElement("Controller3")).InnerText = entity.ProfileNames[2]; + el.AppendChild(doc.CreateElement("Controller4")).InnerText = entity.ProfileNames[3]; + el.AppendChild(doc.CreateElement("TurnOff")).InnerText = entity.Turnoff.ToString(); + + Node.AppendChild(el); + } + + doc.Save(m_Profile); + } + catch (Exception) { saved = false; } + return saved; + } + + public void Remove(AutoProfileEntity item) + { + //autoProfileDict.Remove(item.Path); + autoProfileColl.Remove(item); + } + } + + public class AutoProfileEntity + { + public string path = string.Empty; + public string title = string.Empty; + private string path_lowercase; + private string title_lowercase; + private bool turnoff; + private string[] profileNames = new string[4] { string.Empty, string.Empty, + string.Empty, string.Empty }; + public const string NONE_STRING = "(none)"; + + public string Path { get => path; set => SetSearchPath(value); } + public string Title { get => title; set => SetSearchTitle(value); } + public bool Turnoff { get => turnoff; set => turnoff = value; } + public string[] ProfileNames { get => profileNames; set => profileNames = value; } + + public AutoProfileEntity(string pathStr, string titleStr) + { + // Initialize autoprofile search keywords(xxx_tolower).To improve performance the search keyword is pre - calculated in xxx_tolower variables, + // so autoprofile timer thread doesn't have to create substrings/replace/tolower string instances every second over and over again. + SetSearchPath(pathStr); + SetSearchTitle(titleStr); + } + + public bool IsMatch(string searchPath, string searchTitle) + { + bool bPathMatched = true; + bool bTitleMwatched = true; + + if (!string.IsNullOrEmpty(path_lowercase)) + { + bPathMatched = (path_lowercase == searchPath + || (path[0] == '^' && searchPath.StartsWith(path_lowercase)) + || (path[path.Length - 1] == '$' && searchPath.EndsWith(path_lowercase)) + || (path[0] == '*' && searchPath.Contains(path_lowercase)) + ); + } + + if (bPathMatched && !string.IsNullOrEmpty(title_lowercase)) + { + bTitleMwatched = (title_lowercase == searchTitle + || (title[0] == '^' && searchTitle.StartsWith(title_lowercase)) + || (title[title.Length - 1] == '$' && searchTitle.EndsWith(title_lowercase)) + || (title[0] == '*' && searchTitle.Contains(title_lowercase)) + ); + } + + // If both path and title defined in autoprofile entry then do AND condition (ie. both path and title should match) + return bPathMatched && bTitleMwatched; + } + + private void SetSearchPath(string pathStr) + { + if (!string.IsNullOrEmpty(pathStr)) + { + path = pathStr; + path_lowercase = path.ToLower().Replace('/', '\\'); + + if (path.Length >= 2) + { + if (path[0] == '^') path_lowercase = path_lowercase.Substring(1); + else if (path[path.Length - 1] == '$') path_lowercase = path_lowercase.Substring(0, path_lowercase.Length - 1); + else if (path[0] == '*') path_lowercase = path_lowercase.Substring(1); + } + } + else path = path_lowercase = string.Empty; + } + + private void SetSearchTitle(string titleStr) + { + if (!string.IsNullOrEmpty(titleStr)) + { + title = titleStr; + title_lowercase = title.ToLower(); + + if (title.Length >= 2) + { + if (title[0] == '^') title_lowercase = title_lowercase.Substring(1); + else if (title[title.Length - 1] == '$') title_lowercase = title_lowercase.Substring(0, title_lowercase.Length - 1); + else if (title[0] == '*') title_lowercase = title_lowercase.Substring(1); + } + } + else title = title_lowercase = string.Empty; + } + } +} diff --git a/DS4Windows/BezierCurveEditor/BezierCurve.cs b/DS4Windows/BezierCurveEditor/BezierCurve.cs new file mode 100644 index 0000000000..6d5edd5fb8 --- /dev/null +++ b/DS4Windows/BezierCurveEditor/BezierCurve.cs @@ -0,0 +1,380 @@ +/* MIT License + * + * KeySpline - use bezier curve for transition easing function + * Copyright (c) 2012 Gaetan Renaudeau (GRE) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/* KeySpline - use bezier curve for transition easing function is inspired from Firefox's nsSMILKeySpline.cpp */ + +/* +* This file contains the original bezier curve code (see comments above) and calculations ported as C# code. The original code was in JavaScript. +* +* This file has few customizations and optimizations for the needs of DS4Windows application (see https://github.com/Ryochan7/DS4Windows). +* MIT License. Permission is hereby granted, free of charge, to any person to do whatever they want with this C# ported version of BezierCurve calculation code +* as long this part of the code is open sourced and usage is in compliance with the above shown original license, also. +* +* Copyright (c) 2019, MIKA-N (https://github.com/mika-n). +* +* The original JavaScript version of bezier easing made by GRE (https://github.com/gre/bezier-easing). +* +* Usage: +* BezierCurve.InitBezierCurve = Initialize bezier curve and output lookup table. Must be called at least once before calling GetBezierEasing method (or accessing lookup table directly) to re-map analog axis input. +* BezierCurve.GetBezierEasing = Return re-mapped output value for an input axis value (or alternatively directly accessing the lookup table BezierCurve.arrayBezierLUT[inputVal] if even tiny CPU cycles matter) +* +*/ +using System; + +namespace DS4Windows +{ + public class BezierCurve + { + public enum AxisType { LSRS, L2R2, SA }; + + private static int kSplineTableSize = 11; + private static double kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); + private double[] arraySampleValues; + + // These values are established by empiricism with tests (tradeoff: performance VS precision) (comment by GRE) + private static int NEWTON_ITERATIONS = 4; + private static double NEWTON_MIN_SLOPE = 0.001; + private static double SUBDIVISION_PRECISION = 0.0000001; + private static int SUBDIVISION_MAX_ITERATIONS = 10; + + private double mX1 = 0, mY1 = 0, mX2 = 0, mY2 = 0; // Bezier curve definition (0, 0, 0, 0 = Linear. 99, 99, 0, 0 = Pre-defined hard-coded EnhancedPrecision curve) + + // Set or Get string representation of the bezier curve definition value (Note! Set doesn't initialize the lookup table. InitBezierCurve needs to be called to actually initalize the calculation) + public string AsString + { + get { return ($"{mX1}, {mY1}, {mX2}, {mY2}"); } + set + { + // Set bezier curve defintion from a string value (4 comma separated decimals). If any of the string values are invalid then set curve as linear "zero" curve + string[] bezierDef = value.Split(new Char[] { ',' }, 4); + if (bezierDef.Length < 4 || !Double.TryParse(bezierDef[0], out mX1) || !Double.TryParse(bezierDef[1], out mY1) || !Double.TryParse(bezierDef[2], out mX2) || !Double.TryParse(bezierDef[3], out mY2) ) + mX1 = mY1 = mX2 = mY2 = 0; + } + } + + // Custom definition set by DS4Windows options screens. This string is not validated (ie. the value is as user entered it and could be an invalid curve definition). This value is saved in a profile XML file. + public string CustomDefinition { get; set; } + public override string ToString() { return this.CustomDefinition; } + + public AxisType axisType; // Axis type of curve object (LS/RS/R2/L2/SA) + private double axisMaxDouble; // Max range of axis (range of positive values) + private double axisCenterPosDouble; // Center pos of axis (LS/RS has 128 as "stick center", other axies has 0 as zero center point) + + // Lookup result table is always either in 0..128 or 0..255 range depending on the DS4 analog axis range. LUT table set as public to let DS4Win reading thread to access it directly (every CPU cycle matters) + public byte[] arrayBezierLUT = null; + + public BezierCurve() + { + CustomDefinition = ""; + } + + public bool InitBezierCurve(string bezierCurveDefinition, AxisType gamepadAxisType, bool setCustomDefinitionProperty = false) + { + if (setCustomDefinitionProperty) + this.CustomDefinition = bezierCurveDefinition; + + this.AsString = bezierCurveDefinition; + return InitBezierCurve(mX1, mY1, mX2, mY2, gamepadAxisType); + } + + public bool InitBezierCurve(double x1, double y1, double x2, double y2, AxisType gamepadAxisType) + { + bool bRetValue = true; + + if (arrayBezierLUT == null) + arrayBezierLUT = new byte[256]; + + // Axis type and max range per axis + axisType = gamepadAxisType; + switch (gamepadAxisType) + { + case AxisType.LSRS: + axisMaxDouble = 127; // DS4 LS/RS axis has a "center position" at 128. Left turn has 0..127 positions and right turn 128..255 positions + axisCenterPosDouble = 128; + break; + + case AxisType.L2R2: + axisMaxDouble = 255; // L2R2 analog trigger range 0..255 + axisCenterPosDouble = 0; + break; + + default: + axisMaxDouble = 128; // SixAxis x/z/y range 0..128 + axisCenterPosDouble = 0; + break; + } + + // If x1 = 99.0 then this is probably just a dummy bezier curve value + if (x1 == 99.0) + { + mX1 = 99.0; + mY1 = y1; + mX2 = x2; + mY2 = y2; + + switch (y1) + { + case 91.0: return InitEnhancedPrecision_91(); + case 92.0: return InitQuadric_92(); + case 93.0: return InitCubic_93(); + case 94.0: return InitEaseoutQuad_94(); + case 95.0: return InitEaseoutCubic_95(); + } + } + + if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) + { + // throw new Exception("INVALID VALUE. BezierCurve X1 and X2 should be in [0, 1] range"); + AppLogger.LogToGui($"WARNING. Invalid custom bezier curve \"{x1}, {y1}, {x2}, {y2}\" in {gamepadAxisType} axis. x1 and x2 should be in 0..1 range. Using linear curve.", true); + mX1 = mY1 = mX2 = mY2 = 0; + bRetValue = false; + } + else + { + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + // If this is linear definition then init the lookup table with 1-on-1 mapping + if (x1 == 0 && y1 == 0 && ((x2 == 0 && y2 == 0) || (x2 == 1 && y2 == 1))) + { + for (int idx = 0; idx <= 255; idx++) + arrayBezierLUT[idx] = (byte)idx; + + return bRetValue; + } + + try + { + arraySampleValues = new double[BezierCurve.kSplineTableSize]; + for (int idx = 0; idx < BezierCurve.kSplineTableSize; idx++) + arraySampleValues[idx] = CalcBezier(idx * BezierCurve.kSampleStepSize, mX1, mX2); + + // Pre-populate lookup result table for GetBezierEasing function (performance optimization) + for (byte idx = 0; idx <= (byte)axisMaxDouble; idx++) + { + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)(Global.Clamp(0, Math.Round(CalcBezier(getTForX(idx / axisMaxDouble), mY1, mY2) * axisMaxDouble), axisMaxDouble) + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (gamepadAxisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + } + finally + { + arraySampleValues = null; + } + + return bRetValue; + } + + // Initialize a special "hard-coded" and pre-defined EnhancedPrecision output curve as a lookup result table + private bool InitEnhancedPrecision_91() + { + double abs, output; + + for (byte idx = 0; idx <= axisMaxDouble; idx++) + { + abs = idx / axisMaxDouble; + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else //if (abs > 0.75) + output = (abs * 1.72) - 0.72; + + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)(output * axisMaxDouble + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (this.axisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + return true; + } + + private bool InitQuadric_92() + { + double temp; + for (byte idx = 0; idx <= axisMaxDouble; idx++) + { + temp = idx / axisMaxDouble; + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)((temp * temp * axisMaxDouble) + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (this.axisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + return true; + } + + private bool InitCubic_93() + { + double temp; + for (byte idx = 0; idx <= axisMaxDouble; idx++) + { + temp = idx / axisMaxDouble; + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)((temp * temp * temp * axisMaxDouble) + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (this.axisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + return true; + } + + private bool InitEaseoutQuad_94() + { + double abs, output; + for (byte idx = 0; idx <= axisMaxDouble; idx++) + { + abs = idx / axisMaxDouble; + output = abs * (abs - 2.0); + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)((-1.0 * output * axisMaxDouble) + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (this.axisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + return true; + } + + private bool InitEaseoutCubic_95() + { + double inner, output; + for (byte idx = 0; idx <= axisMaxDouble; idx++) + { + inner = (idx / axisMaxDouble) - 1.0; + output = (inner * inner * inner) + 1.0; + arrayBezierLUT[idx + (byte)axisCenterPosDouble] = (byte)((1.0 * output * axisMaxDouble) + axisCenterPosDouble); + + // Invert curve from a right side of the center position (128) to the left tilted stick axis (or from up tilt to down tilt) + if (this.axisType == AxisType.LSRS) + arrayBezierLUT[127 - idx] = (byte)(255 - arrayBezierLUT[idx + (byte)axisCenterPosDouble]); + + // If the axisMaxDouble is 255 then we need this to break the look (byte is unsigned 0..255, so the FOR loop never reaches 256 idx value. C# would throw an overflow exceptio) + if (idx == axisMaxDouble) break; + } + return true; + } + + public byte GetBezierEasing(byte inputXValue) + { + unchecked + { + return (arrayBezierLUT == null ? inputXValue : arrayBezierLUT[inputXValue]); + //return (byte)(Global.Clamp(0, Math.Round(CalcBezier(getTForX(inputXValue / 255), mY1, mY2) * 255), 255)); + } + } + + private double A(double aA1, double aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } + private double B(double aA1, double aA2) { return 3.0 * aA2 - 6.0 * aA1; } + private double C(double aA1) { return 3.0 * aA1; } + + private double CalcBezier(double aT, double aA1, double aA2) + { + return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; + } + + private double getTForX(double aX) + { + double intervalStart = 0.0; + int currentSample = 1; + int lastSample = kSplineTableSize - 1; + + for (; currentSample != lastSample && arraySampleValues[currentSample] <= aX; ++currentSample) + { + intervalStart += kSampleStepSize; + } + --currentSample; + + // Interpolate to provide an initial guess for t + double dist = (aX - arraySampleValues[currentSample]) / (arraySampleValues[currentSample + 1] - arraySampleValues[currentSample]); + double guessForT = intervalStart + dist * kSampleStepSize; + + double initialSlope = getSlope(guessForT, mX1, mX2); + if (initialSlope >= NEWTON_MIN_SLOPE) + { + return newtonRaphsonIterate(aX, guessForT /*, mX1, mX2*/); + } + else if (initialSlope == 0.0) + { + return guessForT; + } + else + { + return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize /*, mX1, mX2*/); + } + } + + // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. + private double getSlope(double aT, double aA1, double aA2) + { + return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); + } + + private double newtonRaphsonIterate(double aX, double aGuessT /*, double mX1, double mX2*/) + { + for (int i = 0; i < BezierCurve.NEWTON_ITERATIONS; ++i) + { + double currentSlope = getSlope(aGuessT, mX1, mX2); + if (currentSlope == 0.0) + { + return aGuessT; + } + double currentX = CalcBezier(aGuessT, mX1, mX2) - aX; + aGuessT -= currentX / currentSlope; + } + return aGuessT; + } + + private double binarySubdivide(double aX, double aA, double aB /*, double mX1, double mX2*/) + { + double currentX, currentT, i = 0; + do + { + currentT = aA + (aB - aA) / 2.0; + currentX = CalcBezier(currentT, mX1, mX2) - aX; + if (currentX > 0.0) + { + aB = currentT; + } + else + { + aA = currentT; + } + } while (Math.Abs(currentX) > BezierCurve.SUBDIVISION_PRECISION && ++i < BezierCurve.SUBDIVISION_MAX_ITERATIONS); + + return currentT; + } + + } +} diff --git a/DS4Windows/BezierCurveEditor/build.js b/DS4Windows/BezierCurveEditor/build.js new file mode 100644 index 0000000000..278059de0a --- /dev/null +++ b/DS4Windows/BezierCurveEditor/build.js @@ -0,0 +1,10114 @@ +! function e(t, n, r) { + function o(i, s) { + if (!n[i]) { + if (!t[i]) { + var u = "function" == typeof require && require; + if (!s && u) return u(i, !0); + if (a) return a(i, !0); + var c = new Error("Cannot find module '" + i + "'"); + throw c.code = "MODULE_NOT_FOUND", c + } + var l = n[i] = { + exports: {} + }; + t[i][0].call(l.exports, function(e) { + var n = t[i][1][e]; + return o(n ? n : e) + }, l, l.exports, e, t, n, r) + } + return n[i].exports + } + for (var a = "function" == typeof require && require, i = 0; i < r.length; i++) o(r[i]); + return o +}({ + 1: [function(e) { + "use strict"; + var t = function(e) { + return e && e.__esModule ? e["default"] : e + }, + n = function() { + function e(e, t) { + for (var n in t) { + var r = t[n]; + r.configurable = !0, r.value && (r.writable = !0) + } + Object.defineProperties(e, t) + } + return function(t, n, r) { + return n && e(t.prototype, n), r && e(t, r), t + } + }(), + r = function f(e, t, n) { + var r = Object.getOwnPropertyDescriptor(e, t); + if (void 0 === r) { + var o = Object.getPrototypeOf(e); + return null === o ? void 0 : f(o, t, n) + } + if ("value" in r && r.writable) return r.value; + var a = r.get; + return void 0 === a ? void 0 : a.call(n) + }, + o = function(e, t) { + if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); + e.prototype = Object.create(t && t.prototype, { + constructor: { + value: e, + enumerable: !1, + writable: !0, + configurable: !0 + } + }), t && (e.__proto__ = t) + }, + a = function(e, t) { + if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") + }, + i = t(e("react/addons")), + s = t(e("..")), + u = t(e("../package.json")), + c = t(e("raf")); + window.Perf = i.addons.Perf; + var l = { + color: "#0000c6", + textDecoration: "none" + }, + p = { + display: "inline-block", + margin: "5px" + }, + qryVariableValue = function(variableName) + { + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i=0;i o; ++o) { + var a = v(r, e, c); + if (0 === a) return r; + var i = h(r, e, c) - t; + r -= i / a + } + return r + } + + function y() { + for (var t = 0; i > t; ++t) _[t] = h(t * s, e, c) + } + + function g(t, n, r) { + var i, s, u = 0; + do s = n + (r - n) / 2, i = h(s, e, c) - t, i > 0 ? r = s : n = s; while (Math.abs(i) > o && ++u < a); + return s + } + + function E(t) { + for (var n = 0, o = 1, a = i - 1; o != a && _[o] <= t; ++o) n += s; + --o; + var u = (t - _[o]) / (_[o + 1] - _[o]), + l = n + u * s, + p = v(l, e, c); + return p >= r ? m(t, l) : 0 === p ? l : g(t, n, n + s) + } + + function C() { + N = !0, (e != t || c != l) && y() + } + if (4 !== arguments.length) throw new Error("BezierEasing requires 4 arguments."); + for (var b = 0; 4 > b; ++b) + if ("number" != typeof arguments[b] || isNaN(arguments[b]) || !isFinite(arguments[b])) throw new Error("BezierEasing arguments should be integers."); + if (0 > e || e > 1 || 0 > c || c > 1) throw new Error("BezierEasing x values must be in [0, 1] range."); + var _ = u ? new Float32Array(i) : new Array(i), + N = !1, + O = function(n) { + return N || C(), e === t && c === l ? n : 0 === n ? 0 : 1 === n ? 1 : h(E(n), t, l) + }; + O.getControlPoints = function() { + return [{ + x: e, + y: t + }, { + x: c, + y: l + }] + }; + var R = [e, t, c, l], + D = "BezierEasing(" + R + ")"; + O.toString = function() { + return D + }; + var w = "cubic-bezier(" + R + ")"; + return O.toCSS = function() { + return w + }, O + } + var t = this, + n = 4, + r = .001, + o = 1e-7, + a = 10, + i = 11, + s = 1 / (i - 1), + u = "Float32Array" in t; + return e.css = { + ease: e(.25, .1, .25, 1), + linear: e(0, 0, 1, 1), + "ease-in": e(.42, 0, 1, 1), + "ease-out": e(0, 0, .58, 1), + "ease-in-out": e(.42, 0, .58, 1) + }, e + }) + }, {}], + 6: [function(e, t) { + "use strict"; + + function n(e) { + if (null == e) throw new TypeError("Object.assign cannot be called with null or undefined"); + return Object(e) + } + t.exports = Object.assign || function(e) { + for (var t, r, o = n(e), a = 1; a < arguments.length; a++) { + t = arguments[a], r = Object.keys(Object(t)); + for (var i = 0; i < r.length; i++) o[r[i]] = t[r[i]] + } + return o + } + }, {}], + 7: [function(e, t) { + t.exports = e("./lib/ReactWithAddons") + }, { + "./lib/ReactWithAddons": 107 + }], + 8: [function(e, t) { + "use strict"; + var n = e("./focusNode"), + r = { + componentDidMount: function() { + this.props.autoFocus && n(this.getDOMNode()) + } + }; + t.exports = r + }, { + "./focusNode": 141 + }], + 9: [function(e, t) { + "use strict"; + + function n() { + var e = window.opera; + return "object" == typeof e && "function" == typeof e.version && parseInt(e.version(), 10) <= 12 + } + + function r(e) { + return (e.ctrlKey || e.altKey || e.metaKey) && !(e.ctrlKey && e.altKey) + } + + function o(e) { + switch (e) { + case w.topCompositionStart: + return M.compositionStart; + case w.topCompositionEnd: + return M.compositionEnd; + case w.topCompositionUpdate: + return M.compositionUpdate + } + } + + function a(e, t) { + return e === w.topKeyDown && t.keyCode === C + } + + function i(e, t) { + switch (e) { + case w.topKeyUp: + return -1 !== E.indexOf(t.keyCode); + case w.topKeyDown: + return t.keyCode !== C; + case w.topKeyPress: + case w.topMouseDown: + case w.topBlur: + return !0; + default: + return !1 + } + } + + function s(e) { + var t = e.detail; + return "object" == typeof t && "data" in t ? t.data : null + } + + function u(e, t, n, r) { + var u, c; + if (b ? u = o(e) : T ? i(e, r) && (u = M.compositionEnd) : a(e, r) && (u = M.compositionStart), !u) return null; + O && (T || u !== M.compositionStart ? u === M.compositionEnd && T && (c = T.getData()) : T = v.getPooled(t)); + var l = m.getPooled(u, n, r); + if (c) l.data = c; + else { + var p = s(r); + null !== p && (l.data = p) + } + return f.accumulateTwoPhaseDispatches(l), l + } + + function c(e, t) { + switch (e) { + case w.topCompositionEnd: + return s(t); + case w.topKeyPress: + var n = t.which; + return n !== R ? null : (x = !0, D); + case w.topTextInput: + var r = t.data; + return r === D && x ? null : r; + default: + return null + } + } + + function l(e, t) { + if (T) { + if (e === w.topCompositionEnd || i(e, t)) { + var n = T.getData(); + return v.release(T), T = null, n + } + return null + } + switch (e) { + case w.topPaste: + return null; + case w.topKeyPress: + return t.which && !r(t) ? String.fromCharCode(t.which) : null; + case w.topCompositionEnd: + return O ? null : t.data; + default: + return null + } + } + + function p(e, t, n, r) { + var o; + if (o = N ? c(e, r) : l(e, r), !o) return null; + var a = y.getPooled(M.beforeInput, n, r); + return a.data = o, f.accumulateTwoPhaseDispatches(a), a + } + var d = e("./EventConstants"), + f = e("./EventPropagators"), + h = e("./ExecutionEnvironment"), + v = e("./FallbackCompositionState"), + m = e("./SyntheticCompositionEvent"), + y = e("./SyntheticInputEvent"), + g = e("./keyOf"), + E = [9, 13, 27, 32], + C = 229, + b = h.canUseDOM && "CompositionEvent" in window, + _ = null; + h.canUseDOM && "documentMode" in document && (_ = document.documentMode); + var N = h.canUseDOM && "TextEvent" in window && !_ && !n(), + O = h.canUseDOM && (!b || _ && _ > 8 && 11 >= _), + R = 32, + D = String.fromCharCode(R), + w = d.topLevelTypes, + M = { + beforeInput: { + phasedRegistrationNames: { + bubbled: g({ + onBeforeInput: null + }), + captured: g({ + onBeforeInputCapture: null + }) + }, + dependencies: [w.topCompositionEnd, w.topKeyPress, w.topTextInput, w.topPaste] + }, + compositionEnd: { + phasedRegistrationNames: { + bubbled: g({ + onCompositionEnd: null + }), + captured: g({ + onCompositionEndCapture: null + }) + }, + dependencies: [w.topBlur, w.topCompositionEnd, w.topKeyDown, w.topKeyPress, w.topKeyUp, w.topMouseDown] + }, + compositionStart: { + phasedRegistrationNames: { + bubbled: g({ + onCompositionStart: null + }), + captured: g({ + onCompositionStartCapture: null + }) + }, + dependencies: [w.topBlur, w.topCompositionStart, w.topKeyDown, w.topKeyPress, w.topKeyUp, w.topMouseDown] + }, + compositionUpdate: { + phasedRegistrationNames: { + bubbled: g({ + onCompositionUpdate: null + }), + captured: g({ + onCompositionUpdateCapture: null + }) + }, + dependencies: [w.topBlur, w.topCompositionUpdate, w.topKeyDown, w.topKeyPress, w.topKeyUp, w.topMouseDown] + } + }, + x = !1, + T = null, + P = { + eventTypes: M, + extractEvents: function(e, t, n, r) { + return [u(e, t, n, r), p(e, t, n, r)] + } + }; + t.exports = P + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ExecutionEnvironment": 28, + "./FallbackCompositionState": 29, + "./SyntheticCompositionEvent": 113, + "./SyntheticInputEvent": 117, + "./keyOf": 164 + }], + 10: [function(e, t) { + (function(n) { + var r = e("./invariant"), + o = { + addClass: function(e, t) { + return "production" !== n.env.NODE_ENV ? r(!/\s/.test(t), 'CSSCore.addClass takes only a single class name. "%s" contains multiple classes.', t) : r(!/\s/.test(t)), t && (e.classList ? e.classList.add(t) : o.hasClass(e, t) || (e.className = e.className + " " + t)), e + }, + removeClass: function(e, t) { + return "production" !== n.env.NODE_ENV ? r(!/\s/.test(t), 'CSSCore.removeClass takes only a single class name. "%s" contains multiple classes.', t) : r(!/\s/.test(t)), t && (e.classList ? e.classList.remove(t) : o.hasClass(e, t) && (e.className = e.className.replace(new RegExp("(^|\\s)" + t + "(?:\\s|$)", "g"), "$1").replace(/\s+/g, " ").replace(/^\s*|\s*$/g, ""))), e + }, + conditionClass: function(e, t, n) { + return (n ? o.addClass : o.removeClass)(e, t) + }, + hasClass: function(e, t) { + return "production" !== n.env.NODE_ENV ? r(!/\s/.test(t), "CSS.hasClass takes only a single class name.") : r(!/\s/.test(t)), e.classList ? !!t && e.classList.contains(t) : (" " + e.className + " ").indexOf(" " + t + " ") > -1 + } + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 11: [function(e, t) { + "use strict"; + + function n(e, t) { + return e + t.charAt(0).toUpperCase() + t.substring(1) + } + var r = { + boxFlex: !0, + boxFlexGroup: !0, + columnCount: !0, + flex: !0, + flexGrow: !0, + flexShrink: !0, + fontWeight: !0, + lineClamp: !0, + lineHeight: !0, + opacity: !0, + order: !0, + orphans: !0, + widows: !0, + zIndex: !0, + zoom: !0, + fillOpacity: !0, + strokeOpacity: !0 + }, + o = ["Webkit", "ms", "Moz", "O"]; + Object.keys(r).forEach(function(e) { + o.forEach(function(t) { + r[n(t, e)] = r[e] + }) + }); + var a = { + background: { + backgroundImage: !0, + backgroundPosition: !0, + backgroundRepeat: !0, + backgroundColor: !0 + }, + border: { + borderWidth: !0, + borderStyle: !0, + borderColor: !0 + }, + borderBottom: { + borderBottomWidth: !0, + borderBottomStyle: !0, + borderBottomColor: !0 + }, + borderLeft: { + borderLeftWidth: !0, + borderLeftStyle: !0, + borderLeftColor: !0 + }, + borderRight: { + borderRightWidth: !0, + borderRightStyle: !0, + borderRightColor: !0 + }, + borderTop: { + borderTopWidth: !0, + borderTopStyle: !0, + borderTopColor: !0 + }, + font: { + fontStyle: !0, + fontVariant: !0, + fontWeight: !0, + fontSize: !0, + lineHeight: !0, + fontFamily: !0 + } + }, + i = { + isUnitlessNumber: r, + shorthandPropertyExpansions: a + }; + t.exports = i + }, {}], + 12: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./CSSProperty"), + o = e("./ExecutionEnvironment"), + a = e("./camelizeStyleName"), + i = e("./dangerousStyleValue"), + s = e("./hyphenateStyleName"), + u = e("./memoizeStringOnly"), + c = e("./warning"), + l = u(function(e) { + return s(e) + }), + p = "cssFloat"; + if (o.canUseDOM && void 0 === document.documentElement.style.cssFloat && (p = "styleFloat"), "production" !== n.env.NODE_ENV) var d = /^(?:webkit|moz|o)[A-Z]/, + f = /;\s*$/, + h = {}, + v = {}, + m = function(e) { + h.hasOwnProperty(e) && h[e] || (h[e] = !0, "production" !== n.env.NODE_ENV ? c(!1, "Unsupported style property %s. Did you mean %s?", e, a(e)) : null) + }, + y = function(e) { + h.hasOwnProperty(e) && h[e] || (h[e] = !0, "production" !== n.env.NODE_ENV ? c(!1, "Unsupported vendor-prefixed style property %s. Did you mean %s?", e, e.charAt(0).toUpperCase() + e.slice(1)) : null) + }, + g = function(e, t) { + v.hasOwnProperty(t) && v[t] || (v[t] = !0, "production" !== n.env.NODE_ENV ? c(!1, 'Style property values shouldn\'t contain a semicolon. Try "%s: %s" instead.', e, t.replace(f, "")) : null) + }, + E = function(e, t) { + e.indexOf("-") > -1 ? m(e) : d.test(e) ? y(e) : f.test(t) && g(e, t) + }; + var C = { + createMarkupForStyles: function(e) { + var t = ""; + for (var r in e) + if (e.hasOwnProperty(r)) { + var o = e[r]; + "production" !== n.env.NODE_ENV && E(r, o), null != o && (t += l(r) + ":", t += i(r, o) + ";") + } return t || null + }, + setValueForStyles: function(e, t) { + var o = e.style; + for (var a in t) + if (t.hasOwnProperty(a)) { + "production" !== n.env.NODE_ENV && E(a, t[a]); + var s = i(a, t[a]); + if ("float" === a && (a = p), s) o[a] = s; + else { + var u = r.shorthandPropertyExpansions[a]; + if (u) + for (var c in u) o[c] = ""; + else o[a] = "" + } + } + } + }; + t.exports = C + }).call(this, e("_process")) + }, { + "./CSSProperty": 11, + "./ExecutionEnvironment": 28, + "./camelizeStyleName": 128, + "./dangerousStyleValue": 135, + "./hyphenateStyleName": 155, + "./memoizeStringOnly": 166, + "./warning": 178, + _process: 2 + }], + 13: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + this._callbacks = null, this._contexts = null + } + var o = e("./PooledClass"), + a = e("./Object.assign"), + i = e("./invariant"); + a(r.prototype, { + enqueue: function(e, t) { + this._callbacks = this._callbacks || [], this._contexts = this._contexts || [], this._callbacks.push(e), this._contexts.push(t) + }, + notifyAll: function() { + var e = this._callbacks, + t = this._contexts; + if (e) { + "production" !== n.env.NODE_ENV ? i(e.length === t.length, "Mismatched list of contexts in callback queue") : i(e.length === t.length), this._callbacks = null, this._contexts = null; + for (var r = 0, o = e.length; o > r; r++) e[r].call(t[r]); + e.length = 0, t.length = 0 + } + }, + reset: function() { + this._callbacks = null, this._contexts = null + }, + destructor: function() { + this.reset() + } + }), o.addPoolingTo(r), t.exports = r + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./invariant": 157, + _process: 2 + }], + 14: [function(e, t) { + "use strict"; + + function n(e) { + return "SELECT" === e.nodeName || "INPUT" === e.nodeName && "file" === e.type + } + + function r(e) { + var t = _.getPooled(w.change, x, e); + E.accumulateTwoPhaseDispatches(t), b.batchedUpdates(o, t) + } + + function o(e) { + g.enqueueEvents(e), g.processEventQueue() + } + + function a(e, t) { + M = e, x = t, M.attachEvent("onchange", r) + } + + function i() { + M && (M.detachEvent("onchange", r), M = null, x = null) + } + + function s(e, t, n) { + return e === D.topChange ? n : void 0 + } + + function u(e, t, n) { + e === D.topFocus ? (i(), a(t, n)) : e === D.topBlur && i() + } + + function c(e, t) { + M = e, x = t, T = e.value, P = Object.getOwnPropertyDescriptor(e.constructor.prototype, "value"), Object.defineProperty(M, "value", k), M.attachEvent("onpropertychange", p) + } + + function l() { + M && (delete M.value, M.detachEvent("onpropertychange", p), M = null, x = null, T = null, P = null) + } + + function p(e) { + if ("value" === e.propertyName) { + var t = e.srcElement.value; + t !== T && (T = t, r(e)) + } + } + + function d(e, t, n) { + return e === D.topInput ? n : void 0 + } + + function f(e, t, n) { + e === D.topFocus ? (l(), c(t, n)) : e === D.topBlur && l() + } + + function h(e) { + return e !== D.topSelectionChange && e !== D.topKeyUp && e !== D.topKeyDown || !M || M.value === T ? void 0 : (T = M.value, x) + } + + function v(e) { + return "INPUT" === e.nodeName && ("checkbox" === e.type || "radio" === e.type) + } + + function m(e, t, n) { + return e === D.topClick ? n : void 0 + } + var y = e("./EventConstants"), + g = e("./EventPluginHub"), + E = e("./EventPropagators"), + C = e("./ExecutionEnvironment"), + b = e("./ReactUpdates"), + _ = e("./SyntheticEvent"), + N = e("./isEventSupported"), + O = e("./isTextInputElement"), + R = e("./keyOf"), + D = y.topLevelTypes, + w = { + change: { + phasedRegistrationNames: { + bubbled: R({ + onChange: null + }), + captured: R({ + onChangeCapture: null + }) + }, + dependencies: [D.topBlur, D.topChange, D.topClick, D.topFocus, D.topInput, D.topKeyDown, D.topKeyUp, D.topSelectionChange] + } + }, + M = null, + x = null, + T = null, + P = null, + I = !1; + C.canUseDOM && (I = N("change") && (!("documentMode" in document) || document.documentMode > 8)); + var S = !1; + C.canUseDOM && (S = N("input") && (!("documentMode" in document) || document.documentMode > 9)); + var k = { + get: function() { + return P.get.call(this) + }, + set: function(e) { + T = "" + e, P.set.call(this, e) + } + }, + A = { + eventTypes: w, + extractEvents: function(e, t, r, o) { + var a, i; + if (n(t) ? I ? a = s : i = u : O(t) ? S ? a = d : (a = h, i = f) : v(t) && (a = m), a) { + var c = a(e, t, r); + if (c) { + var l = _.getPooled(w.change, c, o); + return E.accumulateTwoPhaseDispatches(l), l + } + } + i && i(e, t, r) + } + }; + t.exports = A + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPropagators": 27, + "./ExecutionEnvironment": 28, + "./ReactUpdates": 106, + "./SyntheticEvent": 115, + "./isEventSupported": 158, + "./isTextInputElement": 160, + "./keyOf": 164 + }], + 15: [function(e, t) { + "use strict"; + var n = 0, + r = { + createReactRootIndex: function() { + return n++ + } + }; + t.exports = r + }, {}], + 16: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t, n) { + e.insertBefore(t, e.childNodes[n] || null) + } + var o = e("./Danger"), + a = e("./ReactMultiChildUpdateTypes"), + i = e("./setTextContent"), + s = e("./invariant"), + u = { + dangerouslyReplaceNodeWithMarkup: o.dangerouslyReplaceNodeWithMarkup, + updateTextContent: i, + processUpdates: function(e, t) { + for (var u, c = null, l = null, p = 0; p < e.length; p++) + if (u = e[p], u.type === a.MOVE_EXISTING || u.type === a.REMOVE_NODE) { + var d = u.fromIndex, + f = u.parentNode.childNodes[d], + h = u.parentID; + "production" !== n.env.NODE_ENV ? s(f, "processUpdates(): Unable to find child %s of element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID `%s`.", d, h) : s(f), c = c || {}, c[h] = c[h] || [], c[h][d] = f, l = l || [], l.push(f) + } var v = o.dangerouslyRenderMarkup(t); + if (l) + for (var m = 0; m < l.length; m++) l[m].parentNode.removeChild(l[m]); + for (var y = 0; y < e.length; y++) switch (u = e[y], u.type) { + case a.INSERT_MARKUP: + r(u.parentNode, v[u.markupIndex], u.toIndex); + break; + case a.MOVE_EXISTING: + r(u.parentNode, c[u.parentID][u.fromIndex], u.toIndex); + break; + case a.TEXT_CONTENT: + i(u.parentNode, u.textContent); + break; + case a.REMOVE_NODE: + } + } + }; + t.exports = u + }).call(this, e("_process")) + }, { + "./Danger": 19, + "./ReactMultiChildUpdateTypes": 85, + "./invariant": 157, + "./setTextContent": 172, + _process: 2 + }], + 17: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + return (e & t) === t + } + var o = e("./invariant"), + a = { + MUST_USE_ATTRIBUTE: 1, + MUST_USE_PROPERTY: 2, + HAS_SIDE_EFFECTS: 4, + HAS_BOOLEAN_VALUE: 8, + HAS_NUMERIC_VALUE: 16, + HAS_POSITIVE_NUMERIC_VALUE: 48, + HAS_OVERLOADED_BOOLEAN_VALUE: 64, + injectDOMPropertyConfig: function(e) { + var t = e.Properties || {}, + i = e.DOMAttributeNames || {}, + u = e.DOMPropertyNames || {}, + c = e.DOMMutationMethods || {}; + e.isCustomAttribute && s._isCustomAttributeFunctions.push(e.isCustomAttribute); + for (var l in t) { + "production" !== n.env.NODE_ENV ? o(!s.isStandardName.hasOwnProperty(l), "injectDOMPropertyConfig(...): You're trying to inject DOM property '%s' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.", l) : o(!s.isStandardName.hasOwnProperty(l)), s.isStandardName[l] = !0; + var p = l.toLowerCase(); + if (s.getPossibleStandardName[p] = l, i.hasOwnProperty(l)) { + var d = i[l]; + s.getPossibleStandardName[d] = l, s.getAttributeName[l] = d + } else s.getAttributeName[l] = p; + s.getPropertyName[l] = u.hasOwnProperty(l) ? u[l] : l, s.getMutationMethod[l] = c.hasOwnProperty(l) ? c[l] : null; + var f = t[l]; + s.mustUseAttribute[l] = r(f, a.MUST_USE_ATTRIBUTE), s.mustUseProperty[l] = r(f, a.MUST_USE_PROPERTY), s.hasSideEffects[l] = r(f, a.HAS_SIDE_EFFECTS), s.hasBooleanValue[l] = r(f, a.HAS_BOOLEAN_VALUE), s.hasNumericValue[l] = r(f, a.HAS_NUMERIC_VALUE), s.hasPositiveNumericValue[l] = r(f, a.HAS_POSITIVE_NUMERIC_VALUE), s.hasOverloadedBooleanValue[l] = r(f, a.HAS_OVERLOADED_BOOLEAN_VALUE), "production" !== n.env.NODE_ENV ? o(!s.mustUseAttribute[l] || !s.mustUseProperty[l], "DOMProperty: Cannot require using both attribute and property: %s", l) : o(!s.mustUseAttribute[l] || !s.mustUseProperty[l]), "production" !== n.env.NODE_ENV ? o(s.mustUseProperty[l] || !s.hasSideEffects[l], "DOMProperty: Properties that have side effects must use property: %s", l) : o(s.mustUseProperty[l] || !s.hasSideEffects[l]), "production" !== n.env.NODE_ENV ? o(!!s.hasBooleanValue[l] + !!s.hasNumericValue[l] + !!s.hasOverloadedBooleanValue[l] <= 1, "DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s", l) : o(!!s.hasBooleanValue[l] + !!s.hasNumericValue[l] + !!s.hasOverloadedBooleanValue[l] <= 1) + } + } + }, + i = {}, + s = { + ID_ATTRIBUTE_NAME: "data-reactid", + isStandardName: {}, + getPossibleStandardName: {}, + getAttributeName: {}, + getPropertyName: {}, + getMutationMethod: {}, + mustUseAttribute: {}, + mustUseProperty: {}, + hasSideEffects: {}, + hasBooleanValue: {}, + hasNumericValue: {}, + hasPositiveNumericValue: {}, + hasOverloadedBooleanValue: {}, + _isCustomAttributeFunctions: [], + isCustomAttribute: function(e) { + for (var t = 0; t < s._isCustomAttributeFunctions.length; t++) { + var n = s._isCustomAttributeFunctions[t]; + if (n(e)) return !0 + } + return !1 + }, + getDefaultValueForProperty: function(e, t) { + var n, r = i[e]; + return r || (i[e] = r = {}), t in r || (n = document.createElement(e), r[t] = n[t]), r[t] + }, + injection: a + }; + t.exports = s + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 18: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + return null == t || o.hasBooleanValue[e] && !t || o.hasNumericValue[e] && isNaN(t) || o.hasPositiveNumericValue[e] && 1 > t || o.hasOverloadedBooleanValue[e] && t === !1 + } + var o = e("./DOMProperty"), + a = e("./quoteAttributeValueForBrowser"), + i = e("./warning"); + if ("production" !== n.env.NODE_ENV) var s = { + children: !0, + dangerouslySetInnerHTML: !0, + key: !0, + ref: !0 + }, + u = {}, + c = function(e) { + if (!(s.hasOwnProperty(e) && s[e] || u.hasOwnProperty(e) && u[e])) { + u[e] = !0; + var t = e.toLowerCase(), + r = o.isCustomAttribute(t) ? t : o.getPossibleStandardName.hasOwnProperty(t) ? o.getPossibleStandardName[t] : null; + "production" !== n.env.NODE_ENV ? i(null == r, "Unknown DOM property %s. Did you mean %s?", e, r) : null + } + }; + var l = { + createMarkupForID: function(e) { + return o.ID_ATTRIBUTE_NAME + "=" + a(e) + }, + createMarkupForProperty: function(e, t) { + if (o.isStandardName.hasOwnProperty(e) && o.isStandardName[e]) { + if (r(e, t)) return ""; + var i = o.getAttributeName[e]; + return o.hasBooleanValue[e] || o.hasOverloadedBooleanValue[e] && t === !0 ? i : i + "=" + a(t) + } + return o.isCustomAttribute(e) ? null == t ? "" : e + "=" + a(t) : ("production" !== n.env.NODE_ENV && c(e), null) + }, + setValueForProperty: function(e, t, a) { + if (o.isStandardName.hasOwnProperty(t) && o.isStandardName[t]) { + var i = o.getMutationMethod[t]; + if (i) i(e, a); + else if (r(t, a)) this.deleteValueForProperty(e, t); + else if (o.mustUseAttribute[t]) e.setAttribute(o.getAttributeName[t], "" + a); + else { + var s = o.getPropertyName[t]; + o.hasSideEffects[t] && "" + e[s] == "" + a || (e[s] = a) + } + } else o.isCustomAttribute(t) ? null == a ? e.removeAttribute(t) : e.setAttribute(t, "" + a) : "production" !== n.env.NODE_ENV && c(t) + }, + deleteValueForProperty: function(e, t) { + if (o.isStandardName.hasOwnProperty(t) && o.isStandardName[t]) { + var r = o.getMutationMethod[t]; + if (r) r(e, void 0); + else if (o.mustUseAttribute[t]) e.removeAttribute(o.getAttributeName[t]); + else { + var a = o.getPropertyName[t], + i = o.getDefaultValueForProperty(e.nodeName, a); + o.hasSideEffects[t] && "" + e[a] === i || (e[a] = i) + } + } else o.isCustomAttribute(t) ? e.removeAttribute(t) : "production" !== n.env.NODE_ENV && c(t) + } + }; + t.exports = l + }).call(this, e("_process")) + }, { + "./DOMProperty": 17, + "./quoteAttributeValueForBrowser": 170, + "./warning": 178, + _process: 2 + }], + 19: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return e.substring(1, e.indexOf(" ")) + } + var o = e("./ExecutionEnvironment"), + a = e("./createNodesFromMarkup"), + i = e("./emptyFunction"), + s = e("./getMarkupWrap"), + u = e("./invariant"), + c = /^(<[^ \/>]+)/, + l = "data-danger-index", + p = { + dangerouslyRenderMarkup: function(e) { + "production" !== n.env.NODE_ENV ? u(o.canUseDOM, "dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.") : u(o.canUseDOM); + for (var t, p = {}, d = 0; d < e.length; d++) "production" !== n.env.NODE_ENV ? u(e[d], "dangerouslyRenderMarkup(...): Missing markup.") : u(e[d]), t = r(e[d]), t = s(t) ? t : "*", p[t] = p[t] || [], p[t][d] = e[d]; + var f = [], + h = 0; + for (t in p) + if (p.hasOwnProperty(t)) { + var v, m = p[t]; + for (v in m) + if (m.hasOwnProperty(v)) { + var y = m[v]; + m[v] = y.replace(c, "$1 " + l + '="' + v + '" ') + } for (var g = a(m.join(""), i), E = 0; E < g.length; ++E) { + var C = g[E]; + C.hasAttribute && C.hasAttribute(l) ? (v = +C.getAttribute(l), C.removeAttribute(l), "production" !== n.env.NODE_ENV ? u(!f.hasOwnProperty(v), "Danger: Assigning to an already-occupied result index.") : u(!f.hasOwnProperty(v)), f[v] = C, h += 1) : "production" !== n.env.NODE_ENV && console.error("Danger: Discarding unexpected node:", C) + } + } return "production" !== n.env.NODE_ENV ? u(h === f.length, "Danger: Did not assign to every index of resultList.") : u(h === f.length), "production" !== n.env.NODE_ENV ? u(f.length === e.length, "Danger: Expected markup to render %s nodes, but rendered %s.", e.length, f.length) : u(f.length === e.length), f + }, + dangerouslyReplaceNodeWithMarkup: function(e, t) { + "production" !== n.env.NODE_ENV ? u(o.canUseDOM, "dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.") : u(o.canUseDOM), "production" !== n.env.NODE_ENV ? u(t, "dangerouslyReplaceNodeWithMarkup(...): Missing markup.") : u(t), "production" !== n.env.NODE_ENV ? u("html" !== e.tagName.toLowerCase(), "dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the node. This is because browser quirks make this unreliable and/or slow. If you want to render to the root you must use server rendering. See React.renderToString().") : u("html" !== e.tagName.toLowerCase()); + var r = a(t, i)[0]; + e.parentNode.replaceChild(r, e) + } + }; + t.exports = p + }).call(this, e("_process")) + }, { + "./ExecutionEnvironment": 28, + "./createNodesFromMarkup": 133, + "./emptyFunction": 136, + "./getMarkupWrap": 149, + "./invariant": 157, + _process: 2 + }], + 20: [function(e, t) { + "use strict"; + var n = e("./keyOf"), + r = [n({ + ResponderEventPlugin: null + }), n({ + SimpleEventPlugin: null + }), n({ + TapEventPlugin: null + }), n({ + EnterLeaveEventPlugin: null + }), n({ + ChangeEventPlugin: null + }), n({ + SelectEventPlugin: null + }), n({ + BeforeInputEventPlugin: null + }), n({ + AnalyticsEventPlugin: null + }), n({ + MobileSafariClickEventPlugin: null + })]; + t.exports = r + }, { + "./keyOf": 164 + }], + 21: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./EventPropagators"), + o = e("./SyntheticMouseEvent"), + a = e("./ReactMount"), + i = e("./keyOf"), + s = n.topLevelTypes, + u = a.getFirstReactDOM, + c = { + mouseEnter: { + registrationName: i({ + onMouseEnter: null + }), + dependencies: [s.topMouseOut, s.topMouseOver] + }, + mouseLeave: { + registrationName: i({ + onMouseLeave: null + }), + dependencies: [s.topMouseOut, s.topMouseOver] + } + }, + l = [null, null], + p = { + eventTypes: c, + extractEvents: function(e, t, n, i) { + if (e === s.topMouseOver && (i.relatedTarget || i.fromElement)) return null; + if (e !== s.topMouseOut && e !== s.topMouseOver) return null; + var p; + if (t.window === t) p = t; + else { + var d = t.ownerDocument; + p = d ? d.defaultView || d.parentWindow : window + } + var f, h; + if (e === s.topMouseOut ? (f = t, h = u(i.relatedTarget || i.toElement) || p) : (f = p, h = t), f === h) return null; + var v = f ? a.getID(f) : "", + m = h ? a.getID(h) : "", + y = o.getPooled(c.mouseLeave, v, i); + y.type = "mouseleave", y.target = f, y.relatedTarget = h; + var g = o.getPooled(c.mouseEnter, m, i); + return g.type = "mouseenter", g.target = h, g.relatedTarget = f, r.accumulateEnterLeaveDispatches(y, g, v, m), l[0] = y, l[1] = g, l + } + }; + t.exports = p + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ReactMount": 83, + "./SyntheticMouseEvent": 119, + "./keyOf": 164 + }], + 22: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + bubbled: null, + captured: null + }), + o = n({ + topBlur: null, + topChange: null, + topClick: null, + topCompositionEnd: null, + topCompositionStart: null, + topCompositionUpdate: null, + topContextMenu: null, + topCopy: null, + topCut: null, + topDoubleClick: null, + topDrag: null, + topDragEnd: null, + topDragEnter: null, + topDragExit: null, + topDragLeave: null, + topDragOver: null, + topDragStart: null, + topDrop: null, + topError: null, + topFocus: null, + topInput: null, + topKeyDown: null, + topKeyPress: null, + topKeyUp: null, + topLoad: null, + topMouseDown: null, + topMouseMove: null, + topMouseOut: null, + topMouseOver: null, + topMouseUp: null, + topPaste: null, + topReset: null, + topScroll: null, + topSelectionChange: null, + topSubmit: null, + topTextInput: null, + topTouchCancel: null, + topTouchEnd: null, + topTouchMove: null, + topTouchStart: null, + topWheel: null + }), + a = { + topLevelTypes: o, + PropagationPhases: r + }; + t.exports = a + }, { + "./keyMirror": 163 + }], + 23: [function(e, t) { + (function(n) { + var r = e("./emptyFunction"), + o = { + listen: function(e, t, n) { + return e.addEventListener ? (e.addEventListener(t, n, !1), { + remove: function() { + e.removeEventListener(t, n, !1) + } + }) : e.attachEvent ? (e.attachEvent("on" + t, n), { + remove: function() { + e.detachEvent("on" + t, n) + } + }) : void 0 + }, + capture: function(e, t, o) { + return e.addEventListener ? (e.addEventListener(t, o, !0), { + remove: function() { + e.removeEventListener(t, o, !0) + } + }) : ("production" !== n.env.NODE_ENV && console.error("Attempted to listen to events during the capture phase on a browser that does not support the capture phase. Your application will not receive some events."), { + remove: r + }) + }, + registerDefault: function() {} + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./emptyFunction": 136, + _process: 2 + }], + 24: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + var e = d && d.traverseTwoPhase && d.traverseEnterLeave; + "production" !== n.env.NODE_ENV ? u(e, "InstanceHandle not injected before use!") : u(e) + } + var o = e("./EventPluginRegistry"), + a = e("./EventPluginUtils"), + i = e("./accumulateInto"), + s = e("./forEachAccumulated"), + u = e("./invariant"), + c = {}, + l = null, + p = function(e) { + if (e) { + var t = a.executeDispatch, + n = o.getPluginModuleForEvent(e); + n && n.executeDispatch && (t = n.executeDispatch), a.executeDispatchesInOrder(e, t), e.isPersistent() || e.constructor.release(e) + } + }, + d = null, + f = { + injection: { + injectMount: a.injection.injectMount, + injectInstanceHandle: function(e) { + d = e, "production" !== n.env.NODE_ENV && r() + }, + getInstanceHandle: function() { + return "production" !== n.env.NODE_ENV && r(), d + }, + injectEventPluginOrder: o.injectEventPluginOrder, + injectEventPluginsByName: o.injectEventPluginsByName + }, + eventNameDispatchConfigs: o.eventNameDispatchConfigs, + registrationNameModules: o.registrationNameModules, + putListener: function(e, t, r) { + "production" !== n.env.NODE_ENV ? u(!r || "function" == typeof r, "Expected %s listener to be a function, instead got type %s", t, typeof r) : u(!r || "function" == typeof r); + var o = c[t] || (c[t] = {}); + o[e] = r + }, + getListener: function(e, t) { + var n = c[t]; + return n && n[e] + }, + deleteListener: function(e, t) { + var n = c[t]; + n && delete n[e] + }, + deleteAllListeners: function(e) { + for (var t in c) delete c[t][e] + }, + extractEvents: function(e, t, n, r) { + for (var a, s = o.plugins, u = 0, c = s.length; c > u; u++) { + var l = s[u]; + if (l) { + var p = l.extractEvents(e, t, n, r); + p && (a = i(a, p)) + } + } + return a + }, + enqueueEvents: function(e) { + e && (l = i(l, e)) + }, + processEventQueue: function() { + var e = l; + l = null, s(e, p), "production" !== n.env.NODE_ENV ? u(!l, "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented.") : u(!l) + }, + __purge: function() { + c = {} + }, + __getListenerBank: function() { + return c + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./EventPluginRegistry": 25, + "./EventPluginUtils": 26, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + "./invariant": 157, + _process: 2 + }], + 25: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + if (s) + for (var e in u) { + var t = u[e], + r = s.indexOf(e); + if ("production" !== n.env.NODE_ENV ? i(r > -1, "EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.", e) : i(r > -1), !c.plugins[r]) { + "production" !== n.env.NODE_ENV ? i(t.extractEvents, "EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.", e) : i(t.extractEvents), c.plugins[r] = t; + var a = t.eventTypes; + for (var l in a) "production" !== n.env.NODE_ENV ? i(o(a[l], t, l), "EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.", l, e) : i(o(a[l], t, l)) + } + } + } + + function o(e, t, r) { + "production" !== n.env.NODE_ENV ? i(!c.eventNameDispatchConfigs.hasOwnProperty(r), "EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.", r) : i(!c.eventNameDispatchConfigs.hasOwnProperty(r)), c.eventNameDispatchConfigs[r] = e; + var o = e.phasedRegistrationNames; + if (o) { + for (var s in o) + if (o.hasOwnProperty(s)) { + var u = o[s]; + a(u, t, r) + } return !0 + } + return e.registrationName ? (a(e.registrationName, t, r), !0) : !1 + } + + function a(e, t, r) { + "production" !== n.env.NODE_ENV ? i(!c.registrationNameModules[e], "EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.", e) : i(!c.registrationNameModules[e]), c.registrationNameModules[e] = t, c.registrationNameDependencies[e] = t.eventTypes[r].dependencies + } + var i = e("./invariant"), + s = null, + u = {}, + c = { + plugins: [], + eventNameDispatchConfigs: {}, + registrationNameModules: {}, + registrationNameDependencies: {}, + injectEventPluginOrder: function(e) { + "production" !== n.env.NODE_ENV ? i(!s, "EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React.") : i(!s), s = Array.prototype.slice.call(e), r() + }, + injectEventPluginsByName: function(e) { + var t = !1; + for (var o in e) + if (e.hasOwnProperty(o)) { + var a = e[o]; + u.hasOwnProperty(o) && u[o] === a || ("production" !== n.env.NODE_ENV ? i(!u[o], "EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.", o) : i(!u[o]), u[o] = a, t = !0) + } t && r() + }, + getPluginModuleForEvent: function(e) { + var t = e.dispatchConfig; + if (t.registrationName) return c.registrationNameModules[t.registrationName] || null; + for (var n in t.phasedRegistrationNames) + if (t.phasedRegistrationNames.hasOwnProperty(n)) { + var r = c.registrationNameModules[t.phasedRegistrationNames[n]]; + if (r) return r + } return null + }, + _resetEventPlugins: function() { + s = null; + for (var e in u) u.hasOwnProperty(e) && delete u[e]; + c.plugins.length = 0; + var t = c.eventNameDispatchConfigs; + for (var n in t) t.hasOwnProperty(n) && delete t[n]; + var r = c.registrationNameModules; + for (var o in r) r.hasOwnProperty(o) && delete r[o] + } + }; + t.exports = c + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 26: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return e === y.topMouseUp || e === y.topTouchEnd || e === y.topTouchCancel + } + + function o(e) { + return e === y.topMouseMove || e === y.topTouchMove + } + + function a(e) { + return e === y.topMouseDown || e === y.topTouchStart + } + + function i(e, t) { + var r = e._dispatchListeners, + o = e._dispatchIDs; + if ("production" !== n.env.NODE_ENV && f(e), Array.isArray(r)) + for (var a = 0; a < r.length && !e.isPropagationStopped(); a++) t(e, r[a], o[a]); + else r && t(e, r, o) + } + + function s(e, t, n) { + e.currentTarget = m.Mount.getNode(n); + var r = t(e, n); + return e.currentTarget = null, r + } + + function u(e, t) { + i(e, t), e._dispatchListeners = null, e._dispatchIDs = null + } + + function c(e) { + var t = e._dispatchListeners, + r = e._dispatchIDs; + if ("production" !== n.env.NODE_ENV && f(e), Array.isArray(t)) { + for (var o = 0; o < t.length && !e.isPropagationStopped(); o++) + if (t[o](e, r[o])) return r[o] + } else if (t && t(e, r)) return r; + return null + } + + function l(e) { + var t = c(e); + return e._dispatchIDs = null, e._dispatchListeners = null, t + } + + function p(e) { + "production" !== n.env.NODE_ENV && f(e); + var t = e._dispatchListeners, + r = e._dispatchIDs; + "production" !== n.env.NODE_ENV ? v(!Array.isArray(t), "executeDirectDispatch(...): Invalid `event`.") : v(!Array.isArray(t)); + var o = t ? t(e, r) : null; + return e._dispatchListeners = null, e._dispatchIDs = null, o + } + + function d(e) { + return !!e._dispatchListeners + } + var f, h = e("./EventConstants"), + v = e("./invariant"), + m = { + Mount: null, + injectMount: function(e) { + m.Mount = e, "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? v(e && e.getNode, "EventPluginUtils.injection.injectMount(...): Injected Mount module is missing getNode.") : v(e && e.getNode)) + } + }, + y = h.topLevelTypes; + "production" !== n.env.NODE_ENV && (f = function(e) { + var t = e._dispatchListeners, + r = e._dispatchIDs, + o = Array.isArray(t), + a = Array.isArray(r), + i = a ? r.length : r ? 1 : 0, + s = o ? t.length : t ? 1 : 0; + "production" !== n.env.NODE_ENV ? v(a === o && i === s, "EventPluginUtils: Invalid `event`.") : v(a === o && i === s) + }); + var g = { + isEndish: r, + isMoveish: o, + isStartish: a, + executeDirectDispatch: p, + executeDispatch: s, + executeDispatchesInOrder: u, + executeDispatchesInOrderStopAtTrue: l, + hasDispatches: d, + injection: m, + useTouchEvents: !1 + }; + t.exports = g + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./invariant": 157, + _process: 2 + }], + 27: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t, n) { + var r = t.dispatchConfig.phasedRegistrationNames[n]; + return m(e, r) + } + + function o(e, t, o) { + if ("production" !== n.env.NODE_ENV && !e) throw new Error("Dispatching id must not be null"); + var a = t ? v.bubbled : v.captured, + i = r(e, o, a); + i && (o._dispatchListeners = f(o._dispatchListeners, i), o._dispatchIDs = f(o._dispatchIDs, e)) + } + + function a(e) { + e && e.dispatchConfig.phasedRegistrationNames && d.injection.getInstanceHandle().traverseTwoPhase(e.dispatchMarker, o, e) + } + + function i(e, t, n) { + if (n && n.dispatchConfig.registrationName) { + var r = n.dispatchConfig.registrationName, + o = m(e, r); + o && (n._dispatchListeners = f(n._dispatchListeners, o), n._dispatchIDs = f(n._dispatchIDs, e)) + } + } + + function s(e) { + e && e.dispatchConfig.registrationName && i(e.dispatchMarker, null, e) + } + + function u(e) { + h(e, a) + } + + function c(e, t, n, r) { + d.injection.getInstanceHandle().traverseEnterLeave(n, r, i, e, t) + } + + function l(e) { + h(e, s) + } + var p = e("./EventConstants"), + d = e("./EventPluginHub"), + f = e("./accumulateInto"), + h = e("./forEachAccumulated"), + v = p.PropagationPhases, + m = d.getListener, + y = { + accumulateTwoPhaseDispatches: u, + accumulateDirectDispatches: l, + accumulateEnterLeaveDispatches: c + }; + t.exports = y + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + _process: 2 + }], + 28: [function(e, t) { + "use strict"; + var n = !("undefined" == typeof window || !window.document || !window.document.createElement), + r = { + canUseDOM: n, + canUseWorkers: "undefined" != typeof Worker, + canUseEventListeners: n && !(!window.addEventListener && !window.attachEvent), + canUseViewport: n && !!window.screen, + isInWorker: !n + }; + t.exports = r + }, {}], + 29: [function(e, t) { + "use strict"; + + function n(e) { + this._root = e, this._startText = this.getText(), this._fallbackText = null + } + var r = e("./PooledClass"), + o = e("./Object.assign"), + a = e("./getTextContentAccessor"); + o(n.prototype, { + getText: function() { + return "value" in this._root ? this._root.value : this._root[a()] + }, + getData: function() { + if (this._fallbackText) return this._fallbackText; + var e, t, n = this._startText, + r = n.length, + o = this.getText(), + a = o.length; + for (e = 0; r > e && n[e] === o[e]; e++); + var i = r - e; + for (t = 1; i >= t && n[r - t] === o[a - t]; t++); + var s = t > 1 ? 1 - t : void 0; + return this._fallbackText = o.slice(e, s), this._fallbackText + } + }), r.addPoolingTo(n), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./getTextContentAccessor": 152 + }], + 30: [function(e, t) { + "use strict"; + var n, r = e("./DOMProperty"), + o = e("./ExecutionEnvironment"), + a = r.injection.MUST_USE_ATTRIBUTE, + i = r.injection.MUST_USE_PROPERTY, + s = r.injection.HAS_BOOLEAN_VALUE, + u = r.injection.HAS_SIDE_EFFECTS, + c = r.injection.HAS_NUMERIC_VALUE, + l = r.injection.HAS_POSITIVE_NUMERIC_VALUE, + p = r.injection.HAS_OVERLOADED_BOOLEAN_VALUE; + if (o.canUseDOM) { + var d = document.implementation; + n = d && d.hasFeature && d.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") + } + var f = { + isCustomAttribute: RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/), + Properties: { + accept: null, + acceptCharset: null, + accessKey: null, + action: null, + allowFullScreen: a | s, + allowTransparency: a, + alt: null, + async: s, + autoComplete: null, + autoPlay: s, + cellPadding: null, + cellSpacing: null, + charSet: a, + checked: i | s, + classID: a, + className: n ? a : i, + cols: a | l, + colSpan: null, + content: null, + contentEditable: null, + contextMenu: a, + controls: i | s, + coords: null, + crossOrigin: null, + data: null, + dateTime: a, + defer: s, + dir: null, + disabled: a | s, + download: p, + draggable: null, + encType: null, + form: a, + formAction: a, + formEncType: a, + formMethod: a, + formNoValidate: s, + formTarget: a, + frameBorder: a, + headers: null, + height: a, + hidden: a | s, + href: null, + hrefLang: null, + htmlFor: null, + httpEquiv: null, + icon: null, + id: i, + label: null, + lang: null, + list: a, + loop: i | s, + manifest: a, + marginHeight: null, + marginWidth: null, + max: null, + maxLength: a, + media: a, + mediaGroup: null, + method: null, + min: null, + multiple: i | s, + muted: i | s, + name: null, + noValidate: s, + open: s, + pattern: null, + placeholder: null, + poster: null, + preload: null, + radioGroup: null, + readOnly: i | s, + rel: null, + required: s, + role: a, + rows: a | l, + rowSpan: null, + sandbox: null, + scope: null, + scrolling: null, + seamless: a | s, + selected: i | s, + shape: null, + size: a | l, + sizes: a, + span: l, + spellCheck: null, + src: null, + srcDoc: i, + srcSet: a, + start: c, + step: null, + style: null, + tabIndex: null, + target: null, + title: null, + type: null, + useMap: null, + value: i | u, + width: a, + wmode: a, + autoCapitalize: null, + autoCorrect: null, + itemProp: a, + itemScope: a | s, + itemType: a, + itemID: a, + itemRef: a, + property: null + }, + DOMAttributeNames: { + acceptCharset: "accept-charset", + className: "class", + htmlFor: "for", + httpEquiv: "http-equiv" + }, + DOMPropertyNames: { + autoCapitalize: "autocapitalize", + autoComplete: "autocomplete", + autoCorrect: "autocorrect", + autoFocus: "autofocus", + autoPlay: "autoplay", + encType: "encoding", + hrefLang: "hreflang", + radioGroup: "radiogroup", + spellCheck: "spellcheck", + srcDoc: "srcdoc", + srcSet: "srcset" + } + }; + t.exports = f + }, { + "./DOMProperty": 17, + "./ExecutionEnvironment": 28 + }], + 31: [function(e, t) { + "use strict"; + var n = e("./ReactLink"), + r = e("./ReactStateSetters"), + o = { + linkState: function(e) { + return new n(this.state[e], r.createStateKeySetter(this, e)) + } + }; + t.exports = o + }, { + "./ReactLink": 81, + "./ReactStateSetters": 100 + }], + 32: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + "production" !== n.env.NODE_ENV ? c(null == e.props.checkedLink || null == e.props.valueLink, "Cannot provide a checkedLink and a valueLink. If you want to use checkedLink, you probably don't want to use valueLink and vice versa.") : c(null == e.props.checkedLink || null == e.props.valueLink) + } + + function o(e) { + r(e), "production" !== n.env.NODE_ENV ? c(null == e.props.value && null == e.props.onChange, "Cannot provide a valueLink and a value or onChange event. If you want to use value or onChange, you probably don't want to use valueLink.") : c(null == e.props.value && null == e.props.onChange) + } + + function a(e) { + r(e), "production" !== n.env.NODE_ENV ? c(null == e.props.checked && null == e.props.onChange, "Cannot provide a checkedLink and a checked property or onChange event. If you want to use checked or onChange, you probably don't want to use checkedLink") : c(null == e.props.checked && null == e.props.onChange) + } + + function i(e) { + this.props.valueLink.requestChange(e.target.value) + } + + function s(e) { + this.props.checkedLink.requestChange(e.target.checked) + } + var u = e("./ReactPropTypes"), + c = e("./invariant"), + l = { + button: !0, + checkbox: !0, + image: !0, + hidden: !0, + radio: !0, + reset: !0, + submit: !0 + }, + p = { + Mixin: { + propTypes: { + value: function(e, t) { + return !e[t] || l[e.type] || e.onChange || e.readOnly || e.disabled ? null : new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.") + }, + checked: function(e, t) { + return !e[t] || e.onChange || e.readOnly || e.disabled ? null : new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.") + }, + onChange: u.func + } + }, + getValue: function(e) { + return e.props.valueLink ? (o(e), e.props.valueLink.value) : e.props.value + }, + getChecked: function(e) { + return e.props.checkedLink ? (a(e), e.props.checkedLink.value) : e.props.checked + }, + getOnChange: function(e) { + return e.props.valueLink ? (o(e), i) : e.props.checkedLink ? (a(e), s) : e.props.onChange + } + }; + t.exports = p + }).call(this, e("_process")) + }, { + "./ReactPropTypes": 92, + "./invariant": 157, + _process: 2 + }], + 33: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e.remove() + } + var o = e("./ReactBrowserEventEmitter"), + a = e("./accumulateInto"), + i = e("./forEachAccumulated"), + s = e("./invariant"), + u = { + trapBubbledEvent: function(e, t) { + "production" !== n.env.NODE_ENV ? s(this.isMounted(), "Must be mounted to trap events") : s(this.isMounted()); + var r = this.getDOMNode(); + "production" !== n.env.NODE_ENV ? s(r, "LocalEventTrapMixin.trapBubbledEvent(...): Requires node to be rendered.") : s(r); + var i = o.trapBubbledEvent(e, t, r); + this._localEventListeners = a(this._localEventListeners, i) + }, + componentWillUnmount: function() { + this._localEventListeners && i(this._localEventListeners, r) + } + }; + t.exports = u + }).call(this, e("_process")) + }, { + "./ReactBrowserEventEmitter": 39, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + "./invariant": 157, + _process: 2 + }], + 34: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./emptyFunction"), + o = n.topLevelTypes, + a = { + eventTypes: null, + extractEvents: function(e, t, n, a) { + if (e === o.topTouchStart) { + var i = a.target; + i && !i.onclick && (i.onclick = r) + } + } + }; + t.exports = a + }, { + "./EventConstants": 22, + "./emptyFunction": 136 + }], + 35: [function(e, t) { + "use strict"; + + function n(e) { + if (null == e) throw new TypeError("Object.assign target cannot be null or undefined"); + for (var t = Object(e), n = Object.prototype.hasOwnProperty, r = 1; r < arguments.length; r++) { + var o = arguments[r]; + if (null != o) { + var a = Object(o); + for (var i in a) n.call(a, i) && (t[i] = a[i]) + } + } + return t + } + t.exports = n + }, {}], + 36: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = function(e) { + var t = this; + if (t.instancePool.length) { + var n = t.instancePool.pop(); + return t.call(n, e), n + } + return new t(e) + }, + a = function(e, t) { + var n = this; + if (n.instancePool.length) { + var r = n.instancePool.pop(); + return n.call(r, e, t), r + } + return new n(e, t) + }, + i = function(e, t, n) { + var r = this; + if (r.instancePool.length) { + var o = r.instancePool.pop(); + return r.call(o, e, t, n), o + } + return new r(e, t, n) + }, + s = function(e, t, n, r, o) { + var a = this; + if (a.instancePool.length) { + var i = a.instancePool.pop(); + return a.call(i, e, t, n, r, o), i + } + return new a(e, t, n, r, o) + }, + u = function(e) { + var t = this; + "production" !== n.env.NODE_ENV ? r(e instanceof t, "Trying to release an instance into a pool of a different type.") : r(e instanceof t), e.destructor && e.destructor(), t.instancePool.length < t.poolSize && t.instancePool.push(e) + }, + c = 10, + l = o, + p = function(e, t) { + var n = e; + return n.instancePool = [], n.getPooled = t || l, n.poolSize || (n.poolSize = c), n.release = u, n + }, + d = { + addPoolingTo: p, + oneArgumentPooler: o, + twoArgumentPooler: a, + threeArgumentPooler: i, + fiveArgumentPooler: s + }; + t.exports = d + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 37: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./EventPluginUtils"), + o = e("./ReactChildren"), + a = e("./ReactComponent"), + i = e("./ReactClass"), + s = e("./ReactContext"), + u = e("./ReactCurrentOwner"), + c = e("./ReactElement"), + l = e("./ReactElementValidator"), + p = e("./ReactDOM"), + d = e("./ReactDOMTextComponent"), + f = e("./ReactDefaultInjection"), + h = e("./ReactInstanceHandles"), + v = e("./ReactMount"), + m = e("./ReactPerf"), + y = e("./ReactPropTypes"), + g = e("./ReactReconciler"), + E = e("./ReactServerRendering"), + C = e("./Object.assign"), + b = e("./findDOMNode"), + _ = e("./onlyChild"); + f.inject(); + var N = c.createElement, + O = c.createFactory, + R = c.cloneElement; + "production" !== n.env.NODE_ENV && (N = l.createElement, O = l.createFactory, R = l.cloneElement); + var D = m.measure("React", "render", v.render), + w = { + Children: { + map: o.map, + forEach: o.forEach, + count: o.count, + only: _ + }, + Component: a, + DOM: p, + PropTypes: y, + initializeTouchEvents: function(e) { + r.useTouchEvents = e + }, + createClass: i.createClass, + createElement: N, + cloneElement: R, + createFactory: O, + createMixin: function(e) { + return e + }, + constructAndRenderComponent: v.constructAndRenderComponent, + constructAndRenderComponentByID: v.constructAndRenderComponentByID, + findDOMNode: b, + render: D, + renderToString: E.renderToString, + renderToStaticMarkup: E.renderToStaticMarkup, + unmountComponentAtNode: v.unmountComponentAtNode, + isValidElement: c.isValidElement, + withContext: s.withContext, + __spread: C + }; + if ("undefined" != typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" == typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject && __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ + CurrentOwner: u, + InstanceHandles: h, + Mount: v, + Reconciler: g, + TextComponent: d + }), "production" !== n.env.NODE_ENV) { + var M = e("./ExecutionEnvironment"); + if (M.canUseDOM && window.top === window.self) { + navigator.userAgent.indexOf("Chrome") > -1 && "undefined" == typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && console.debug("Download the React DevTools for a better development experience: http://fb.me/react-devtools"); + for (var x = [Array.isArray, Array.prototype.every, Array.prototype.forEach, Array.prototype.indexOf, Array.prototype.map, Date.now, Function.prototype.bind, Object.keys, String.prototype.split, String.prototype.trim, Object.create, Object.freeze], T = 0; T < x.length; T++) + if (!x[T]) { + console.error("One or more ES5 shim/shams expected by React are not available: http://fb.me/react-warning-polyfills"); + break + } + } + } + w.version = "0.13.1", t.exports = w + }).call(this, e("_process")) + }, { + "./EventPluginUtils": 26, + "./ExecutionEnvironment": 28, + "./Object.assign": 35, + "./ReactChildren": 43, + "./ReactClass": 44, + "./ReactComponent": 45, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./ReactDOM": 52, + "./ReactDOMTextComponent": 63, + "./ReactDefaultInjection": 66, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactPerf": 88, + "./ReactPropTypes": 92, + "./ReactReconciler": 95, + "./ReactServerRendering": 98, + "./findDOMNode": 139, + "./onlyChild": 167, + _process: 2 + }], + 38: [function(e, t) { + "use strict"; + var n = e("./findDOMNode"), + r = { + getDOMNode: function() { + return n(this) + } + }; + t.exports = r + }, { + "./findDOMNode": 139 + }], + 39: [function(e, t) { + "use strict"; + + function n(e) { + return Object.prototype.hasOwnProperty.call(e, h) || (e[h] = d++, l[e[h]] = {}), l[e[h]] + } + var r = e("./EventConstants"), + o = e("./EventPluginHub"), + a = e("./EventPluginRegistry"), + i = e("./ReactEventEmitterMixin"), + s = e("./ViewportMetrics"), + u = e("./Object.assign"), + c = e("./isEventSupported"), + l = {}, + p = !1, + d = 0, + f = { + topBlur: "blur", + topChange: "change", + topClick: "click", + topCompositionEnd: "compositionend", + topCompositionStart: "compositionstart", + topCompositionUpdate: "compositionupdate", + topContextMenu: "contextmenu", + topCopy: "copy", + topCut: "cut", + topDoubleClick: "dblclick", + topDrag: "drag", + topDragEnd: "dragend", + topDragEnter: "dragenter", + topDragExit: "dragexit", + topDragLeave: "dragleave", + topDragOver: "dragover", + topDragStart: "dragstart", + topDrop: "drop", + topFocus: "focus", + topInput: "input", + topKeyDown: "keydown", + topKeyPress: "keypress", + topKeyUp: "keyup", + topMouseDown: "mousedown", + topMouseMove: "mousemove", + topMouseOut: "mouseout", + topMouseOver: "mouseover", + topMouseUp: "mouseup", + topPaste: "paste", + topScroll: "scroll", + topSelectionChange: "selectionchange", + topTextInput: "textInput", + topTouchCancel: "touchcancel", + topTouchEnd: "touchend", + topTouchMove: "touchmove", + topTouchStart: "touchstart", + topWheel: "wheel" + }, + h = "_reactListenersID" + String(Math.random()).slice(2), + v = u({}, i, { + ReactEventListener: null, + injection: { + injectReactEventListener: function(e) { + e.setHandleTopLevel(v.handleTopLevel), v.ReactEventListener = e + } + }, + setEnabled: function(e) { + v.ReactEventListener && v.ReactEventListener.setEnabled(e) + }, + isEnabled: function() { + return !(!v.ReactEventListener || !v.ReactEventListener.isEnabled()) + }, + listenTo: function(e, t) { + for (var o = t, i = n(o), s = a.registrationNameDependencies[e], u = r.topLevelTypes, l = 0, p = s.length; p > l; l++) { + var d = s[l]; + i.hasOwnProperty(d) && i[d] || (d === u.topWheel ? c("wheel") ? v.ReactEventListener.trapBubbledEvent(u.topWheel, "wheel", o) : c("mousewheel") ? v.ReactEventListener.trapBubbledEvent(u.topWheel, "mousewheel", o) : v.ReactEventListener.trapBubbledEvent(u.topWheel, "DOMMouseScroll", o) : d === u.topScroll ? c("scroll", !0) ? v.ReactEventListener.trapCapturedEvent(u.topScroll, "scroll", o) : v.ReactEventListener.trapBubbledEvent(u.topScroll, "scroll", v.ReactEventListener.WINDOW_HANDLE) : d === u.topFocus || d === u.topBlur ? (c("focus", !0) ? (v.ReactEventListener.trapCapturedEvent(u.topFocus, "focus", o), v.ReactEventListener.trapCapturedEvent(u.topBlur, "blur", o)) : c("focusin") && (v.ReactEventListener.trapBubbledEvent(u.topFocus, "focusin", o), v.ReactEventListener.trapBubbledEvent(u.topBlur, "focusout", o)), i[u.topBlur] = !0, i[u.topFocus] = !0) : f.hasOwnProperty(d) && v.ReactEventListener.trapBubbledEvent(d, f[d], o), i[d] = !0) + } + }, + trapBubbledEvent: function(e, t, n) { + return v.ReactEventListener.trapBubbledEvent(e, t, n) + }, + trapCapturedEvent: function(e, t, n) { + return v.ReactEventListener.trapCapturedEvent(e, t, n) + }, + ensureScrollValueMonitoring: function() { + if (!p) { + var e = s.refreshScrollValues; + v.ReactEventListener.monitorScrollValue(e), p = !0 + } + }, + eventNameDispatchConfigs: o.eventNameDispatchConfigs, + registrationNameModules: o.registrationNameModules, + putListener: o.putListener, + getListener: o.getListener, + deleteListener: o.deleteListener, + deleteAllListeners: o.deleteAllListeners + }); + t.exports = v + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPluginRegistry": 25, + "./Object.assign": 35, + "./ReactEventEmitterMixin": 73, + "./ViewportMetrics": 124, + "./isEventSupported": 158 + }], + 40: [function(e, t) { + "use strict"; + var n = e("./React"), + r = e("./Object.assign"), + o = n.createFactory(e("./ReactTransitionGroup")), + a = n.createFactory(e("./ReactCSSTransitionGroupChild")), + i = n.createClass({ + displayName: "ReactCSSTransitionGroup", + propTypes: { + transitionName: n.PropTypes.string.isRequired, + transitionAppear: n.PropTypes.bool, + transitionEnter: n.PropTypes.bool, + transitionLeave: n.PropTypes.bool + }, + getDefaultProps: function() { + return { + transitionAppear: !1, + transitionEnter: !0, + transitionLeave: !0 + } + }, + _wrapChild: function(e) { + return a({ + name: this.props.transitionName, + appear: this.props.transitionAppear, + enter: this.props.transitionEnter, + leave: this.props.transitionLeave + }, e) + }, + render: function() { + return o(r({}, this.props, { + childFactory: this._wrapChild + })) + } + }); + t.exports = i + }, { + "./Object.assign": 35, + "./React": 37, + "./ReactCSSTransitionGroupChild": 41, + "./ReactTransitionGroup": 104 + }], + 41: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./React"), + o = e("./CSSCore"), + a = e("./ReactTransitionEvents"), + i = e("./onlyChild"), + s = e("./warning"), + u = 17, + c = 5e3, + l = null; + "production" !== n.env.NODE_ENV && (l = function() { + "production" !== n.env.NODE_ENV ? s(!1, "transition(): tried to perform an animation without an animationend or transitionend event after timeout (%sms). You should either disable this transition in JS or add a CSS animation/transition.", c) : null + }); + var p = r.createClass({ + displayName: "ReactCSSTransitionGroupChild", + transition: function(e, t) { + var r = this.getDOMNode(), + i = this.props.name + "-" + e, + s = i + "-active", + u = null, + p = function(e) { + e && e.target !== r || ("production" !== n.env.NODE_ENV && clearTimeout(u), o.removeClass(r, i), o.removeClass(r, s), a.removeEndEventListener(r, p), t && t()) + }; + a.addEndEventListener(r, p), o.addClass(r, i), this.queueClass(s), "production" !== n.env.NODE_ENV && (u = setTimeout(l, c)) + }, + queueClass: function(e) { + this.classNameQueue.push(e), this.timeout || (this.timeout = setTimeout(this.flushClassNameQueue, u)) + }, + flushClassNameQueue: function() { + this.isMounted() && this.classNameQueue.forEach(o.addClass.bind(o, this.getDOMNode())), this.classNameQueue.length = 0, this.timeout = null + }, + componentWillMount: function() { + this.classNameQueue = [] + }, + componentWillUnmount: function() { + this.timeout && clearTimeout(this.timeout) + }, + componentWillAppear: function(e) { + this.props.appear ? this.transition("appear", e) : e() + }, + componentWillEnter: function(e) { + this.props.enter ? this.transition("enter", e) : e() + }, + componentWillLeave: function(e) { + this.props.leave ? this.transition("leave", e) : e() + }, + render: function() { + return i(this.props.children) + } + }); + t.exports = p + }).call(this, e("_process")) + }, { + "./CSSCore": 10, + "./React": 37, + "./ReactTransitionEvents": 103, + "./onlyChild": 167, + "./warning": 178, + _process: 2 + }], + 42: [function(e, t) { + "use strict"; + var n = e("./ReactReconciler"), + r = e("./flattenChildren"), + o = e("./instantiateReactComponent"), + a = e("./shouldUpdateReactComponent"), + i = { + instantiateChildren: function(e) { + var t = r(e); + for (var n in t) + if (t.hasOwnProperty(n)) { + var a = t[n], + i = o(a, null); + t[n] = i + } return t + }, + updateChildren: function(e, t, i, s) { + var u = r(t); + if (!u && !e) return null; + var c; + for (c in u) + if (u.hasOwnProperty(c)) { + var l = e && e[c], + p = l && l._currentElement, + d = u[c]; + if (a(p, d)) n.receiveComponent(l, d, i, s), u[c] = l; + else { + l && n.unmountComponent(l, c); + var f = o(d, null); + u[c] = f + } + } for (c in e) !e.hasOwnProperty(c) || u && u.hasOwnProperty(c) || n.unmountComponent(e[c]); + return u + }, + unmountChildren: function(e) { + for (var t in e) { + var r = e[t]; + n.unmountComponent(r) + } + } + }; + t.exports = i + }, { + "./ReactReconciler": 95, + "./flattenChildren": 140, + "./instantiateReactComponent": 156, + "./shouldUpdateReactComponent": 174 + }], + 43: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + this.forEachFunction = e, this.forEachContext = t + } + + function o(e, t, n, r) { + var o = e; + o.forEachFunction.call(o.forEachContext, t, r) + } + + function a(e, t, n) { + if (null == e) return e; + var a = r.getPooled(t, n); + f(e, o, a), r.release(a) + } + + function i(e, t, n) { + this.mapResult = e, this.mapFunction = t, this.mapContext = n + } + + function s(e, t, r, o) { + var a = e, + i = a.mapResult, + s = !i.hasOwnProperty(r); + if ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? h(s, "ReactChildren.map(...): Encountered two children with the same key, `%s`. Child keys must be unique; when two children share a key, only the first child will be used.", r) : null), s) { + var u = a.mapFunction.call(a.mapContext, t, o); + i[r] = u + } + } + + function u(e, t, n) { + if (null == e) return e; + var r = {}, + o = i.getPooled(r, t, n); + return f(e, s, o), i.release(o), d.create(r) + } + + function c() { + return null + } + + function l(e) { + return f(e, c, null) + } + var p = e("./PooledClass"), + d = e("./ReactFragment"), + f = e("./traverseAllChildren"), + h = e("./warning"), + v = p.twoArgumentPooler, + m = p.threeArgumentPooler; + p.addPoolingTo(r, v), p.addPoolingTo(i, m); + var y = { + forEach: a, + map: u, + count: l + }; + t.exports = y + }).call(this, e("_process")) + }, { + "./PooledClass": 36, + "./ReactFragment": 75, + "./traverseAllChildren": 176, + "./warning": 178, + _process: 2 + }], + 44: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t, r) { + for (var o in t) t.hasOwnProperty(o) && ("production" !== n.env.NODE_ENV ? R("function" == typeof t[o], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", e.displayName || "ReactClass", E[r], o) : null) + } + + function o(e, t) { + var r = x.hasOwnProperty(t) ? x[t] : null; + I.hasOwnProperty(t) && ("production" !== n.env.NODE_ENV ? _(r === w.OVERRIDE_BASE, "ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.", t) : _(r === w.OVERRIDE_BASE)), e.hasOwnProperty(t) && ("production" !== n.env.NODE_ENV ? _(r === w.DEFINE_MANY || r === w.DEFINE_MANY_MERGED, "ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.", t) : _(r === w.DEFINE_MANY || r === w.DEFINE_MANY_MERGED)) + } + + function a(e, t) { + if (t) { + "production" !== n.env.NODE_ENV ? _("function" != typeof t, "ReactClass: You're attempting to use a component class as a mixin. Instead, just use a regular object.") : _("function" != typeof t), "production" !== n.env.NODE_ENV ? _(!h.isValidElement(t), "ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.") : _(!h.isValidElement(t)); + var r = e.prototype; + t.hasOwnProperty(D) && T.mixins(e, t.mixins); + for (var a in t) + if (t.hasOwnProperty(a) && a !== D) { + var i = t[a]; + if (o(r, a), T.hasOwnProperty(a)) T[a](e, i); + else { + var s = x.hasOwnProperty(a), + l = r.hasOwnProperty(a), + p = i && i.__reactDontBind, + d = "function" == typeof i, + f = d && !s && !l && !p; + if (f) r.__reactAutoBindMap || (r.__reactAutoBindMap = {}), r.__reactAutoBindMap[a] = i, r[a] = i; + else if (l) { + var v = x[a]; + "production" !== n.env.NODE_ENV ? _(s && (v === w.DEFINE_MANY_MERGED || v === w.DEFINE_MANY), "ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.", v, a) : _(s && (v === w.DEFINE_MANY_MERGED || v === w.DEFINE_MANY)), v === w.DEFINE_MANY_MERGED ? r[a] = u(r[a], i) : v === w.DEFINE_MANY && (r[a] = c(r[a], i)) + } else r[a] = i, "production" !== n.env.NODE_ENV && "function" == typeof i && t.displayName && (r[a].displayName = t.displayName + "_" + a) + } + } + } + } + + function i(e, t) { + if (t) + for (var r in t) { + var o = t[r]; + if (t.hasOwnProperty(r)) { + var a = r in T; + "production" !== n.env.NODE_ENV ? _(!a, 'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\'t be on the "statics" key. Define it as an instance property instead; it will still be accessible on the constructor.', r) : _(!a); + var i = r in e; + "production" !== n.env.NODE_ENV ? _(!i, "ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.", r) : _(!i), e[r] = o + } + } + } + + function s(e, t) { + "production" !== n.env.NODE_ENV ? _(e && t && "object" == typeof e && "object" == typeof t, "mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.") : _(e && t && "object" == typeof e && "object" == typeof t); + for (var r in t) t.hasOwnProperty(r) && ("production" !== n.env.NODE_ENV ? _(void 0 === e[r], "mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.", r) : _(void 0 === e[r]), e[r] = t[r]); + return e + } + + function u(e, t) { + return function() { + var n = e.apply(this, arguments), + r = t.apply(this, arguments); + if (null == n) return r; + if (null == r) return n; + var o = {}; + return s(o, n), s(o, r), o + } + } + + function c(e, t) { + return function() { + e.apply(this, arguments), t.apply(this, arguments) + } + } + + function l(e, t) { + var r = t.bind(e); + if ("production" !== n.env.NODE_ENV) { + r.__reactBoundContext = e, r.__reactBoundMethod = t, r.__reactBoundArguments = null; + var o = e.constructor.displayName, + a = r.bind; + r.bind = function(i) { + for (var s = [], u = 1, c = arguments.length; c > u; u++) s.push(arguments[u]); + if (i !== e && null !== i) "production" !== n.env.NODE_ENV ? R(!1, "bind(): React component methods may only be bound to the component instance. See %s", o) : null; + else if (!s.length) return "production" !== n.env.NODE_ENV ? R(!1, "bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See %s", o) : null, r; + var l = a.apply(r, arguments); + return l.__reactBoundContext = e, l.__reactBoundMethod = t, l.__reactBoundArguments = s, l + } + } + return r + } + + function p(e) { + for (var t in e.__reactAutoBindMap) + if (e.__reactAutoBindMap.hasOwnProperty(t)) { + var n = e.__reactAutoBindMap[t]; + e[t] = l(e, v.guard(n, e.constructor.displayName + "." + t)) + } + } + var d = e("./ReactComponent"), + f = e("./ReactCurrentOwner"), + h = e("./ReactElement"), + v = e("./ReactErrorUtils"), + m = e("./ReactInstanceMap"), + y = e("./ReactLifeCycle"), + g = e("./ReactPropTypeLocations"), + E = e("./ReactPropTypeLocationNames"), + C = e("./ReactUpdateQueue"), + b = e("./Object.assign"), + _ = e("./invariant"), + N = e("./keyMirror"), + O = e("./keyOf"), + R = e("./warning"), + D = O({ + mixins: null + }), + w = N({ + DEFINE_ONCE: null, + DEFINE_MANY: null, + OVERRIDE_BASE: null, + DEFINE_MANY_MERGED: null + }), + M = [], + x = { + mixins: w.DEFINE_MANY, + statics: w.DEFINE_MANY, + propTypes: w.DEFINE_MANY, + contextTypes: w.DEFINE_MANY, + childContextTypes: w.DEFINE_MANY, + getDefaultProps: w.DEFINE_MANY_MERGED, + getInitialState: w.DEFINE_MANY_MERGED, + getChildContext: w.DEFINE_MANY_MERGED, + render: w.DEFINE_ONCE, + componentWillMount: w.DEFINE_MANY, + componentDidMount: w.DEFINE_MANY, + componentWillReceiveProps: w.DEFINE_MANY, + shouldComponentUpdate: w.DEFINE_ONCE, + componentWillUpdate: w.DEFINE_MANY, + componentDidUpdate: w.DEFINE_MANY, + componentWillUnmount: w.DEFINE_MANY, + updateComponent: w.OVERRIDE_BASE + }, + T = { + displayName: function(e, t) { + e.displayName = t + }, + mixins: function(e, t) { + if (t) + for (var n = 0; n < t.length; n++) a(e, t[n]) + }, + childContextTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.childContext), e.childContextTypes = b({}, e.childContextTypes, t) + }, + contextTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.context), e.contextTypes = b({}, e.contextTypes, t) + }, + getDefaultProps: function(e, t) { + e.getDefaultProps = e.getDefaultProps ? u(e.getDefaultProps, t) : t; + + }, + propTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.prop), e.propTypes = b({}, e.propTypes, t) + }, + statics: function(e, t) { + i(e, t) + } + }, + P = { + enumerable: !1, + get: function() { + var e = this.displayName || this.name || "Component"; + return "production" !== n.env.NODE_ENV ? R(!1, "%s.type is deprecated. Use %s directly to access the class.", e, e) : null, Object.defineProperty(this, "type", { + value: this + }), this + } + }, + I = { + replaceState: function(e, t) { + C.enqueueReplaceState(this, e), t && C.enqueueCallback(this, t) + }, + isMounted: function() { + if ("production" !== n.env.NODE_ENV) { + var e = f.current; + null !== e && ("production" !== n.env.NODE_ENV ? R(e._warnedAboutRefsInRender, "%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.", e.getName() || "A component") : null, e._warnedAboutRefsInRender = !0) + } + var t = m.get(this); + return t && t !== y.currentlyMountingInstance + }, + setProps: function(e, t) { + C.enqueueSetProps(this, e), t && C.enqueueCallback(this, t) + }, + replaceProps: function(e, t) { + C.enqueueReplaceProps(this, e), t && C.enqueueCallback(this, t) + } + }, + S = function() {}; + b(S.prototype, d.prototype, I); + var k = { + createClass: function(e) { + var t = function(e, r) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? R(this instanceof t, "Something is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory") : null), this.__reactAutoBindMap && p(this), this.props = e, this.context = r, this.state = null; + var o = this.getInitialState ? this.getInitialState() : null; + "production" !== n.env.NODE_ENV && "undefined" == typeof o && this.getInitialState._isMockFunction && (o = null), "production" !== n.env.NODE_ENV ? _("object" == typeof o && !Array.isArray(o), "%s.getInitialState(): must return an object or null", t.displayName || "ReactCompositeComponent") : _("object" == typeof o && !Array.isArray(o)), this.state = o + }; + t.prototype = new S, t.prototype.constructor = t, M.forEach(a.bind(null, t)), a(t, e), t.getDefaultProps && (t.defaultProps = t.getDefaultProps()), "production" !== n.env.NODE_ENV && (t.getDefaultProps && (t.getDefaultProps.isReactClassApproved = {}), t.prototype.getInitialState && (t.prototype.getInitialState.isReactClassApproved = {})), "production" !== n.env.NODE_ENV ? _(t.prototype.render, "createClass(...): Class specification must implement a `render` method.") : _(t.prototype.render), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? R(!t.prototype.componentShouldUpdate, "%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.", e.displayName || "A component") : null); + for (var r in x) t.prototype[r] || (t.prototype[r] = null); + if (t.type = t, "production" !== n.env.NODE_ENV) try { + Object.defineProperty(t, "type", P) + } catch (o) {} + return t + }, + injection: { + injectMixin: function(e) { + M.push(e) + } + } + }; + t.exports = k + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactComponent": 45, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactErrorUtils": 72, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./ReactUpdateQueue": 105, + "./invariant": 157, + "./keyMirror": 163, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 45: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + this.props = e, this.context = t + } + var o = e("./ReactUpdateQueue"), + a = e("./invariant"), + i = e("./warning"); + if (r.prototype.setState = function(e, t) { + "production" !== n.env.NODE_ENV ? a("object" == typeof e || "function" == typeof e || null == e, "setState(...): takes an object of state variables to update or a function which returns an object of state variables.") : a("object" == typeof e || "function" == typeof e || null == e), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? i(null != e, "setState(...): You passed an undefined or null state object; instead, use forceUpdate().") : null), o.enqueueSetState(this, e), t && o.enqueueCallback(this, t) + }, r.prototype.forceUpdate = function(e) { + o.enqueueForceUpdate(this), e && o.enqueueCallback(this, e) + }, "production" !== n.env.NODE_ENV) { + var s = { + getDOMNode: "getDOMNode", + isMounted: "isMounted", + replaceProps: "replaceProps", + replaceState: "replaceState", + setProps: "setProps" + }, + u = function(e, t) { + try { + Object.defineProperty(r.prototype, e, { + get: function() { + return void("production" !== n.env.NODE_ENV ? i(!1, "%s(...) is deprecated in plain JavaScript React classes.", t) : null) + } + }) + } catch (o) {} + }; + for (var c in s) s.hasOwnProperty(c) && u(c, s[c]) + } + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactUpdateQueue": 105, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 46: [function(e, t) { + "use strict"; + var n = e("./ReactDOMIDOperations"), + r = e("./ReactMount"), + o = { + processChildrenUpdates: n.dangerouslyProcessChildrenUpdates, + replaceNodeWithMarkupByID: n.dangerouslyReplaceNodeWithMarkupByID, + unmountIDFromEnvironment: function(e) { + r.purgeID(e) + } + }; + t.exports = o + }, { + "./ReactDOMIDOperations": 56, + "./ReactMount": 83 + }], + 47: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = !1, + a = { + unmountIDFromEnvironment: null, + replaceNodeWithMarkupByID: null, + processChildrenUpdates: null, + injection: { + injectEnvironment: function(e) { + "production" !== n.env.NODE_ENV ? r(!o, "ReactCompositeComponent: injectEnvironment() can only be called once.") : r(!o), a.unmountIDFromEnvironment = e.unmountIDFromEnvironment, a.replaceNodeWithMarkupByID = e.replaceNodeWithMarkupByID, a.processChildrenUpdates = e.processChildrenUpdates, o = !0 + } + } + }; + t.exports = a + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 48: [function(e, t) { + "use strict"; + var n = e("./shallowEqual"), + r = { + shouldComponentUpdate: function(e, t) { + return !n(this.props, e) || !n(this.state, t) + } + }; + t.exports = r + }, { + "./shallowEqual": 173 + }], + 49: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + var t = e._currentElement._owner || null; + if (t) { + var n = t.getName(); + if (n) return " Check the render method of `" + n + "`." + } + return "" + } + var o = e("./ReactComponentEnvironment"), + a = e("./ReactContext"), + i = e("./ReactCurrentOwner"), + s = e("./ReactElement"), + u = e("./ReactElementValidator"), + c = e("./ReactInstanceMap"), + l = e("./ReactLifeCycle"), + p = e("./ReactNativeComponent"), + d = e("./ReactPerf"), + f = e("./ReactPropTypeLocations"), + h = e("./ReactPropTypeLocationNames"), + v = e("./ReactReconciler"), + m = e("./ReactUpdates"), + y = e("./Object.assign"), + g = e("./emptyObject"), + E = e("./invariant"), + C = e("./shouldUpdateReactComponent"), + b = e("./warning"), + _ = 1, + N = { + construct: function(e) { + this._currentElement = e, this._rootNodeID = null, this._instance = null, this._pendingElement = null, this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1, this._renderedComponent = null, this._context = null, this._mountOrder = 0, this._isTopLevel = !1, this._pendingCallbacks = null + }, + mountComponent: function(e, t, r) { + this._context = r, this._mountOrder = _++, this._rootNodeID = e; + var o = this._processProps(this._currentElement.props), + a = this._processContext(this._currentElement._context), + i = p.getComponentClassForElement(this._currentElement), + s = new i(o, a); + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b(null != s.render, "%s(...): No `render` method found on the returned component instance: you may have forgotten to define `render` in your component or you may have accidentally tried to render an element whose type is a function that isn't a React component.", i.displayName || i.name || "Component") : null), s.props = o, s.context = a, s.refs = g, this._instance = s, c.set(s, this), "production" !== n.env.NODE_ENV && this._warnIfContextsDiffer(this._currentElement._context, r), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b(!s.getInitialState || s.getInitialState.isReactClassApproved, "getInitialState was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b(!s.propTypes, "propTypes was defined as an instance property on %s. Use a static property to define propTypes instead.", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b(!s.contextTypes, "contextTypes was defined as an instance property on %s. Use a static property to define contextTypes instead.", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b("function" != typeof s.componentShouldUpdate, "%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.", this.getName() || "A component") : null); + var u = s.state; + void 0 === u && (s.state = u = null), "production" !== n.env.NODE_ENV ? E("object" == typeof u && !Array.isArray(u), "%s.state: must be set to an object or null", this.getName() || "ReactCompositeComponent") : E("object" == typeof u && !Array.isArray(u)), this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1; + var d, f = l.currentlyMountingInstance; + l.currentlyMountingInstance = this; + try { + s.componentWillMount && (s.componentWillMount(), this._pendingStateQueue && (s.state = this._processPendingState(s.props, s.context))), d = this._renderValidatedComponent() + } finally { + l.currentlyMountingInstance = f + } + this._renderedComponent = this._instantiateReactComponent(d, this._currentElement.type); + var h = v.mountComponent(this._renderedComponent, e, t, this._processChildContext(r)); + return s.componentDidMount && t.getReactMountReady().enqueue(s.componentDidMount, s), h + }, + unmountComponent: function() { + var e = this._instance; + if (e.componentWillUnmount) { + var t = l.currentlyUnmountingInstance; + l.currentlyUnmountingInstance = this; + try { + e.componentWillUnmount() + } finally { + l.currentlyUnmountingInstance = t + } + } + v.unmountComponent(this._renderedComponent), this._renderedComponent = null, this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1, this._pendingCallbacks = null, this._pendingElement = null, this._context = null, this._rootNodeID = null, c.remove(e) + }, + _setPropsInternal: function(e, t) { + var n = this._pendingElement || this._currentElement; + this._pendingElement = s.cloneAndReplaceProps(n, y({}, n.props, e)), m.enqueueUpdate(this, t) + }, + _maskContext: function(e) { + var t = null; + if ("string" == typeof this._currentElement.type) return g; + var n = this._currentElement.type.contextTypes; + if (!n) return g; + t = {}; + for (var r in n) t[r] = e[r]; + return t + }, + _processContext: function(e) { + var t = this._maskContext(e); + if ("production" !== n.env.NODE_ENV) { + var r = p.getComponentClassForElement(this._currentElement); + r.contextTypes && this._checkPropTypes(r.contextTypes, t, f.context) + } + return t + }, + _processChildContext: function(e) { + var t = this._instance, + r = t.getChildContext && t.getChildContext(); + if (r) { + "production" !== n.env.NODE_ENV ? E("object" == typeof t.constructor.childContextTypes, "%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().", this.getName() || "ReactCompositeComponent") : E("object" == typeof t.constructor.childContextTypes), "production" !== n.env.NODE_ENV && this._checkPropTypes(t.constructor.childContextTypes, r, f.childContext); + for (var o in r) "production" !== n.env.NODE_ENV ? E(o in t.constructor.childContextTypes, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', this.getName() || "ReactCompositeComponent", o) : E(o in t.constructor.childContextTypes); + return y({}, e, r) + } + return e + }, + _processProps: function(e) { + if ("production" !== n.env.NODE_ENV) { + var t = p.getComponentClassForElement(this._currentElement); + t.propTypes && this._checkPropTypes(t.propTypes, e, f.prop) + } + return e + }, + _checkPropTypes: function(e, t, o) { + var a = this.getName(); + for (var i in e) + if (e.hasOwnProperty(i)) { + var s; + try { + "production" !== n.env.NODE_ENV ? E("function" == typeof e[i], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", a || "React class", h[o], i) : E("function" == typeof e[i]), s = e[i](t, i, a, o) + } catch (u) { + s = u + } + if (s instanceof Error) { + var c = r(this); + o === f.prop ? "production" !== n.env.NODE_ENV ? b(!1, "Failed Composite propType: %s%s", s.message, c) : null : "production" !== n.env.NODE_ENV ? b(!1, "Failed Context Types: %s%s", s.message, c) : null + } + } + }, + receiveComponent: function(e, t, n) { + var r = this._currentElement, + o = this._context; + this._pendingElement = null, this.updateComponent(t, r, e, o, n) + }, + performUpdateIfNecessary: function(e) { + null != this._pendingElement && v.receiveComponent(this, this._pendingElement || this._currentElement, e, this._context), (null !== this._pendingStateQueue || this._pendingForceUpdate) && ("production" !== n.env.NODE_ENV && u.checkAndWarnForMutatedProps(this._currentElement), this.updateComponent(e, this._currentElement, this._currentElement, this._context, this._context)) + }, + _warnIfContextsDiffer: function(e, t) { + e = this._maskContext(e), t = this._maskContext(t); + for (var r = Object.keys(t).sort(), o = this.getName() || "ReactCompositeComponent", a = 0; a < r.length; a++) { + var i = r[a]; + "production" !== n.env.NODE_ENV ? b(e[i] === t[i], "owner-based and parent-based contexts differ (values: `%s` vs `%s`) for key (%s) while mounting %s (see: http://fb.me/react-context-by-parent)", e[i], t[i], i, o) : null + } + }, + updateComponent: function(e, t, r, o, a) { + var i = this._instance, + s = i.context, + u = i.props; + t !== r && (s = this._processContext(r._context), u = this._processProps(r.props), "production" !== n.env.NODE_ENV && null != a && this._warnIfContextsDiffer(r._context, a), i.componentWillReceiveProps && i.componentWillReceiveProps(u, s)); + var c = this._processPendingState(u, s), + l = this._pendingForceUpdate || !i.shouldComponentUpdate || i.shouldComponentUpdate(u, c, s); + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b("undefined" != typeof l, "%s.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.", this.getName() || "ReactCompositeComponent") : null), l ? (this._pendingForceUpdate = !1, this._performComponentUpdate(r, u, c, s, e, a)) : (this._currentElement = r, this._context = a, i.props = u, i.state = c, i.context = s) + }, + _processPendingState: function(e, t) { + var n = this._instance, + r = this._pendingStateQueue, + o = this._pendingReplaceState; + if (this._pendingReplaceState = !1, this._pendingStateQueue = null, !r) return n.state; + for (var a = y({}, o ? r[0] : n.state), i = o ? 1 : 0; i < r.length; i++) { + var s = r[i]; + y(a, "function" == typeof s ? s.call(n, a, e, t) : s) + } + return a + }, + _performComponentUpdate: function(e, t, n, r, o, a) { + var i = this._instance, + s = i.props, + u = i.state, + c = i.context; + i.componentWillUpdate && i.componentWillUpdate(t, n, r), this._currentElement = e, this._context = a, i.props = t, i.state = n, i.context = r, this._updateRenderedComponent(o, a), i.componentDidUpdate && o.getReactMountReady().enqueue(i.componentDidUpdate.bind(i, s, u, c), i) + }, + _updateRenderedComponent: function(e, t) { + var n = this._renderedComponent, + r = n._currentElement, + o = this._renderValidatedComponent(); + if (C(r, o)) v.receiveComponent(n, o, e, this._processChildContext(t)); + else { + var a = this._rootNodeID, + i = n._rootNodeID; + v.unmountComponent(n), this._renderedComponent = this._instantiateReactComponent(o, this._currentElement.type); + var s = v.mountComponent(this._renderedComponent, a, e, t); + this._replaceNodeWithMarkupByID(i, s) + } + }, + _replaceNodeWithMarkupByID: function(e, t) { + o.replaceNodeWithMarkupByID(e, t) + }, + _renderValidatedComponentWithoutOwnerOrContext: function() { + var e = this._instance, + t = e.render(); + return "production" !== n.env.NODE_ENV && "undefined" == typeof t && e.render._isMockFunction && (t = null), t + }, + _renderValidatedComponent: function() { + var e, t = a.current; + a.current = this._processChildContext(this._currentElement._context), i.current = this; + try { + e = this._renderValidatedComponentWithoutOwnerOrContext() + } finally { + a.current = t, i.current = null + } + return "production" !== n.env.NODE_ENV ? E(null === e || e === !1 || s.isValidElement(e), "%s.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.", this.getName() || "ReactCompositeComponent") : E(null === e || e === !1 || s.isValidElement(e)), e + }, + attachRef: function(e, t) { + var n = this.getPublicInstance(), + r = n.refs === g ? n.refs = {} : n.refs; + r[e] = t.getPublicInstance() + }, + detachRef: function(e) { + var t = this.getPublicInstance().refs; + delete t[e] + }, + getName: function() { + var e = this._currentElement.type, + t = this._instance && this._instance.constructor; + return e.displayName || t && t.displayName || e.name || t && t.name || null + }, + getPublicInstance: function() { + return this._instance + }, + _instantiateReactComponent: null + }; + d.measureMethods(N, "ReactCompositeComponent", { + mountComponent: "mountComponent", + updateComponent: "updateComponent", + _renderValidatedComponent: "_renderValidatedComponent" + }); + var O = { + Mixin: N + }; + t.exports = O + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactComponentEnvironment": 47, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactNativeComponent": 86, + "./ReactPerf": 88, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./ReactReconciler": 95, + "./ReactUpdates": 106, + "./emptyObject": 137, + "./invariant": 157, + "./shouldUpdateReactComponent": 174, + "./warning": 178, + _process: 2 + }], + 50: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./Object.assign"), + o = e("./emptyObject"), + a = e("./warning"), + i = !1, + s = { + current: o, + withContext: function(e, t) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? a(i, "withContext is deprecated and will be removed in a future version. Use a wrapper component with getChildContext instead.") : null, i = !0); + var o, u = s.current; + s.current = r({}, u, e); + try { + o = t() + } finally { + s.current = u + } + return o + } + }; + t.exports = s + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./emptyObject": 137, + "./warning": 178, + _process: 2 + }], + 51: [function(e, t) { + "use strict"; + var n = { + current: null + }; + t.exports = n + }, {}], + 52: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return "production" !== n.env.NODE_ENV ? a.createFactory(e) : o.createFactory(e) + } + var o = e("./ReactElement"), + a = e("./ReactElementValidator"), + i = e("./mapObject"), + s = i({ + a: "a", + abbr: "abbr", + address: "address", + area: "area", + article: "article", + aside: "aside", + audio: "audio", + b: "b", + base: "base", + bdi: "bdi", + bdo: "bdo", + big: "big", + blockquote: "blockquote", + body: "body", + br: "br", + button: "button", + canvas: "canvas", + caption: "caption", + cite: "cite", + code: "code", + col: "col", + colgroup: "colgroup", + data: "data", + datalist: "datalist", + dd: "dd", + del: "del", + details: "details", + dfn: "dfn", + dialog: "dialog", + div: "div", + dl: "dl", + dt: "dt", + em: "em", + embed: "embed", + fieldset: "fieldset", + figcaption: "figcaption", + figure: "figure", + footer: "footer", + form: "form", + h1: "h1", + h2: "h2", + h3: "h3", + h4: "h4", + h5: "h5", + h6: "h6", + head: "head", + header: "header", + hr: "hr", + html: "html", + i: "i", + iframe: "iframe", + img: "img", + input: "input", + ins: "ins", + kbd: "kbd", + keygen: "keygen", + label: "label", + legend: "legend", + li: "li", + link: "link", + main: "main", + map: "map", + mark: "mark", + menu: "menu", + menuitem: "menuitem", + meta: "meta", + meter: "meter", + nav: "nav", + noscript: "noscript", + object: "object", + ol: "ol", + optgroup: "optgroup", + option: "option", + output: "output", + p: "p", + param: "param", + picture: "picture", + pre: "pre", + progress: "progress", + q: "q", + rp: "rp", + rt: "rt", + ruby: "ruby", + s: "s", + samp: "samp", + script: "script", + section: "section", + select: "select", + small: "small", + source: "source", + span: "span", + strong: "strong", + style: "style", + sub: "sub", + summary: "summary", + sup: "sup", + table: "table", + tbody: "tbody", + td: "td", + textarea: "textarea", + tfoot: "tfoot", + th: "th", + thead: "thead", + time: "time", + title: "title", + tr: "tr", + track: "track", + u: "u", + ul: "ul", + "var": "var", + video: "video", + wbr: "wbr", + circle: "circle", + defs: "defs", + ellipse: "ellipse", + g: "g", + line: "line", + linearGradient: "linearGradient", + mask: "mask", + path: "path", + pattern: "pattern", + polygon: "polygon", + polyline: "polyline", + radialGradient: "radialGradient", + rect: "rect", + stop: "stop", + svg: "svg", + text: "text", + tspan: "tspan" + }, r); + t.exports = s + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./mapObject": 165, + _process: 2 + }], + 53: [function(e, t) { + "use strict"; + var n = e("./AutoFocusMixin"), + r = e("./ReactBrowserComponentMixin"), + o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./keyMirror"), + s = a.createFactory("button"), + u = i({ + onClick: !0, + onDoubleClick: !0, + onMouseDown: !0, + onMouseMove: !0, + onMouseUp: !0, + onClickCapture: !0, + onDoubleClickCapture: !0, + onMouseDownCapture: !0, + onMouseMoveCapture: !0, + onMouseUpCapture: !0 + }), + c = o.createClass({ + displayName: "ReactDOMButton", + tagName: "BUTTON", + mixins: [n, r], + render: function() { + var e = {}; + for (var t in this.props) !this.props.hasOwnProperty(t) || this.props.disabled && u[t] || (e[t] = this.props[t]); + return s(e, this.props.children) + } + }); + t.exports = c + }, { + "./AutoFocusMixin": 8, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./keyMirror": 163 + }], + 54: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e && (null != e.dangerouslySetInnerHTML && ("production" !== n.env.NODE_ENV ? y(null == e.children, "Can only set one of `children` or `props.dangerouslySetInnerHTML`.") : y(null == e.children), "production" !== n.env.NODE_ENV ? y(null != e.dangerouslySetInnerHTML.__html, "`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit http://fb.me/react-invariant-dangerously-set-inner-html for more information.") : y(null != e.dangerouslySetInnerHTML.__html)), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? C(null == e.innerHTML, "Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`.") : null, "production" !== n.env.NODE_ENV ? C(!e.contentEditable || null == e.children, "A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.") : null), "production" !== n.env.NODE_ENV ? y(null == e.style || "object" == typeof e.style, "The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.") : y(null == e.style || "object" == typeof e.style)) + } + + function o(e, t, r, o) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? C("onScroll" !== t || g("scroll", !0), "This browser doesn't support the `onScroll` event") : null); + var a = d.findReactContainerForID(e); + if (a) { + var i = a.nodeType === D ? a.ownerDocument : a; + _(t, i) + } + o.getPutListenerQueue().enqueuePutListener(e, t, r) + } + + function a(e) { + P.call(T, e) || ("production" !== n.env.NODE_ENV ? y(x.test(e), "Invalid tag: %s", e) : y(x.test(e)), T[e] = !0) + } + + function i(e) { + a(e), this._tag = e, this._renderedChildren = null, this._previousStyleCopy = null, this._rootNodeID = null + } + var s = e("./CSSPropertyOperations"), + u = e("./DOMProperty"), + c = e("./DOMPropertyOperations"), + l = e("./ReactBrowserEventEmitter"), + p = e("./ReactComponentBrowserEnvironment"), + d = e("./ReactMount"), + f = e("./ReactMultiChild"), + h = e("./ReactPerf"), + v = e("./Object.assign"), + m = e("./escapeTextContentForBrowser"), + y = e("./invariant"), + g = e("./isEventSupported"), + E = e("./keyOf"), + C = e("./warning"), + b = l.deleteListener, + _ = l.listenTo, + N = l.registrationNameModules, + O = { + string: !0, + number: !0 + }, + R = E({ + style: null + }), + D = 1, + w = null, + M = { + area: !0, + base: !0, + br: !0, + col: !0, + embed: !0, + hr: !0, + img: !0, + input: !0, + keygen: !0, + link: !0, + meta: !0, + param: !0, + source: !0, + track: !0, + wbr: !0 + }, + x = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/, + T = {}, + P = {}.hasOwnProperty; + i.displayName = "ReactDOMComponent", i.Mixin = { + construct: function(e) { + this._currentElement = e + }, + mountComponent: function(e, t, n) { + this._rootNodeID = e, r(this._currentElement.props); + var o = M[this._tag] ? "" : "" + this._tag + ">"; + return this._createOpenTagMarkupAndPutListeners(t) + this._createContentMarkup(t, n) + o + }, + _createOpenTagMarkupAndPutListeners: function(e) { + var t = this._currentElement.props, + n = "<" + this._tag; + for (var r in t) + if (t.hasOwnProperty(r)) { + var a = t[r]; + if (null != a) + if (N.hasOwnProperty(r)) o(this._rootNodeID, r, a, e); + else { + r === R && (a && (a = this._previousStyleCopy = v({}, t.style)), a = s.createMarkupForStyles(a)); + var i = c.createMarkupForProperty(r, a); + i && (n += " " + i) + } + } if (e.renderToStaticMarkup) return n + ">"; + var u = c.createMarkupForID(this._rootNodeID); + return n + " " + u + ">" + }, + _createContentMarkup: function(e, t) { + var n = ""; + ("listing" === this._tag || "pre" === this._tag || "textarea" === this._tag) && (n = "\n"); + var r = this._currentElement.props, + o = r.dangerouslySetInnerHTML; + if (null != o) { + if (null != o.__html) return n + o.__html + } else { + var a = O[typeof r.children] ? r.children : null, + i = null != a ? null : r.children; + if (null != a) return n + m(a); + if (null != i) { + var s = this.mountChildren(i, e, t); + return n + s.join("") + } + } + return n + }, + receiveComponent: function(e, t, n) { + var r = this._currentElement; + this._currentElement = e, this.updateComponent(t, r, e, n) + }, + updateComponent: function(e, t, n, o) { + r(this._currentElement.props), this._updateDOMProperties(t.props, e), this._updateDOMChildren(t.props, e, o) + }, + _updateDOMProperties: function(e, t) { + var n, r, a, i = this._currentElement.props; + for (n in e) + if (!i.hasOwnProperty(n) && e.hasOwnProperty(n)) + if (n === R) { + var s = this._previousStyleCopy; + for (r in s) s.hasOwnProperty(r) && (a = a || {}, a[r] = ""); + this._previousStyleCopy = null + } else N.hasOwnProperty(n) ? b(this._rootNodeID, n) : (u.isStandardName[n] || u.isCustomAttribute(n)) && w.deletePropertyByID(this._rootNodeID, n); + for (n in i) { + var c = i[n], + l = n === R ? this._previousStyleCopy : e[n]; + if (i.hasOwnProperty(n) && c !== l) + if (n === R) + if (c && (c = this._previousStyleCopy = v({}, c)), l) { + for (r in l) !l.hasOwnProperty(r) || c && c.hasOwnProperty(r) || (a = a || {}, a[r] = ""); + for (r in c) c.hasOwnProperty(r) && l[r] !== c[r] && (a = a || {}, a[r] = c[r]) + } else a = c; + else N.hasOwnProperty(n) ? o(this._rootNodeID, n, c, t) : (u.isStandardName[n] || u.isCustomAttribute(n)) && w.updatePropertyByID(this._rootNodeID, n, c) + } + a && w.updateStylesByID(this._rootNodeID, a) + }, + _updateDOMChildren: function(e, t, n) { + var r = this._currentElement.props, + o = O[typeof e.children] ? e.children : null, + a = O[typeof r.children] ? r.children : null, + i = e.dangerouslySetInnerHTML && e.dangerouslySetInnerHTML.__html, + s = r.dangerouslySetInnerHTML && r.dangerouslySetInnerHTML.__html, + u = null != o ? null : e.children, + c = null != a ? null : r.children, + l = null != o || null != i, + p = null != a || null != s; + null != u && null == c ? this.updateChildren(null, t, n) : l && !p && this.updateTextContent(""), null != a ? o !== a && this.updateTextContent("" + a) : null != s ? i !== s && w.updateInnerHTMLByID(this._rootNodeID, s) : null != c && this.updateChildren(c, t, n) + }, + unmountComponent: function() { + this.unmountChildren(), l.deleteAllListeners(this._rootNodeID), p.unmountIDFromEnvironment(this._rootNodeID), this._rootNodeID = null + } + }, h.measureMethods(i, "ReactDOMComponent", { + mountComponent: "mountComponent", + updateComponent: "updateComponent" + }), v(i.prototype, i.Mixin, f.Mixin), i.injection = { + injectIDOperations: function(e) { + i.BackendIDOperations = w = e + } + }, t.exports = i + }).call(this, e("_process")) + }, { + "./CSSPropertyOperations": 12, + "./DOMProperty": 17, + "./DOMPropertyOperations": 18, + "./Object.assign": 35, + "./ReactBrowserEventEmitter": 39, + "./ReactComponentBrowserEnvironment": 46, + "./ReactMount": 83, + "./ReactMultiChild": 84, + "./ReactPerf": 88, + "./escapeTextContentForBrowser": 138, + "./invariant": 157, + "./isEventSupported": 158, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 55: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("form"), + u = a.createClass({ + displayName: "ReactDOMForm", + tagName: "FORM", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topReset, "reset"), this.trapBubbledEvent(n.topLevelTypes.topSubmit, "submit") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 56: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./CSSPropertyOperations"), + o = e("./DOMChildrenOperations"), + a = e("./DOMPropertyOperations"), + i = e("./ReactMount"), + s = e("./ReactPerf"), + u = e("./invariant"), + c = e("./setInnerHTML"), + l = { + dangerouslySetInnerHTML: "`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.", + style: "`style` must be set using `updateStylesByID()`." + }, + p = { + updatePropertyByID: function(e, t, r) { + var o = i.getNode(e); + "production" !== n.env.NODE_ENV ? u(!l.hasOwnProperty(t), "updatePropertyByID(...): %s", l[t]) : u(!l.hasOwnProperty(t)), null != r ? a.setValueForProperty(o, t, r) : a.deleteValueForProperty(o, t) + }, + deletePropertyByID: function(e, t, r) { + var o = i.getNode(e); + "production" !== n.env.NODE_ENV ? u(!l.hasOwnProperty(t), "updatePropertyByID(...): %s", l[t]) : u(!l.hasOwnProperty(t)), a.deleteValueForProperty(o, t, r) + }, + updateStylesByID: function(e, t) { + var n = i.getNode(e); + r.setValueForStyles(n, t) + }, + updateInnerHTMLByID: function(e, t) { + var n = i.getNode(e); + c(n, t) + }, + updateTextContentByID: function(e, t) { + var n = i.getNode(e); + o.updateTextContent(n, t) + }, + dangerouslyReplaceNodeWithMarkupByID: function(e, t) { + var n = i.getNode(e); + o.dangerouslyReplaceNodeWithMarkup(n, t) + }, + dangerouslyProcessChildrenUpdates: function(e, t) { + for (var n = 0; n < e.length; n++) e[n].parentNode = i.getNode(e[n].parentID); + o.processUpdates(e, t) + } + }; + s.measureMethods(p, "ReactDOMIDOperations", { + updatePropertyByID: "updatePropertyByID", + deletePropertyByID: "deletePropertyByID", + updateStylesByID: "updateStylesByID", + updateInnerHTMLByID: "updateInnerHTMLByID", + updateTextContentByID: "updateTextContentByID", + dangerouslyReplaceNodeWithMarkupByID: "dangerouslyReplaceNodeWithMarkupByID", + dangerouslyProcessChildrenUpdates: "dangerouslyProcessChildrenUpdates" + }), t.exports = p + }).call(this, e("_process")) + }, { + "./CSSPropertyOperations": 12, + "./DOMChildrenOperations": 16, + "./DOMPropertyOperations": 18, + "./ReactMount": 83, + "./ReactPerf": 88, + "./invariant": 157, + "./setInnerHTML": 171, + _process: 2 + }], + 57: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("iframe"), + u = a.createClass({ + displayName: "ReactDOMIframe", + tagName: "IFRAME", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topLoad, "load") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 58: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("img"), + u = a.createClass({ + displayName: "ReactDOMImg", + tagName: "IMG", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topLoad, "load"), this.trapBubbledEvent(n.topLevelTypes.topError, "error") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 59: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + this.isMounted() && this.forceUpdate() + } + var o = e("./AutoFocusMixin"), + a = e("./DOMPropertyOperations"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactMount"), + p = e("./ReactUpdates"), + d = e("./Object.assign"), + f = e("./invariant"), + h = c.createFactory("input"), + v = {}, + m = u.createClass({ + displayName: "ReactDOMInput", + tagName: "INPUT", + mixins: [o, i.Mixin, s], + getInitialState: function() { + var e = this.props.defaultValue; + return { + initialChecked: this.props.defaultChecked || !1, + initialValue: null != e ? e : null + } + }, + render: function() { + var e = d({}, this.props); + e.defaultChecked = null, e.defaultValue = null; + var t = i.getValue(this); + e.value = null != t ? t : this.state.initialValue; + var n = i.getChecked(this); + return e.checked = null != n ? n : this.state.initialChecked, e.onChange = this._handleChange, h(e, this.props.children) + }, + componentDidMount: function() { + var e = l.getID(this.getDOMNode()); + v[e] = this + }, + componentWillUnmount: function() { + var e = this.getDOMNode(), + t = l.getID(e); + delete v[t] + }, + componentDidUpdate: function() { + var e = this.getDOMNode(); + null != this.props.checked && a.setValueForProperty(e, "checked", this.props.checked || !1); + var t = i.getValue(this); + null != t && a.setValueForProperty(e, "value", "" + t) + }, + _handleChange: function(e) { + var t, o = i.getOnChange(this); + o && (t = o.call(this, e)), p.asap(r, this); + var a = this.props.name; + if ("radio" === this.props.type && null != a) { + for (var s = this.getDOMNode(), u = s; u.parentNode;) u = u.parentNode; + for (var c = u.querySelectorAll("input[name=" + JSON.stringify("" + a) + '][type="radio"]'), d = 0, h = c.length; h > d; d++) { + var m = c[d]; + if (m !== s && m.form === s.form) { + var y = l.getID(m); + "production" !== n.env.NODE_ENV ? f(y, "ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported.") : f(y); + var g = v[y]; + "production" !== n.env.NODE_ENV ? f(g, "ReactDOMInput: Unknown radio button ID %s.", y) : f(g), p.asap(r, g) + } + } + } + return t + } + }); + t.exports = m + }).call(this, e("_process")) + }, { + "./AutoFocusMixin": 8, + "./DOMPropertyOperations": 18, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./invariant": 157, + _process: 2 + }], + 60: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./ReactBrowserComponentMixin"), + o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./warning"), + s = a.createFactory("option"), + u = o.createClass({ + displayName: "ReactDOMOption", + tagName: "OPTION", + mixins: [r], + componentWillMount: function() { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? i(null == this.props.selected, "Use the `defaultValue` or `value` props on instead of setting `selected` on .") : null); + + }, + render: function() { + return s(this.props, this.props.children) + } + }); + t.exports = u + }).call(this, e("_process")) + }, { + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./warning": 178, + _process: 2 + }], + 61: [function(e, t) { + "use strict"; + + function n() { + if (this._pendingUpdate) { + this._pendingUpdate = !1; + var e = i.getValue(this); + null != e && this.isMounted() && o(this, e) + } + } + + function r(e, t) { + if (null == e[t]) return null; + if (e.multiple) { + if (!Array.isArray(e[t])) return new Error("The `" + t + "` prop supplied to must be an array if `multiple` is true.") + } else if (Array.isArray(e[t])) return new Error("The `" + t + "` prop supplied to must be a scalar value if `multiple` is false.") + } + + function o(e, t) { + var n, r, o, a = e.getDOMNode().options; + if (e.props.multiple) { + for (n = {}, r = 0, o = t.length; o > r; r++) n["" + t[r]] = !0; + for (r = 0, o = a.length; o > r; r++) { + var i = n.hasOwnProperty(a[r].value); + a[r].selected !== i && (a[r].selected = i) + } + } else { + for (n = "" + t, r = 0, o = a.length; o > r; r++) + if (a[r].value === n) return void(a[r].selected = !0); + a.length && (a[0].selected = !0) + } + } + var a = e("./AutoFocusMixin"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactUpdates"), + p = e("./Object.assign"), + d = c.createFactory("select"), + f = u.createClass({ + displayName: "ReactDOMSelect", + tagName: "SELECT", + mixins: [a, i.Mixin, s], + propTypes: { + defaultValue: r, + value: r + }, + render: function() { + var e = p({}, this.props); + return e.onChange = this._handleChange, e.value = null, d(e, this.props.children) + }, + componentWillMount: function() { + this._pendingUpdate = !1 + }, + componentDidMount: function() { + var e = i.getValue(this); + null != e ? o(this, e) : null != this.props.defaultValue && o(this, this.props.defaultValue) + }, + componentDidUpdate: function(e) { + var t = i.getValue(this); + null != t ? (this._pendingUpdate = !1, o(this, t)) : !e.multiple != !this.props.multiple && (null != this.props.defaultValue ? o(this, this.props.defaultValue) : o(this, this.props.multiple ? [] : "")) + }, + _handleChange: function(e) { + var t, r = i.getOnChange(this); + return r && (t = r.call(this, e)), this._pendingUpdate = !0, l.asap(n, this), t + } + }); + t.exports = f + }, { + "./AutoFocusMixin": 8, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactUpdates": 106 + }], + 62: [function(e, t) { + "use strict"; + + function n(e, t, n, r) { + return e === n && t === r + } + + function r(e) { + var t = document.selection, + n = t.createRange(), + r = n.text.length, + o = n.duplicate(); + o.moveToElementText(e), o.setEndPoint("EndToStart", n); + var a = o.text.length, + i = a + r; + return { + start: a, + end: i + } + } + + function o(e) { + var t = window.getSelection && window.getSelection(); + if (!t || 0 === t.rangeCount) return null; + var r = t.anchorNode, + o = t.anchorOffset, + a = t.focusNode, + i = t.focusOffset, + s = t.getRangeAt(0), + u = n(t.anchorNode, t.anchorOffset, t.focusNode, t.focusOffset), + c = u ? 0 : s.toString().length, + l = s.cloneRange(); + l.selectNodeContents(e), l.setEnd(s.startContainer, s.startOffset); + var p = n(l.startContainer, l.startOffset, l.endContainer, l.endOffset), + d = p ? 0 : l.toString().length, + f = d + c, + h = document.createRange(); + h.setStart(r, o), h.setEnd(a, i); + var v = h.collapsed; + return { + start: v ? f : d, + end: v ? d : f + } + } + + function a(e, t) { + var n, r, o = document.selection.createRange().duplicate(); + "undefined" == typeof t.end ? (n = t.start, r = n) : t.start > t.end ? (n = t.end, r = t.start) : (n = t.start, r = t.end), o.moveToElementText(e), o.moveStart("character", n), o.setEndPoint("EndToStart", o), o.moveEnd("character", r - n), o.select() + } + + function i(e, t) { + if (window.getSelection) { + var n = window.getSelection(), + r = e[c()].length, + o = Math.min(t.start, r), + a = "undefined" == typeof t.end ? o : Math.min(t.end, r); + if (!n.extend && o > a) { + var i = a; + a = o, o = i + } + var s = u(e, o), + l = u(e, a); + if (s && l) { + var p = document.createRange(); + p.setStart(s.node, s.offset), n.removeAllRanges(), o > a ? (n.addRange(p), n.extend(l.node, l.offset)) : (p.setEnd(l.node, l.offset), n.addRange(p)) + } + } + } + var s = e("./ExecutionEnvironment"), + u = e("./getNodeForCharacterOffset"), + c = e("./getTextContentAccessor"), + l = s.canUseDOM && "selection" in document && !("getSelection" in window), + p = { + getOffsets: l ? r : o, + setOffsets: l ? a : i + }; + t.exports = p + }, { + "./ExecutionEnvironment": 28, + "./getNodeForCharacterOffset": 150, + "./getTextContentAccessor": 152 + }], + 63: [function(e, t) { + "use strict"; + var n = e("./DOMPropertyOperations"), + r = e("./ReactComponentBrowserEnvironment"), + o = e("./ReactDOMComponent"), + a = e("./Object.assign"), + i = e("./escapeTextContentForBrowser"), + s = function() {}; + a(s.prototype, { + construct: function(e) { + this._currentElement = e, this._stringText = "" + e, this._rootNodeID = null, this._mountIndex = 0 + }, + mountComponent: function(e, t) { + this._rootNodeID = e; + var r = i(this._stringText); + return t.renderToStaticMarkup ? r : "" + r + "" + }, + receiveComponent: function(e) { + if (e !== this._currentElement) { + this._currentElement = e; + var t = "" + e; + t !== this._stringText && (this._stringText = t, o.BackendIDOperations.updateTextContentByID(this._rootNodeID, t)) + } + }, + unmountComponent: function() { + r.unmountIDFromEnvironment(this._rootNodeID) + } + }), t.exports = s + }, { + "./DOMPropertyOperations": 18, + "./Object.assign": 35, + "./ReactComponentBrowserEnvironment": 46, + "./ReactDOMComponent": 54, + "./escapeTextContentForBrowser": 138 + }], + 64: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + this.isMounted() && this.forceUpdate() + } + var o = e("./AutoFocusMixin"), + a = e("./DOMPropertyOperations"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactUpdates"), + p = e("./Object.assign"), + d = e("./invariant"), + f = e("./warning"), + h = c.createFactory("textarea"), + v = u.createClass({ + displayName: "ReactDOMTextarea", + tagName: "TEXTAREA", + mixins: [o, i.Mixin, s], + getInitialState: function() { + var e = this.props.defaultValue, + t = this.props.children; + null != t && ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? f(!1, "Use the `defaultValue` or `value` props instead of setting children on .") : null), "production" !== n.env.NODE_ENV ? d(null == e, "If you supply `defaultValue` on a , do not pass children.") : d(null == e), Array.isArray(t) && ("production" !== n.env.NODE_ENV ? d(t.length <= 1, " can only have at most one child.") : d(t.length <= 1), t = t[0]), e = "" + t), null == e && (e = ""); + var r = i.getValue(this); + return { + initialValue: "" + (null != r ? r : e) + } + }, + render: function() { + var e = p({}, this.props); + return "production" !== n.env.NODE_ENV ? d(null == e.dangerouslySetInnerHTML, "`dangerouslySetInnerHTML` does not make sense on .") : d(null == e.dangerouslySetInnerHTML), e.defaultValue = null, e.value = null, e.onChange = this._handleChange, h(e, this.state.initialValue) + }, + componentDidUpdate: function() { + var e = i.getValue(this); + if (null != e) { + var t = this.getDOMNode(); + a.setValueForProperty(t, "value", "" + e) + } + }, + _handleChange: function(e) { + var t, n = i.getOnChange(this); + return n && (t = n.call(this, e)), l.asap(r, this), t + } + }); + t.exports = v + }).call(this, e("_process")) + }, { + "./AutoFocusMixin": 8, + "./DOMPropertyOperations": 18, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactUpdates": 106, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 65: [function(e, t) { + "use strict"; + + function n() { + this.reinitializeTransaction() + } + var r = e("./ReactUpdates"), + o = e("./Transaction"), + a = e("./Object.assign"), + i = e("./emptyFunction"), + s = { + initialize: i, + close: function() { + p.isBatchingUpdates = !1 + } + }, + u = { + initialize: i, + close: r.flushBatchedUpdates.bind(r) + }, + c = [u, s]; + a(n.prototype, o.Mixin, { + getTransactionWrappers: function() { + return c + } + }); + var l = new n, + p = { + isBatchingUpdates: !1, + batchedUpdates: function(e, t, n, r, o) { + var a = p.isBatchingUpdates; + p.isBatchingUpdates = !0, a ? e(t, n, r, o) : l.perform(e, null, t, n, r, o) + } + }; + t.exports = p + }, { + "./Object.assign": 35, + "./ReactUpdates": 106, + "./Transaction": 123, + "./emptyFunction": 136 + }], + 66: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return h.createClass({ + tagName: e.toUpperCase(), + render: function() { + return new M(e, null, null, null, null, this.props) + } + }) + } + + function o() { + if (T.EventEmitter.injectReactEventListener(x), T.EventPluginHub.injectEventPluginOrder(u), T.EventPluginHub.injectInstanceHandle(P), T.EventPluginHub.injectMount(I), T.EventPluginHub.injectEventPluginsByName({ + SimpleEventPlugin: V, + EnterLeaveEventPlugin: c, + ChangeEventPlugin: i, + MobileSafariClickEventPlugin: d, + SelectEventPlugin: k, + BeforeInputEventPlugin: a + }), T.NativeComponent.injectGenericComponentClass(y), T.NativeComponent.injectTextComponentClass(w), T.NativeComponent.injectAutoWrapper(r), T.Class.injectMixin(f), T.NativeComponent.injectComponentClasses({ + button: g, + form: E, + iframe: _, + img: C, + input: N, + option: O, + select: R, + textarea: D, + html: L("html"), + head: L("head"), + body: L("body") + }), T.DOMProperty.injectDOMPropertyConfig(p), T.DOMProperty.injectDOMPropertyConfig(U), T.EmptyComponent.injectEmptyComponent("noscript"), T.Updates.injectReconcileTransaction(S), T.Updates.injectBatchingStrategy(m), T.RootIndex.injectCreateReactRootIndex(l.canUseDOM ? s.createReactRootIndex : A.createReactRootIndex), T.Component.injectEnvironment(v), T.DOMComponent.injectIDOperations(b), "production" !== n.env.NODE_ENV) { + var t = l.canUseDOM && window.location.href || ""; + if (/[?&]react_perf\b/.test(t)) { + var o = e("./ReactDefaultPerf"); + o.start() + } + } + } + var a = e("./BeforeInputEventPlugin"), + i = e("./ChangeEventPlugin"), + s = e("./ClientReactRootIndex"), + u = e("./DefaultEventPluginOrder"), + c = e("./EnterLeaveEventPlugin"), + l = e("./ExecutionEnvironment"), + p = e("./HTMLDOMPropertyConfig"), + d = e("./MobileSafariClickEventPlugin"), + f = e("./ReactBrowserComponentMixin"), + h = e("./ReactClass"), + v = e("./ReactComponentBrowserEnvironment"), + m = e("./ReactDefaultBatchingStrategy"), + y = e("./ReactDOMComponent"), + g = e("./ReactDOMButton"), + E = e("./ReactDOMForm"), + C = e("./ReactDOMImg"), + b = e("./ReactDOMIDOperations"), + _ = e("./ReactDOMIframe"), + N = e("./ReactDOMInput"), + O = e("./ReactDOMOption"), + R = e("./ReactDOMSelect"), + D = e("./ReactDOMTextarea"), + w = e("./ReactDOMTextComponent"), + M = e("./ReactElement"), + x = e("./ReactEventListener"), + T = e("./ReactInjection"), + P = e("./ReactInstanceHandles"), + I = e("./ReactMount"), + S = e("./ReactReconcileTransaction"), + k = e("./SelectEventPlugin"), + A = e("./ServerReactRootIndex"), + V = e("./SimpleEventPlugin"), + U = e("./SVGDOMPropertyConfig"), + L = e("./createFullPageComponent"); + t.exports = { + inject: o + } + }).call(this, e("_process")) + }, { + "./BeforeInputEventPlugin": 9, + "./ChangeEventPlugin": 14, + "./ClientReactRootIndex": 15, + "./DefaultEventPluginOrder": 20, + "./EnterLeaveEventPlugin": 21, + "./ExecutionEnvironment": 28, + "./HTMLDOMPropertyConfig": 30, + "./MobileSafariClickEventPlugin": 34, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactComponentBrowserEnvironment": 46, + "./ReactDOMButton": 53, + "./ReactDOMComponent": 54, + "./ReactDOMForm": 55, + "./ReactDOMIDOperations": 56, + "./ReactDOMIframe": 57, + "./ReactDOMImg": 58, + "./ReactDOMInput": 59, + "./ReactDOMOption": 60, + "./ReactDOMSelect": 61, + "./ReactDOMTextComponent": 63, + "./ReactDOMTextarea": 64, + "./ReactDefaultBatchingStrategy": 65, + "./ReactDefaultPerf": 67, + "./ReactElement": 69, + "./ReactEventListener": 74, + "./ReactInjection": 76, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactReconcileTransaction": 94, + "./SVGDOMPropertyConfig": 108, + "./SelectEventPlugin": 109, + "./ServerReactRootIndex": 110, + "./SimpleEventPlugin": 111, + "./createFullPageComponent": 132, + _process: 2 + }], + 67: [function(e, t) { + "use strict"; + + function n(e) { + return Math.floor(100 * e) / 100 + } + + function r(e, t, n) { + e[t] = (e[t] || 0) + n + } + var o = e("./DOMProperty"), + a = e("./ReactDefaultPerfAnalysis"), + i = e("./ReactMount"), + s = e("./ReactPerf"), + u = e("./performanceNow"), + c = { + _allMeasurements: [], + _mountStack: [0], + _injected: !1, + start: function() { + c._injected || s.injection.injectMeasure(c.measure), c._allMeasurements.length = 0, s.enableMeasure = !0 + }, + stop: function() { + s.enableMeasure = !1 + }, + getLastMeasurements: function() { + return c._allMeasurements + }, + printExclusive: function(e) { + e = e || c._allMeasurements; + var t = a.getExclusiveSummary(e); + console.table(t.map(function(e) { + return { + "Component class name": e.componentName, + "Total inclusive time (ms)": n(e.inclusive), + "Exclusive mount time (ms)": n(e.exclusive), + "Exclusive render time (ms)": n(e.render), + "Mount time per instance (ms)": n(e.exclusive / e.count), + "Render time per instance (ms)": n(e.render / e.count), + Instances: e.count + } + })) + }, + printInclusive: function(e) { + e = e || c._allMeasurements; + var t = a.getInclusiveSummary(e); + console.table(t.map(function(e) { + return { + "Owner > component": e.componentName, + "Inclusive time (ms)": n(e.time), + Instances: e.count + } + })), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + getMeasurementsSummaryMap: function(e) { + var t = a.getInclusiveSummary(e, !0); + return t.map(function(e) { + return { + "Owner > component": e.componentName, + "Wasted time (ms)": e.time, + Instances: e.count + } + }) + }, + printWasted: function(e) { + e = e || c._allMeasurements, console.table(c.getMeasurementsSummaryMap(e)), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + printDOM: function(e) { + e = e || c._allMeasurements; + var t = a.getDOMSummary(e); + console.table(t.map(function(e) { + var t = {}; + return t[o.ID_ATTRIBUTE_NAME] = e.id, t.type = e.type, t.args = JSON.stringify(e.args), t + })), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + _recordWrite: function(e, t, n, r) { + var o = c._allMeasurements[c._allMeasurements.length - 1].writes; + o[e] = o[e] || [], o[e].push({ + type: t, + time: n, + args: r + }) + }, + measure: function(e, t, n) { + return function() { + for (var o = [], a = 0, s = arguments.length; s > a; a++) o.push(arguments[a]); + var l, p, d; + if ("_renderNewRootComponent" === t || "flushBatchedUpdates" === t) return c._allMeasurements.push({ + exclusive: {}, + inclusive: {}, + render: {}, + counts: {}, + writes: {}, + displayNames: {}, + totalTime: 0 + }), d = u(), p = n.apply(this, o), c._allMeasurements[c._allMeasurements.length - 1].totalTime = u() - d, p; + if ("_mountImageIntoNode" === t || "ReactDOMIDOperations" === e) { + if (d = u(), p = n.apply(this, o), l = u() - d, "_mountImageIntoNode" === t) { + var f = i.getID(o[1]); + c._recordWrite(f, t, l, o[0]) + } else "dangerouslyProcessChildrenUpdates" === t ? o[0].forEach(function(e) { + var t = {}; + null !== e.fromIndex && (t.fromIndex = e.fromIndex), null !== e.toIndex && (t.toIndex = e.toIndex), null !== e.textContent && (t.textContent = e.textContent), null !== e.markupIndex && (t.markup = o[1][e.markupIndex]), c._recordWrite(e.parentID, e.type, l, t) + }) : c._recordWrite(o[0], t, l, Array.prototype.slice.call(o, 1)); + return p + } + if ("ReactCompositeComponent" !== e || "mountComponent" !== t && "updateComponent" !== t && "_renderValidatedComponent" !== t) return n.apply(this, o); + if ("string" == typeof this._currentElement.type) return n.apply(this, o); + var h = "mountComponent" === t ? o[0] : this._rootNodeID, + v = "_renderValidatedComponent" === t, + m = "mountComponent" === t, + y = c._mountStack, + g = c._allMeasurements[c._allMeasurements.length - 1]; + if (v ? r(g.counts, h, 1) : m && y.push(0), d = u(), p = n.apply(this, o), l = u() - d, v) r(g.render, h, l); + else if (m) { + var E = y.pop(); + y[y.length - 1] += l, r(g.exclusive, h, l - E), r(g.inclusive, h, l) + } else r(g.inclusive, h, l); + return g.displayNames[h] = { + current: this.getName(), + owner: this._currentElement._owner ? this._currentElement._owner.getName() : "" + }, p + } + } + }; + t.exports = c + }, { + "./DOMProperty": 17, + "./ReactDefaultPerfAnalysis": 68, + "./ReactMount": 83, + "./ReactPerf": 88, + "./performanceNow": 169 + }], + 68: [function(e, t) { + function n(e) { + for (var t = 0, n = 0; n < e.length; n++) { + var r = e[n]; + t += r.totalTime + } + return t + } + + function r(e) { + for (var t = [], n = 0; n < e.length; n++) { + var r, o = e[n]; + for (r in o.writes) o.writes[r].forEach(function(e) { + t.push({ + id: r, + type: c[e.type] || e.type, + args: e.args + }) + }) + } + return t + } + + function o(e) { + for (var t, n = {}, r = 0; r < e.length; r++) { + var o = e[r], + a = s({}, o.exclusive, o.inclusive); + for (var i in a) t = o.displayNames[i].current, n[t] = n[t] || { + componentName: t, + inclusive: 0, + exclusive: 0, + render: 0, + count: 0 + }, o.render[i] && (n[t].render += o.render[i]), o.exclusive[i] && (n[t].exclusive += o.exclusive[i]), o.inclusive[i] && (n[t].inclusive += o.inclusive[i]), o.counts[i] && (n[t].count += o.counts[i]) + } + var c = []; + for (t in n) n[t].exclusive >= u && c.push(n[t]); + return c.sort(function(e, t) { + return t.exclusive - e.exclusive + }), c + } + + function a(e, t) { + for (var n, r = {}, o = 0; o < e.length; o++) { + var a, c = e[o], + l = s({}, c.exclusive, c.inclusive); + t && (a = i(c)); + for (var p in l) + if (!t || a[p]) { + var d = c.displayNames[p]; + n = d.owner + " > " + d.current, r[n] = r[n] || { + componentName: n, + time: 0, + count: 0 + }, c.inclusive[p] && (r[n].time += c.inclusive[p]), c.counts[p] && (r[n].count += c.counts[p]) + } + } + var f = []; + for (n in r) r[n].time >= u && f.push(r[n]); + return f.sort(function(e, t) { + return t.time - e.time + }), f + } + + function i(e) { + var t = {}, + n = Object.keys(e.writes), + r = s({}, e.exclusive, e.inclusive); + for (var o in r) { + for (var a = !1, i = 0; i < n.length; i++) + if (0 === n[i].indexOf(o)) { + a = !0; + break + }! a && e.counts[o] > 0 && (t[o] = !0) + } + return t + } + var s = e("./Object.assign"), + u = 1.2, + c = { + _mountImageIntoNode: "set innerHTML", + INSERT_MARKUP: "set innerHTML", + MOVE_EXISTING: "move", + REMOVE_NODE: "remove", + TEXT_CONTENT: "set textContent", + updatePropertyByID: "update attribute", + deletePropertyByID: "delete attribute", + updateStylesByID: "update styles", + updateInnerHTMLByID: "set innerHTML", + dangerouslyReplaceNodeWithMarkupByID: "replace" + }, + l = { + getExclusiveSummary: o, + getInclusiveSummary: a, + getDOMSummary: r, + getTotalTime: n + }; + t.exports = l + }, { + "./Object.assign": 35 + }], + 69: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + Object.defineProperty(e, t, { + configurable: !1, + enumerable: !0, + get: function() { + return this._store ? this._store[t] : null + }, + set: function(e) { + "production" !== n.env.NODE_ENV ? u(!1, "Don't set the %s property of the React element. Instead, specify the correct value when initially creating the element.", t) : null, this._store[t] = e + } + }) + } + + function o(e) { + try { + var t = { + props: !0 + }; + for (var n in t) r(e, n); + l = !0 + } catch (o) {} + } + var a = e("./ReactContext"), + i = e("./ReactCurrentOwner"), + s = e("./Object.assign"), + u = e("./warning"), + c = { + key: !0, + ref: !0 + }, + l = !1, + p = function(e, t, r, o, a, i) { + if (this.type = e, this.key = t, this.ref = r, this._owner = o, this._context = a, "production" !== n.env.NODE_ENV) { + this._store = { + props: i, + originalProps: s({}, i) + }; + try { + Object.defineProperty(this._store, "validated", { + configurable: !1, + enumerable: !1, + writable: !0 + }) + } catch (u) {} + if (this._store.validated = !1, l) return void Object.freeze(this) + } + this.props = i + }; + p.prototype = { + _isReactElement: !0 + }, "production" !== n.env.NODE_ENV && o(p.prototype), p.createElement = function(e, t, n) { + var r, o = {}, + s = null, + u = null; + if (null != t) { + u = void 0 === t.ref ? null : t.ref, s = void 0 === t.key ? null : "" + t.key; + for (r in t) t.hasOwnProperty(r) && !c.hasOwnProperty(r) && (o[r] = t[r]) + } + var l = arguments.length - 2; + if (1 === l) o.children = n; + else if (l > 1) { + for (var d = Array(l), f = 0; l > f; f++) d[f] = arguments[f + 2]; + o.children = d + } + if (e && e.defaultProps) { + var h = e.defaultProps; + for (r in h) "undefined" == typeof o[r] && (o[r] = h[r]) + } + return new p(e, s, u, i.current, a.current, o) + }, p.createFactory = function(e) { + var t = p.createElement.bind(null, e); + return t.type = e, t + }, p.cloneAndReplaceProps = function(e, t) { + var r = new p(e.type, e.key, e.ref, e._owner, e._context, t); + return "production" !== n.env.NODE_ENV && (r._store.validated = e._store.validated), r + }, p.cloneElement = function(e, t, n) { + var r, o = s({}, e.props), + a = e.key, + u = e.ref, + l = e._owner; + if (null != t) { + void 0 !== t.ref && (u = t.ref, l = i.current), void 0 !== t.key && (a = "" + t.key); + for (r in t) t.hasOwnProperty(r) && !c.hasOwnProperty(r) && (o[r] = t[r]) + } + var d = arguments.length - 2; + if (1 === d) o.children = n; + else if (d > 1) { + for (var f = Array(d), h = 0; d > h; h++) f[h] = arguments[h + 2]; + o.children = f + } + return new p(e.type, a, u, l, e._context, o) + }, p.isValidElement = function(e) { + var t = !(!e || !e._isReactElement); + return t + }, t.exports = p + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./warning": 178, + _process: 2 + }], + 70: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + if (E.current) { + var e = E.current.getName(); + if (e) return " Check the render method of `" + e + "`." + } + return "" + } + + function o(e) { + var t = e && e.getPublicInstance(); + if (!t) return void 0; + var n = t.constructor; + return n ? n.displayName || n.name || void 0 : void 0 + } + + function a() { + var e = E.current; + return e && o(e) || void 0 + } + + function i(e, t) { + e._store.validated || null != e.key || (e._store.validated = !0, u('Each child in an array or iterator should have a unique "key" prop.', e, t)) + } + + function s(e, t, n) { + D.test(e) && u("Child objects should have non-numeric keys so ordering is preserved.", t, n) + } + + function u(e, t, r) { + var i = a(), + s = "string" == typeof r ? r : r.displayName || r.name, + u = i || s, + c = O[e] || (O[e] = {}); + if (!c.hasOwnProperty(u)) { + c[u] = !0; + var l = i ? " Check the render method of " + i + "." : s ? " Check the React.render call using <" + s + ">." : "", + p = ""; + if (t && t._owner && t._owner !== E.current) { + var d = o(t._owner); + p = " It was passed a child from " + d + "." + } + "production" !== n.env.NODE_ENV ? N(!1, e + "%s%s See http://fb.me/react-warning-keys for more information.", l, p) : null + } + } + + function c(e, t) { + if (Array.isArray(e)) + for (var n = 0; n < e.length; n++) { + var r = e[n]; + v.isValidElement(r) && i(r, t) + } else if (v.isValidElement(e)) e._store.validated = !0; + else if (e) { + var o = b(e); + if (o) { + if (o !== e.entries) + for (var a, u = o.call(e); !(a = u.next()).done;) v.isValidElement(a.value) && i(a.value, t) + } else if ("object" == typeof e) { + var c = m.extractIfFragment(e); + for (var l in c) c.hasOwnProperty(l) && s(l, c[l], t) + } + } + } + + function l(e, t, o, a) { + for (var i in t) + if (t.hasOwnProperty(i)) { + var s; + try { + "production" !== n.env.NODE_ENV ? _("function" == typeof t[i], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", e || "React class", g[a], i) : _("function" == typeof t[i]), s = t[i](o, i, e, a) + } catch (u) { + s = u + } + if (s instanceof Error && !(s.message in R)) { + R[s.message] = !0; + var c = r(this); + "production" !== n.env.NODE_ENV ? N(!1, "Failed propType: %s%s", s.message, c) : null + } + } + } + + function p(e, t) { + var r = t.type, + o = "string" == typeof r ? r : r.displayName, + a = t._owner ? t._owner.getPublicInstance().constructor.displayName : null, + i = e + "|" + o + "|" + a; + if (!w.hasOwnProperty(i)) { + w[i] = !0; + var s = ""; + o && (s = " <" + o + " />"); + var u = ""; + a && (u = " The element was created by " + a + "."), "production" !== n.env.NODE_ENV ? N(!1, "Don't set .props.%s of the React component%s. Instead, specify the correct value when initially creating the element.%s", e, s, u) : null + } + } + + function d(e, t) { + return e !== e ? t !== t : 0 === e && 0 === t ? 1 / e === 1 / t : e === t + } + + function f(e) { + if (e._store) { + var t = e._store.originalProps, + n = e.props; + for (var r in n) n.hasOwnProperty(r) && (t.hasOwnProperty(r) && d(t[r], n[r]) || (p(r, e), t[r] = n[r])) + } + } + + function h(e) { + if (null != e.type) { + var t = C.getComponentClassForElement(e), + r = t.displayName || t.name; + t.propTypes && l(r, t.propTypes, e.props, y.prop), "function" == typeof t.getDefaultProps && ("production" !== n.env.NODE_ENV ? N(t.getDefaultProps.isReactClassApproved, "getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.") : null) + } + } + var v = e("./ReactElement"), + m = e("./ReactFragment"), + y = e("./ReactPropTypeLocations"), + g = e("./ReactPropTypeLocationNames"), + E = e("./ReactCurrentOwner"), + C = e("./ReactNativeComponent"), + b = e("./getIteratorFn"), + _ = e("./invariant"), + N = e("./warning"), + O = {}, + R = {}, + D = /^\d+$/, + w = {}, + M = { + checkAndWarnForMutatedProps: f, + createElement: function(e) { + "production" !== n.env.NODE_ENV ? N(null != e, "React.createElement: type should not be null or undefined. It should be a string (for DOM elements) or a ReactClass (for composite components).") : null; + var t = v.createElement.apply(this, arguments); + if (null == t) return t; + for (var r = 2; r < arguments.length; r++) c(arguments[r], e); + return h(t), t + }, + createFactory: function(e) { + var t = M.createElement.bind(null, e); + if (t.type = e, "production" !== n.env.NODE_ENV) try { + Object.defineProperty(t, "type", { + enumerable: !1, + get: function() { + return "production" !== n.env.NODE_ENV ? N(!1, "Factory.type is deprecated. Access the class directly before passing it to createFactory.") : null, Object.defineProperty(this, "type", { + value: e + }), e + } + }) + } catch (r) {} + return t + }, + cloneElement: function() { + for (var e = v.cloneElement.apply(this, arguments), t = 2; t < arguments.length; t++) c(arguments[t], e.type); + return h(e), e + } + }; + t.exports = M + }).call(this, e("_process")) + }, { + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactFragment": 75, + "./ReactNativeComponent": 86, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./getIteratorFn": 148, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 71: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + l[e] = !0 + } + + function o(e) { + delete l[e] + } + + function a(e) { + return !!l[e] + } + var i, s = e("./ReactElement"), + u = e("./ReactInstanceMap"), + c = e("./invariant"), + l = {}, + p = { + injectEmptyComponent: function(e) { + i = s.createFactory(e) + } + }, + d = function() {}; + d.prototype.componentDidMount = function() { + var e = u.get(this); + e && r(e._rootNodeID) + }, d.prototype.componentWillUnmount = function() { + var e = u.get(this); + e && o(e._rootNodeID) + }, d.prototype.render = function() { + return "production" !== n.env.NODE_ENV ? c(i, "Trying to return null from a render, but no null placeholder component was injected.") : c(i), i() + }; + var f = s.createElement(d), + h = { + emptyElement: f, + injection: p, + isNullComponentID: a + }; + t.exports = h + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactInstanceMap": 79, + "./invariant": 157, + _process: 2 + }], + 72: [function(e, t) { + "use strict"; + var n = { + guard: function(e) { + return e + } + }; + t.exports = n + }, {}], + 73: [function(e, t) { + "use strict"; + + function n(e) { + r.enqueueEvents(e), r.processEventQueue() + } + var r = e("./EventPluginHub"), + o = { + handleTopLevel: function(e, t, o, a) { + var i = r.extractEvents(e, t, o, a); + n(i) + } + }; + t.exports = o + }, { + "./EventPluginHub": 24 + }], + 74: [function(e, t) { + "use strict"; + + function n(e) { + var t = l.getID(e), + n = c.getReactRootIDFromNodeID(t), + r = l.findReactContainerForID(n), + o = l.getFirstReactDOM(r); + return o + } + + function r(e, t) { + this.topLevelType = e, this.nativeEvent = t, this.ancestors = [] + } + + function o(e) { + for (var t = l.getFirstReactDOM(f(e.nativeEvent)) || window, r = t; r;) e.ancestors.push(r), r = n(r); + for (var o = 0, a = e.ancestors.length; a > o; o++) { + t = e.ancestors[o]; + var i = l.getID(t) || ""; + v._handleTopLevel(e.topLevelType, t, i, e.nativeEvent) + } + } + + function a(e) { + var t = h(window); + e(t) + } + var i = e("./EventListener"), + s = e("./ExecutionEnvironment"), + u = e("./PooledClass"), + c = e("./ReactInstanceHandles"), + l = e("./ReactMount"), + p = e("./ReactUpdates"), + d = e("./Object.assign"), + f = e("./getEventTarget"), + h = e("./getUnboundedScrollPosition"); + d(r.prototype, { + destructor: function() { + this.topLevelType = null, this.nativeEvent = null, this.ancestors.length = 0 + } + }), u.addPoolingTo(r, u.twoArgumentPooler); + var v = { + _enabled: !0, + _handleTopLevel: null, + WINDOW_HANDLE: s.canUseDOM ? window : null, + setHandleTopLevel: function(e) { + v._handleTopLevel = e + }, + setEnabled: function(e) { + v._enabled = !!e + }, + isEnabled: function() { + return v._enabled + }, + trapBubbledEvent: function(e, t, n) { + var r = n; + return r ? i.listen(r, t, v.dispatchEvent.bind(null, e)) : null + }, + trapCapturedEvent: function(e, t, n) { + var r = n; + return r ? i.capture(r, t, v.dispatchEvent.bind(null, e)) : null + }, + monitorScrollValue: function(e) { + var t = a.bind(null, e); + i.listen(window, "scroll", t) + }, + dispatchEvent: function(e, t) { + if (v._enabled) { + var n = r.getPooled(e, t); + try { + p.batchedUpdates(o, n) + } finally { + r.release(n) + } + } + } + }; + t.exports = v + }, { + "./EventListener": 23, + "./ExecutionEnvironment": 28, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./getEventTarget": 147, + "./getUnboundedScrollPosition": 153 + }], + 75: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./ReactElement"), + o = e("./warning"); + if ("production" !== n.env.NODE_ENV) { + var a = "_reactFragment", + i = "_reactDidWarn", + s = !1; + try { + var u = function() { + return 1 + }; + Object.defineProperty({}, a, { + enumerable: !1, + value: !0 + }), Object.defineProperty({}, "key", { + enumerable: !0, + get: u + }), s = !0 + } catch (c) {} + var l = function(e, t) { + Object.defineProperty(e, t, { + enumerable: !0, + get: function() { + return "production" !== n.env.NODE_ENV ? o(this[i], "A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers.") : null, this[i] = !0, this[a][t] + }, + set: function(e) { + "production" !== n.env.NODE_ENV ? o(this[i], "A ReactFragment is an immutable opaque type. Mutating its properties is deprecated.") : null, this[i] = !0, this[a][t] = e + } + }) + }, + p = {}, + d = function(e) { + var t = ""; + for (var n in e) t += n + ":" + typeof e[n] + ","; + var r = !!p[t]; + return p[t] = !0, r + } + } + var f = { + create: function(e) { + if ("production" !== n.env.NODE_ENV) { + if ("object" != typeof e || !e || Array.isArray(e)) return "production" !== n.env.NODE_ENV ? o(!1, "React.addons.createFragment only accepts a single object.", e) : null, e; + if (r.isValidElement(e)) return "production" !== n.env.NODE_ENV ? o(!1, "React.addons.createFragment does not accept a ReactElement without a wrapper object.") : null, e; + if (s) { + var t = {}; + Object.defineProperty(t, a, { + enumerable: !1, + value: e + }), Object.defineProperty(t, i, { + writable: !0, + enumerable: !1, + value: !1 + }); + for (var u in e) l(t, u); + return Object.preventExtensions(t), t + } + } + return e + }, + extract: function(e) { + return "production" !== n.env.NODE_ENV && s ? e[a] ? e[a] : ("production" !== n.env.NODE_ENV ? o(d(e), "Any use of a keyed object should be wrapped in React.addons.createFragment(object) before being passed as a child.") : null, e) : e + }, + extractIfFragment: function(e) { + if ("production" !== n.env.NODE_ENV && s) { + if (e[a]) return e[a]; + for (var t in e) + if (e.hasOwnProperty(t) && r.isValidElement(e[t])) return f.extract(e) + } + return e + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./warning": 178, + _process: 2 + }], + 76: [function(e, t) { + "use strict"; + var n = e("./DOMProperty"), + r = e("./EventPluginHub"), + o = e("./ReactComponentEnvironment"), + a = e("./ReactClass"), + i = e("./ReactEmptyComponent"), + s = e("./ReactBrowserEventEmitter"), + u = e("./ReactNativeComponent"), + c = e("./ReactDOMComponent"), + l = e("./ReactPerf"), + p = e("./ReactRootIndex"), + d = e("./ReactUpdates"), + f = { + Component: o.injection, + Class: a.injection, + DOMComponent: c.injection, + DOMProperty: n.injection, + EmptyComponent: i.injection, + EventPluginHub: r.injection, + EventEmitter: s.injection, + NativeComponent: u.injection, + Perf: l.injection, + RootIndex: p.injection, + Updates: d.injection + }; + t.exports = f + }, { + "./DOMProperty": 17, + "./EventPluginHub": 24, + "./ReactBrowserEventEmitter": 39, + "./ReactClass": 44, + "./ReactComponentEnvironment": 47, + "./ReactDOMComponent": 54, + "./ReactEmptyComponent": 71, + "./ReactNativeComponent": 86, + "./ReactPerf": 88, + "./ReactRootIndex": 97, + "./ReactUpdates": 106 + }], + 77: [function(e, t) { + "use strict"; + + function n(e) { + return o(document.documentElement, e) + } + var r = e("./ReactDOMSelection"), + o = e("./containsNode"), + a = e("./focusNode"), + i = e("./getActiveElement"), + s = { + hasSelectionCapabilities: function(e) { + return e && ("INPUT" === e.nodeName && "text" === e.type || "TEXTAREA" === e.nodeName || "true" === e.contentEditable) + }, + getSelectionInformation: function() { + var e = i(); + return { + focusedElem: e, + selectionRange: s.hasSelectionCapabilities(e) ? s.getSelection(e) : null + } + }, + restoreSelection: function(e) { + var t = i(), + r = e.focusedElem, + o = e.selectionRange; + t !== r && n(r) && (s.hasSelectionCapabilities(r) && s.setSelection(r, o), a(r)) + }, + getSelection: function(e) { + var t; + if ("selectionStart" in e) t = { + start: e.selectionStart, + end: e.selectionEnd + }; + else if (document.selection && "INPUT" === e.nodeName) { + var n = document.selection.createRange(); + n.parentElement() === e && (t = { + start: -n.moveStart("character", -e.value.length), + end: -n.moveEnd("character", -e.value.length) + }) + } else t = r.getOffsets(e); + return t || { + start: 0, + end: 0 + } + }, + setSelection: function(e, t) { + var n = t.start, + o = t.end; + if ("undefined" == typeof o && (o = n), "selectionStart" in e) e.selectionStart = n, e.selectionEnd = Math.min(o, e.value.length); + else if (document.selection && "INPUT" === e.nodeName) { + var a = e.createTextRange(); + a.collapse(!0), a.moveStart("character", n), a.moveEnd("character", o - n), a.select() + } else r.setOffsets(e, t) + } + }; + t.exports = s + }, { + "./ReactDOMSelection": 62, + "./containsNode": 130, + "./focusNode": 141, + "./getActiveElement": 143 + }], + 78: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return f + e.toString(36) + } + + function o(e, t) { + return e.charAt(t) === f || t === e.length + } + + function a(e) { + return "" === e || e.charAt(0) === f && e.charAt(e.length - 1) !== f + } + + function i(e, t) { + return 0 === t.indexOf(e) && o(t, e.length) + } + + function s(e) { + return e ? e.substr(0, e.lastIndexOf(f)) : "" + } + + function u(e, t) { + if ("production" !== n.env.NODE_ENV ? d(a(e) && a(t), "getNextDescendantID(%s, %s): Received an invalid React DOM ID.", e, t) : d(a(e) && a(t)), "production" !== n.env.NODE_ENV ? d(i(e, t), "getNextDescendantID(...): React has made an invalid assumption about the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.", e, t) : d(i(e, t)), e === t) return e; + var r, s = e.length + h; + for (r = s; r < t.length && !o(t, r); r++); + return t.substr(0, r) + } + + function c(e, t) { + var r = Math.min(e.length, t.length); + if (0 === r) return ""; + for (var i = 0, s = 0; r >= s; s++) + if (o(e, s) && o(t, s)) i = s; + else if (e.charAt(s) !== t.charAt(s)) break; + var u = e.substr(0, i); + return "production" !== n.env.NODE_ENV ? d(a(u), "getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s", e, t, u) : d(a(u)), u + } + + function l(e, t, r, o, a, c) { + e = e || "", t = t || "", "production" !== n.env.NODE_ENV ? d(e !== t, "traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.", e) : d(e !== t); + var l = i(t, e); + "production" !== n.env.NODE_ENV ? d(l || i(e, t), "traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do not have a parent path.", e, t) : d(l || i(e, t)); + for (var p = 0, f = l ? s : u, h = e;; h = f(h, t)) { + var m; + if (a && h === e || c && h === t || (m = r(h, l, o)), m === !1 || h === t) break; + "production" !== n.env.NODE_ENV ? d(p++ < v, "traverseParentPath(%s, %s, ...): Detected an infinite loop while traversing the React DOM ID tree. This may be due to malformed IDs: %s", e, t) : d(p++ < v) + } + } + var p = e("./ReactRootIndex"), + d = e("./invariant"), + f = ".", + h = f.length, + v = 100, + m = { + createReactRootID: function() { + return r(p.createReactRootIndex()) + }, + createReactID: function(e, t) { + return e + t + }, + getReactRootIDFromNodeID: function(e) { + if (e && e.charAt(0) === f && e.length > 1) { + var t = e.indexOf(f, 1); + return t > -1 ? e.substr(0, t) : e + } + return null + }, + traverseEnterLeave: function(e, t, n, r, o) { + var a = c(e, t); + a !== e && l(e, a, n, r, !1, !0), a !== t && l(a, t, n, o, !0, !1) + }, + traverseTwoPhase: function(e, t, n) { + e && (l("", e, t, n, !0, !1), l(e, "", t, n, !1, !0)) + }, + traverseAncestors: function(e, t, n) { + l("", e, t, n, !0, !1) + }, + _getFirstCommonAncestorID: c, + _getNextDescendantID: u, + isAncestorIDOf: i, + SEPARATOR: f + }; + t.exports = m + }).call(this, e("_process")) + }, { + "./ReactRootIndex": 97, + "./invariant": 157, + _process: 2 + }], + 79: [function(e, t) { + "use strict"; + var n = { + remove: function(e) { + e._reactInternalInstance = void 0 + }, + get: function(e) { + return e._reactInternalInstance + }, + has: function(e) { + return void 0 !== e._reactInternalInstance + }, + set: function(e, t) { + e._reactInternalInstance = t + } + }; + t.exports = n + }, {}], + 80: [function(e, t) { + "use strict"; + var n = { + currentlyMountingInstance: null, + currentlyUnmountingInstance: null + }; + t.exports = n + }, {}], + 81: [function(e, t) { + "use strict"; + + function n(e, t) { + this.value = e, this.requestChange = t + } + + function r(e) { + var t = { + value: "undefined" == typeof e ? o.PropTypes.any.isRequired : e.isRequired, + requestChange: o.PropTypes.func.isRequired + }; + return o.PropTypes.shape(t) + } + var o = e("./React"); + n.PropTypes = { + link: r + }, t.exports = n + }, { + "./React": 37 + }], + 82: [function(e, t) { + "use strict"; + var n = e("./adler32"), + r = { + CHECKSUM_ATTR_NAME: "data-react-checksum", + addChecksumToMarkup: function(e) { + var t = n(e); + return e.replace(">", " " + r.CHECKSUM_ATTR_NAME + '="' + t + '">') + }, + canReuseMarkup: function(e, t) { + var o = t.getAttribute(r.CHECKSUM_ATTR_NAME); + o = o && parseInt(o, 10); + var a = n(e); + return a === o + } + }; + t.exports = r + }, { + "./adler32": 126 + }], + 83: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + for (var n = Math.min(e.length, t.length), r = 0; n > r; r++) + if (e.charAt(r) !== t.charAt(r)) return r; + return e.length === t.length ? -1 : n + } + + function o(e) { + var t = P(e); + return t && Y.getID(t) + } + + function a(e) { + var t = i(e); + if (t) + if (j.hasOwnProperty(t)) { + var r = j[t]; + r !== e && ("production" !== n.env.NODE_ENV ? S(!l(r, t), "ReactMount: Two valid but unequal nodes with the same `%s`: %s", L, t) : S(!l(r, t)), j[t] = e) + } else j[t] = e; + return t + } + + function i(e) { + return e && e.getAttribute && e.getAttribute(L) || "" + } + + function s(e, t) { + var n = i(e); + n !== t && delete j[n], e.setAttribute(L, t), j[t] = e + } + + function u(e) { + return j.hasOwnProperty(e) && l(j[e], e) || (j[e] = Y.findReactNodeByID(e)), j[e] + } + + function c(e) { + var t = N.get(e)._rootNodeID; + return b.isNullComponentID(t) ? null : (j.hasOwnProperty(t) && l(j[t], t) || (j[t] = Y.findReactNodeByID(t)), j[t]) + } + + function l(e, t) { + if (e) { + "production" !== n.env.NODE_ENV ? S(i(e) === t, "ReactMount: Unexpected modification of `%s`", L) : S(i(e) === t); + var r = Y.findReactContainerForID(t); + if (r && T(r, e)) return !0 + } + return !1 + } + + function p(e) { + delete j[e] + } + + function d(e) { + var t = j[e]; + return t && l(t, e) ? void(z = t) : !1 + } + + function f(e) { + z = null, _.traverseAncestors(e, d); + var t = z; + return z = null, t + } + + function h(e, t, n, r, o) { + var a = D.mountComponent(e, t, r, x); + e._isTopLevel = !0, Y._mountImageIntoNode(a, n, o) + } + + function v(e, t, n, r) { + var o = M.ReactReconcileTransaction.getPooled(); + o.perform(h, null, e, t, n, o, r), M.ReactReconcileTransaction.release(o) + } + var m = e("./DOMProperty"), + y = e("./ReactBrowserEventEmitter"), + g = e("./ReactCurrentOwner"), + E = e("./ReactElement"), + C = e("./ReactElementValidator"), + b = e("./ReactEmptyComponent"), + _ = e("./ReactInstanceHandles"), + N = e("./ReactInstanceMap"), + O = e("./ReactMarkupChecksum"), + R = e("./ReactPerf"), + D = e("./ReactReconciler"), + w = e("./ReactUpdateQueue"), + M = e("./ReactUpdates"), + x = e("./emptyObject"), + T = e("./containsNode"), + P = e("./getReactRootElementInContainer"), + I = e("./instantiateReactComponent"), + S = e("./invariant"), + k = e("./setInnerHTML"), + A = e("./shouldUpdateReactComponent"), + V = e("./warning"), + U = _.SEPARATOR, + L = m.ID_ATTRIBUTE_NAME, + j = {}, + F = 1, + B = 9, + W = {}, + H = {}; + if ("production" !== n.env.NODE_ENV) var q = {}; + var K = [], + z = null, + Y = { + _instancesByReactRootID: W, + scrollMonitor: function(e, t) { + t() + }, + _updateRootComponent: function(e, t, r, a) { + return "production" !== n.env.NODE_ENV && C.checkAndWarnForMutatedProps(t), Y.scrollMonitor(r, function() { + w.enqueueElementInternal(e, t), a && w.enqueueCallbackInternal(e, a) + }), "production" !== n.env.NODE_ENV && (q[o(r)] = P(r)), e + }, + _registerComponent: function(e, t) { + "production" !== n.env.NODE_ENV ? S(t && (t.nodeType === F || t.nodeType === B), "_registerComponent(...): Target container is not a DOM element.") : S(t && (t.nodeType === F || t.nodeType === B)), y.ensureScrollValueMonitoring(); + var r = Y.registerContainer(t); + return W[r] = e, r + }, + _renderNewRootComponent: function(e, t, r) { + "production" !== n.env.NODE_ENV ? V(null == g.current, "_renderNewRootComponent(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null; + var o = I(e, null), + a = Y._registerComponent(o, t); + return M.batchedUpdates(v, o, a, t, r), "production" !== n.env.NODE_ENV && (q[a] = P(t)), o + }, + render: function(e, t, r) { + "production" !== n.env.NODE_ENV ? S(E.isValidElement(e), "React.render(): Invalid component element.%s", "string" == typeof e ? " Instead of passing an element string, make sure to instantiate it by passing it to React.createElement." : "function" == typeof e ? " Instead of passing a component class, make sure to instantiate it by passing it to React.createElement." : null != e && void 0 !== e.props ? " This may be caused by unintentionally loading two independent copies of React." : "") : S(E.isValidElement(e)); + var a = W[o(t)]; + if (a) { + var i = a._currentElement; + if (A(i, e)) return Y._updateRootComponent(a, e, t, r).getPublicInstance(); + Y.unmountComponentAtNode(t) + } + var s = P(t), + u = s && Y.isRenderedByReact(s); + if ("production" !== n.env.NODE_ENV && (!u || s.nextSibling)) + for (var c = s; c;) { + if (Y.isRenderedByReact(c)) { + "production" !== n.env.NODE_ENV ? V(!1, "render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup.") : null; + break + } + c = c.nextSibling + } + var l = u && !a, + p = Y._renderNewRootComponent(e, t, l).getPublicInstance(); + return r && r.call(p), p + }, + constructAndRenderComponent: function(e, t, n) { + var r = E.createElement(e, t); + return Y.render(r, n) + }, + constructAndRenderComponentByID: function(e, t, r) { + var o = document.getElementById(r); + return "production" !== n.env.NODE_ENV ? S(o, 'Tried to get element with id of "%s" but it is not present on the page.', r) : S(o), Y.constructAndRenderComponent(e, t, o) + }, + registerContainer: function(e) { + var t = o(e); + return t && (t = _.getReactRootIDFromNodeID(t)), t || (t = _.createReactRootID()), H[t] = e, t + }, + unmountComponentAtNode: function(e) { + "production" !== n.env.NODE_ENV ? V(null == g.current, "unmountComponentAtNode(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null, "production" !== n.env.NODE_ENV ? S(e && (e.nodeType === F || e.nodeType === B), "unmountComponentAtNode(...): Target container is not a DOM element.") : S(e && (e.nodeType === F || e.nodeType === B)); + var t = o(e), + r = W[t]; + return r ? (Y.unmountComponentFromNode(r, e), delete W[t], delete H[t], "production" !== n.env.NODE_ENV && delete q[t], !0) : !1 + }, + unmountComponentFromNode: function(e, t) { + for (D.unmountComponent(e), t.nodeType === B && (t = t.documentElement); t.lastChild;) t.removeChild(t.lastChild) + }, + findReactContainerForID: function(e) { + var t = _.getReactRootIDFromNodeID(e), + r = H[t]; + if ("production" !== n.env.NODE_ENV) { + var o = q[t]; + if (o && o.parentNode !== r) { + "production" !== n.env.NODE_ENV ? S(i(o) === t, "ReactMount: Root element ID differed from reactRootID.") : S(i(o) === t); + var a = r.firstChild; + a && t === i(a) ? q[t] = a : "production" !== n.env.NODE_ENV ? V(!1, "ReactMount: Root element has been removed from its original container. New container:", o.parentNode) : null + } + } + return r + }, + findReactNodeByID: function(e) { + var t = Y.findReactContainerForID(e); + return Y.findComponentRoot(t, e) + }, + isRenderedByReact: function(e) { + if (1 !== e.nodeType) return !1; + var t = Y.getID(e); + return t ? t.charAt(0) === U : !1 + }, + getFirstReactDOM: function(e) { + for (var t = e; t && t.parentNode !== t;) { + if (Y.isRenderedByReact(t)) return t; + t = t.parentNode + } + return null + }, + findComponentRoot: function(e, t) { + var r = K, + o = 0, + a = f(t) || e; + for (r[0] = a.firstChild, r.length = 1; o < r.length;) { + for (var i, s = r[o++]; s;) { + var u = Y.getID(s); + u ? t === u ? i = s : _.isAncestorIDOf(u, t) && (r.length = o = 0, r.push(s.firstChild)) : r.push(s.firstChild), s = s.nextSibling + } + if (i) return r.length = 0, i + } + r.length = 0, "production" !== n.env.NODE_ENV ? S(!1, "findComponentRoot(..., %s): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID `%s`.", t, Y.getID(e)) : S(!1) + }, + _mountImageIntoNode: function(e, t, o) { + if ("production" !== n.env.NODE_ENV ? S(t && (t.nodeType === F || t.nodeType === B), "mountComponentIntoNode(...): Target container is not valid.") : S(t && (t.nodeType === F || t.nodeType === B)), o) { + var a = P(t); + if (O.canReuseMarkup(e, a)) return; + var i = a.getAttribute(O.CHECKSUM_ATTR_NAME); + a.removeAttribute(O.CHECKSUM_ATTR_NAME); + var s = a.outerHTML; + a.setAttribute(O.CHECKSUM_ATTR_NAME, i); + var u = r(e, s), + c = " (client) " + e.substring(u - 20, u + 20) + "\n (server) " + s.substring(u - 20, u + 20); + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side:\n%s", c) : S(t.nodeType !== B), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? V(!1, "React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:\n%s", c) : null) + } + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document but you didn't use server rendering. We can't do this without using server rendering due to cross-browser quirks. See React.renderToString() for server rendering.") : S(t.nodeType !== B), k(t, e) + }, + getReactRootID: o, + getID: a, + setID: s, + getNode: u, + getNodeFromInstance: c, + purgeID: p + }; + R.measureMethods(Y, "ReactMount", { + _renderNewRootComponent: "_renderNewRootComponent", + _mountImageIntoNode: "_mountImageIntoNode" + }), t.exports = Y + }).call(this, e("_process")) + }, { + "./DOMProperty": 17, + "./ReactBrowserEventEmitter": 39, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMarkupChecksum": 82, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./ReactUpdateQueue": 105, + "./ReactUpdates": 106, + "./containsNode": 130, + "./emptyObject": 137, + "./getReactRootElementInContainer": 151, + "./instantiateReactComponent": 156, + "./invariant": 157, + "./setInnerHTML": 171, + "./shouldUpdateReactComponent": 174, + "./warning": 178, + _process: 2 + }], + 84: [function(e, t) { + "use strict"; + + function n(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.INSERT_MARKUP, + markupIndex: h.push(t) - 1, + textContent: null, + fromIndex: null, + toIndex: n + }) + } + + function r(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.MOVE_EXISTING, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: n + }) + } + + function o(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.REMOVE_NODE, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: null + }) + } + + function a(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.TEXT_CONTENT, + markupIndex: null, + textContent: t, + fromIndex: null, + toIndex: null + }) + } + + function i() { + f.length && (u.processChildrenUpdates(f, h), s()) + } + + function s() { + f.length = 0, h.length = 0 + } + var u = e("./ReactComponentEnvironment"), + c = e("./ReactMultiChildUpdateTypes"), + l = e("./ReactReconciler"), + p = e("./ReactChildReconciler"), + d = 0, + f = [], + h = [], + v = { + Mixin: { + mountChildren: function(e, t, n) { + var r = p.instantiateChildren(e, t, n); + this._renderedChildren = r; + var o = [], + a = 0; + for (var i in r) + if (r.hasOwnProperty(i)) { + var s = r[i], + u = this._rootNodeID + i, + c = l.mountComponent(s, u, t, n); + s._mountIndex = a, o.push(c), a++ + } return o + }, + updateTextContent: function(e) { + d++; + var t = !0; + try { + var n = this._renderedChildren; + p.unmountChildren(n); + for (var r in n) n.hasOwnProperty(r) && this._unmountChildByName(n[r], r); + this.setTextContent(e), t = !1 + } finally { + d--, d || (t ? s() : i()) + } + }, + updateChildren: function(e, t, n) { + d++; + var r = !0; + try { + this._updateChildren(e, t, n), r = !1 + } finally { + d--, d || (r ? s() : i()) + } + }, + _updateChildren: function(e, t, n) { + var r = this._renderedChildren, + o = p.updateChildren(r, e, t, n); + if (this._renderedChildren = o, o || r) { + var a, i = 0, + s = 0; + for (a in o) + if (o.hasOwnProperty(a)) { + var u = r && r[a], + c = o[a]; + u === c ? (this.moveChild(u, s, i), i = Math.max(u._mountIndex, i), u._mountIndex = s) : (u && (i = Math.max(u._mountIndex, i), this._unmountChildByName(u, a)), this._mountChildByNameAtIndex(c, a, s, t, n)), s++ + } for (a in r) !r.hasOwnProperty(a) || o && o.hasOwnProperty(a) || this._unmountChildByName(r[a], a) + } + }, + unmountChildren: function() { + var e = this._renderedChildren; + p.unmountChildren(e), this._renderedChildren = null + }, + moveChild: function(e, t, n) { + e._mountIndex < n && r(this._rootNodeID, e._mountIndex, t) + }, + createChild: function(e, t) { + n(this._rootNodeID, t, e._mountIndex) + }, + removeChild: function(e) { + o(this._rootNodeID, e._mountIndex) + }, + setTextContent: function(e) { + a(this._rootNodeID, e) + }, + _mountChildByNameAtIndex: function(e, t, n, r, o) { + var a = this._rootNodeID + t, + i = l.mountComponent(e, a, r, o); + e._mountIndex = n, this.createChild(e, i) + }, + _unmountChildByName: function(e) { + this.removeChild(e), e._mountIndex = null + } + } + }; + t.exports = v + }, { + "./ReactChildReconciler": 42, + "./ReactComponentEnvironment": 47, + "./ReactMultiChildUpdateTypes": 85, + "./ReactReconciler": 95 + }], + 85: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + INSERT_MARKUP: null, + MOVE_EXISTING: null, + REMOVE_NODE: null, + TEXT_CONTENT: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 86: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + if ("function" == typeof e.type) return e.type; + var t = e.type, + n = p[t]; + return null == n && (p[t] = n = c(t)), n + } + + function o(e) { + return "production" !== n.env.NODE_ENV ? u(l, "There is no registered component for the tag %s", e.type) : u(l), new l(e.type, e.props) + } + + function a(e) { + return new d(e) + } + + function i(e) { + return e instanceof d + } + var s = e("./Object.assign"), + u = e("./invariant"), + c = null, + l = null, + p = {}, + d = null, + f = { + injectGenericComponentClass: function(e) { + l = e + }, + injectTextComponentClass: function(e) { + d = e + }, + injectComponentClasses: function(e) { + s(p, e) + }, + injectAutoWrapper: function(e) { + c = e + } + }, + h = { + getComponentClassForElement: r, + createInternalComponent: o, + createInstanceForText: a, + isTextComponent: i, + injection: f + }; + t.exports = h + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./invariant": 157, + _process: 2 + }], + 87: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + isValidOwner: function(e) { + return !(!e || "function" != typeof e.attachRef || "function" != typeof e.detachRef) + }, + addComponentAsRefTo: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.attachRef(t, e) + }, + removeComponentAsRefFrom: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This usually means that you're trying to remove a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.getPublicInstance().refs[t] === e.getPublicInstance() && a.detachRef(t) + } + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 88: [function(e, t) { + (function(e) { + "use strict"; + + function n(e, t, n) { + return n + } + var r = { + enableMeasure: !1, + storedMeasure: n, + measureMethods: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) + for (var a in o) o.hasOwnProperty(a) && (t[a] = r.measure(n, o[a], t[a])) + }, + measure: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) { + var a = null, + i = function() { + return r.enableMeasure ? (a || (a = r.storedMeasure(t, n, o)), a.apply(this, arguments)) : o.apply(this, arguments) + }; + return i.displayName = t + "_" + n, i + } + return o + }, + injection: { + injectMeasure: function(e) { + r.storedMeasure = e + } + } + }; + t.exports = r + }).call(this, e("_process")) + }, { + _process: 2 + }], + 89: [function(e, t) { + "use strict"; + + function n(e) { + return function(t, n, r) { + t[n] = t.hasOwnProperty(n) ? e(t[n], r) : r + } + } + + function r(e, t) { + for (var n in t) + if (t.hasOwnProperty(n)) { + var r = u[n]; + r && u.hasOwnProperty(n) ? r(e, n, t[n]) : e.hasOwnProperty(n) || (e[n] = t[n]) + } return e + } + var o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./joinClasses"), + s = n(function(e, t) { + return o({}, t, e) + }), + u = { + children: a, + className: n(i), + style: s + }, + c = { + mergeProps: function(e, t) { + return r(o({}, e), t) + } + }; + t.exports = c + }, { + "./Object.assign": 35, + "./emptyFunction": 136, + "./joinClasses": 162 + }], + 90: [function(e, t) { + (function(e) { + "use strict"; + var n = {}; + "production" !== e.env.NODE_ENV && (n = { + prop: "prop", + context: "context", + childContext: "child context" + }), t.exports = n + }).call(this, e("_process")) + }, { + _process: 2 + }], + 91: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + prop: null, + context: null, + childContext: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 92: [function(e, t) { + "use strict"; + + function n(e) { + function t(t, n, r, o, a) { + if (o = o || C, null == n[r]) { + var i = g[a]; + return t ? new Error("Required " + i + " `" + r + "` was not specified in " + ("`" + o + "`.")) : null + } + return e(n, r, o, a) + } + var n = t.bind(null, !1); + return n.isRequired = t.bind(null, !0), n + } + + function r(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if (i !== e) { + var s = g[o], + u = v(a); + return new Error("Invalid " + s + " `" + n + "` of type `" + u + "` " + ("supplied to `" + r + "`, expected `" + e + "`.")) + } + return null + } + return n(t) + } + + function o() { + return n(E.thatReturns(null)) + } + + function a(e) { + function t(t, n, r, o) { + var a = t[n]; + if (!Array.isArray(a)) { + var i = g[o], + s = h(a); + return new Error("Invalid " + i + " `" + n + "` of type " + ("`" + s + "` supplied to `" + r + "`, expected an array.")) + } + for (var u = 0; u < a.length; u++) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } + return null + } + return n(t) + } + + function i() { + function e(e, t, n, r) { + if (!m.isValidElement(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactElement.")) + } + return null + } + return n(e) + } + + function s(e) { + function t(t, n, r, o) { + if (!(t[n] instanceof e)) { + var a = g[o], + i = e.name || C; + return new Error("Invalid " + a + " `" + n + "` supplied to " + ("`" + r + "`, expected instance of `" + i + "`.")) + } + return null + } + return n(t) + } + + function u(e) { + function t(t, n, r, o) { + for (var a = t[n], i = 0; i < e.length; i++) + if (a === e[i]) return null; + var s = g[o], + u = JSON.stringify(e); + return new Error("Invalid " + s + " `" + n + "` of value `" + a + "` " + ("supplied to `" + r + "`, expected one of " + u + ".")) + } + return n(t) + } + + function c(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type " + ("`" + i + "` supplied to `" + r + "`, expected an object.")) + } + for (var u in a) + if (a.hasOwnProperty(u)) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } return null + } + return n(t) + } + + function l(e) { + function t(t, n, r, o) { + for (var a = 0; a < e.length; a++) { + var i = e[a]; + if (null == i(t, n, r, o)) return null + } + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` supplied to " + ("`" + r + "`.")) + } + return n(t) + } + + function p() { + function e(e, t, n, r) { + if (!f(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactNode.")) + } + return null + } + return n(e) + } + + function d(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type `" + i + "` " + ("supplied to `" + r + "`, expected `object`.")) + } + for (var u in e) { + var c = e[u]; + if (c) { + var l = c(a, u, r, o); + if (l) return l + } + } + return null + } + return n(t) + } + + function f(e) { + switch (typeof e) { + case "number": + case "string": + case "undefined": + return !0; + case "boolean": + return !e; + case "object": + if (Array.isArray(e)) return e.every(f); + if (null === e || m.isValidElement(e)) return !0; + e = y.extractIfFragment(e); + for (var t in e) + if (!f(e[t])) return !1; + return !0; + default: + return !1 + } + } + + function h(e) { + var t = typeof e; + return Array.isArray(e) ? "array" : e instanceof RegExp ? "object" : t + } + + function v(e) { + var t = h(e); + if ("object" === t) { + if (e instanceof Date) return "date"; + if (e instanceof RegExp) return "regexp" + } + return t + } + var m = e("./ReactElement"), + y = e("./ReactFragment"), + g = e("./ReactPropTypeLocationNames"), + E = e("./emptyFunction"), + C = "<>", + b = i(), + _ = p(), + N = { + array: r("array"), + bool: r("boolean"), + func: r("function"), + number: r("number"), + object: r("object"), + string: r("string"), + any: o(), + arrayOf: a, + element: b, + instanceOf: s, + node: _, + objectOf: c, + oneOf: u, + oneOfType: l, + shape: d + }; + t.exports = N + }, { + "./ReactElement": 69, + "./ReactFragment": 75, + "./ReactPropTypeLocationNames": 90, + "./emptyFunction": 136 + }], + 93: [function(e, t) { + "use strict"; + + function n() { + this.listenersToPut = [] + } + var r = e("./PooledClass"), + o = e("./ReactBrowserEventEmitter"), + a = e("./Object.assign"); + a(n.prototype, { + enqueuePutListener: function(e, t, n) { + this.listenersToPut.push({ + rootNodeID: e, + propKey: t, + propValue: n + }) + }, + putListeners: function() { + for (var e = 0; e < this.listenersToPut.length; e++) { + var t = this.listenersToPut[e]; + o.putListener(t.rootNodeID, t.propKey, t.propValue) + } + }, + reset: function() { + this.listenersToPut.length = 0 + }, + destructor: function() { + this.reset() + } + }), r.addPoolingTo(n), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39 + }], + 94: [function(e, t) { + "use strict"; + + function n() { + this.reinitializeTransaction(), this.renderToStaticMarkup = !1, this.reactMountReady = r.getPooled(null), this.putListenerQueue = s.getPooled() + } + var r = e("./CallbackQueue"), + o = e("./PooledClass"), + a = e("./ReactBrowserEventEmitter"), + i = e("./ReactInputSelection"), + s = e("./ReactPutListenerQueue"), + u = e("./Transaction"), + c = e("./Object.assign"), + l = { + initialize: i.getSelectionInformation, + close: i.restoreSelection + }, + p = { + initialize: function() { + var e = a.isEnabled(); + return a.setEnabled(!1), e + }, + close: function(e) { + a.setEnabled(e) + } + }, + d = { + initialize: function() { + this.reactMountReady.reset() + }, + close: function() { + this.reactMountReady.notifyAll() + } + }, + f = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: function() { + this.putListenerQueue.putListeners() + } + }, + h = [f, l, p, d], + v = { + getTransactionWrappers: function() { + return h + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + r.release(this.reactMountReady), this.reactMountReady = null, s.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + c(n.prototype, u.Mixin, v), o.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39, + "./ReactInputSelection": 77, + "./ReactPutListenerQueue": 93, + "./Transaction": 123 + }], + 95: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + o.attachRefs(this, this._currentElement) + } + var o = e("./ReactRef"), + a = e("./ReactElementValidator"), + i = { + mountComponent: function(e, t, o, i) { + var s = e.mountComponent(t, o, i); + return "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(e._currentElement), o.getReactMountReady().enqueue(r, e), s + }, + unmountComponent: function(e) { + o.detachRefs(e, e._currentElement), e.unmountComponent() + }, + receiveComponent: function(e, t, i, s) { + var u = e._currentElement; + if (t !== u || null == t._owner) { + "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(t); + var c = o.shouldUpdateRefs(u, t); + c && o.detachRefs(e, u), e.receiveComponent(t, i, s), c && i.getReactMountReady().enqueue(r, e) + } + }, + performUpdateIfNecessary: function(e, t) { + e.performUpdateIfNecessary(t) + } + }; + t.exports = i + }).call(this, e("_process")) + }, { + "./ReactElementValidator": 70, + "./ReactRef": 96, + _process: 2 + }], + 96: [function(e, t) { + "use strict"; + + function n(e, t, n) { + "function" == typeof e ? e(t.getPublicInstance()) : o.addComponentAsRefTo(t, e, n) + } + + function r(e, t, n) { + "function" == typeof e ? e(null) : o.removeComponentAsRefFrom(t, e, n) + } + var o = e("./ReactOwner"), + a = {}; + a.attachRefs = function(e, t) { + var r = t.ref; + null != r && n(r, e, t._owner) + }, a.shouldUpdateRefs = function(e, t) { + return t._owner !== e._owner || t.ref !== e.ref + }, a.detachRefs = function(e, t) { + var n = t.ref; + null != n && r(n, e, t._owner) + }, t.exports = a + }, { + "./ReactOwner": 87 + }], + 97: [function(e, t) { + "use strict"; + var n = { + injectCreateReactRootIndex: function(e) { + r.createReactRootIndex = e + } + }, + r = { + createReactRootIndex: null, + injection: n + }; + t.exports = r + }, {}], + 98: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToString(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!1), t.perform(function() { + var n = l(e, null), + o = n.mountComponent(r, t, c); + return s.addChecksumToMarkup(o) + }, null) + } finally { + u.release(t) + } + } + + function o(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToStaticMarkup(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!0), t.perform(function() { + var n = l(e, null); + return n.mountComponent(r, t, c) + }, null) + } finally { + u.release(t) + } + } + var a = e("./ReactElement"), + i = e("./ReactInstanceHandles"), + s = e("./ReactMarkupChecksum"), + u = e("./ReactServerRenderingTransaction"), + c = e("./emptyObject"), + l = e("./instantiateReactComponent"), + p = e("./invariant"); + t.exports = { + renderToString: r, + renderToStaticMarkup: o + } + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactInstanceHandles": 78, + "./ReactMarkupChecksum": 82, + "./ReactServerRenderingTransaction": 99, + "./emptyObject": 137, + "./instantiateReactComponent": 156, + "./invariant": 157, + _process: 2 + }], + 99: [function(e, t) { + "use strict"; + + function n(e) { + this.reinitializeTransaction(), this.renderToStaticMarkup = e, this.reactMountReady = o.getPooled(null), this.putListenerQueue = a.getPooled() + } + var r = e("./PooledClass"), + o = e("./CallbackQueue"), + a = e("./ReactPutListenerQueue"), + i = e("./Transaction"), + s = e("./Object.assign"), + u = e("./emptyFunction"), + c = { + initialize: function() { + this.reactMountReady.reset() + }, + close: u + }, + l = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: u + }, + p = [l, c], + d = { + getTransactionWrappers: function() { + return p + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + o.release(this.reactMountReady), this.reactMountReady = null, a.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + s(n.prototype, i.Mixin, d), r.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactPutListenerQueue": 93, + "./Transaction": 123, + "./emptyFunction": 136 + }], + 100: [function(e, t) { + "use strict"; + + function n(e, t) { + var n = {}; + return function(r) { + n[t] = r, e.setState(n) + } + } + var r = { + createStateSetter: function(e, t) { + return function(n, r, o, a, i, s) { + var u = t.call(e, n, r, o, a, i, s); + u && e.setState(u) + } + }, + createStateKeySetter: function(e, t) { + var r = e.__keySetters || (e.__keySetters = {}); + return r[t] || (r[t] = n(e, t)) + } + }; + r.Mixin = { + createStateSetter: function(e) { + return r.createStateSetter(this, e) + }, + createStateKeySetter: function(e) { + return r.createStateKeySetter(this, e) + } + }, t.exports = r + }, {}], + 101: [function(e, t) { + "use strict"; + + function n() {} + + function r(e) { + return function(t, r) { + var o; + b.isDOMComponent(t) ? o = t.getDOMNode() : t.tagName && (o = t); + var a = new n; + a.target = o; + var i = new g(d.eventNameDispatchConfigs[e], m.getID(o), a); + E(i, r), u.accumulateTwoPhaseDispatches(i), y.batchedUpdates(function() { + s.enqueueEvents(i), s.processEventQueue() + }) + } + } + + function o() { + b.Simulate = {}; + var e; + for (e in d.eventNameDispatchConfigs) b.Simulate[e] = r(e) + } + + function a(e) { + return function(t, r) { + var o = new n(e); + E(o, r), b.isDOMComponent(t) ? b.simulateNativeEventOnDOMComponent(e, t, o) : t.tagName && b.simulateNativeEventOnNode(e, t, o) + } + } + var i = e("./EventConstants"), + s = e("./EventPluginHub"), + u = e("./EventPropagators"), + c = e("./React"), + l = e("./ReactElement"), + p = e("./ReactEmptyComponent"), + d = e("./ReactBrowserEventEmitter"), + f = e("./ReactCompositeComponent"), + h = e("./ReactInstanceHandles"), + v = e("./ReactInstanceMap"), + m = e("./ReactMount"), + y = e("./ReactUpdates"), + g = e("./SyntheticEvent"), + E = e("./Object.assign"), + C = i.topLevelTypes, + b = { + renderIntoDocument: function(e) { + var t = document.createElement("div"); + return c.render(e, t) + }, + isElement: function(e) { + return l.isValidElement(e) + }, + isElementOfType: function(e, t) { + return l.isValidElement(e) && e.type === t + }, + isDOMComponent: function(e) { + return !!(e && e.tagName && e.getDOMNode) + }, + isDOMComponentElement: function(e) { + return !!(e && l.isValidElement(e) && e.tagName) + }, + isCompositeComponent: function(e) { + return "function" == typeof e.render && "function" == typeof e.setState + }, + isCompositeComponentWithType: function(e, t) { + return !(!b.isCompositeComponent(e) || e.constructor !== t) + }, + isCompositeComponentElement: function(e) { + if (!l.isValidElement(e)) return !1; + var t = e.type.prototype; + return "function" == typeof t.render && "function" == typeof t.setState + }, + isCompositeComponentElementWithType: function(e, t) { + return !(!b.isCompositeComponentElement(e) || e.constructor !== t) + }, + getRenderedChildOfCompositeComponent: function(e) { + if (!b.isCompositeComponent(e)) return null; + var t = v.get(e); + return t._renderedComponent.getPublicInstance() + }, + findAllInRenderedTree: function(e, t) { + if (!e) return []; + var n = t(e) ? [e] : []; + if (b.isDOMComponent(e)) { + var r, o = v.get(e), + a = o._renderedComponent._renderedChildren; + for (r in a) a.hasOwnProperty(r) && a[r].getPublicInstance && (n = n.concat(b.findAllInRenderedTree(a[r].getPublicInstance(), t))) + } else b.isCompositeComponent(e) && (n = n.concat(b.findAllInRenderedTree(b.getRenderedChildOfCompositeComponent(e), t))); + return n + }, + scryRenderedDOMComponentsWithClass: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + var n = e.props.className; + return b.isDOMComponent(e) && n && -1 !== (" " + n + " ").indexOf(" " + t + " ") + }) + }, + findRenderedDOMComponentWithClass: function(e, t) { + var n = b.scryRenderedDOMComponentsWithClass(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match (found: " + n.length + ") for class:" + t); + return n[0] + }, + scryRenderedDOMComponentsWithTag: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isDOMComponent(e) && e.tagName === t.toUpperCase() + }) + }, + findRenderedDOMComponentWithTag: function(e, t) { + var n = b.scryRenderedDOMComponentsWithTag(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for tag:" + t); + return n[0] + }, + scryRenderedComponentsWithType: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isCompositeComponentWithType(e, t) + }) + }, + findRenderedComponentWithType: function(e, t) { + var n = b.scryRenderedComponentsWithType(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for componentType:" + t); + return n[0] + }, + mockComponent: function(e, t) { + return t = t || e.mockTagName || "div", e.prototype.render.mockImplementation(function() { + return c.createElement(t, null, this.props.children) + }), this + }, + simulateNativeEventOnNode: function(e, t, n) { + n.target = t, d.ReactEventListener.dispatchEvent(e, n) + }, + simulateNativeEventOnDOMComponent: function(e, t, n) { + b.simulateNativeEventOnNode(e, t.getDOMNode(), n) + }, + nativeTouchData: function(e, t) { + return { + touches: [{ + pageX: e, + pageY: t + }] + } + }, + createRenderer: function() { + return new _ + }, + Simulate: null, + SimulateNative: {} + }, + _ = function() { + this._instance = null + }; + _.prototype.getRenderOutput = function() { + return this._instance && this._instance._renderedComponent && this._instance._renderedComponent._renderedOutput || null + }; + var N = function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }; + N.prototype = { + mountComponent: function() {}, + receiveComponent: function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }, + unmountComponent: function() {} + }; + var O = function() {}; + E(O.prototype, f.Mixin, { + _instantiateReactComponent: function(e) { + return new N(e) + }, + _replaceNodeWithMarkupByID: function() {}, + _renderValidatedComponent: f.Mixin._renderValidatedComponentWithoutOwnerOrContext + }), _.prototype.render = function(e, t) { + var n = y.ReactReconcileTransaction.getPooled(); + this._render(e, n, t), y.ReactReconcileTransaction.release(n) + }, _.prototype.unmount = function() { + this._instance && this._instance.unmountComponent() + }, _.prototype._render = function(e, t, n) { + if (this._instance) this._instance.receiveComponent(e, t, n); + else { + var r = h.createReactRootID(), + o = new O(e.type); + o.construct(e), o.mountComponent(r, t, n), this._instance = o + } + }; + var R = s.injection.injectEventPluginOrder; + s.injection.injectEventPluginOrder = function() { + R.apply(this, arguments), o() + }; + var D = s.injection.injectEventPluginsByName; + s.injection.injectEventPluginsByName = function() { + D.apply(this, arguments), o() + }, o(); + var w; + for (w in C) { + var M = 0 === w.indexOf("top") ? w.charAt(3).toLowerCase() + w.substr(4) : w; + b.SimulateNative[M] = a(w) + } + t.exports = b + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPropagators": 27, + "./Object.assign": 35, + "./React": 37, + "./ReactBrowserEventEmitter": 39, + "./ReactCompositeComponent": 49, + "./ReactElement": 69, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./SyntheticEvent": 115 + }], + 102: [function(e, t) { + "use strict"; + var n = e("./ReactChildren"), + r = e("./ReactFragment"), + o = { + getChildMapping: function(e) { + return e ? r.extract(n.map(e, function(e) { + return e + })) : e + }, + mergeChildMappings: function(e, t) { + function n(n) { + return t.hasOwnProperty(n) ? t[n] : e[n] + } + e = e || {}, t = t || {}; + var r = {}, + o = []; + for (var a in e) t.hasOwnProperty(a) ? o.length && (r[a] = o, o = []) : o.push(a); + var i, s = {}; + for (var u in t) { + if (r.hasOwnProperty(u)) + for (i = 0; i < r[u].length; i++) { + var c = r[u][i]; + s[r[u][i]] = n(c) + } + s[u] = n(u) + } + for (i = 0; i < o.length; i++) s[o[i]] = n(o[i]); + return s + } + }; + t.exports = o + }, { + "./ReactChildren": 43, + "./ReactFragment": 75 + }], + 103: [function(e, t) { + "use strict"; + + function n() { + var e = document.createElement("div"), + t = e.style; + "AnimationEvent" in window || delete i.animationend.animation, "TransitionEvent" in window || delete i.transitionend.transition; + for (var n in i) { + var r = i[n]; + for (var o in r) + if (o in t) { + s.push(r[o]); + break + } + } + } + + function r(e, t, n) { + e.addEventListener(t, n, !1) + } + + function o(e, t, n) { + e.removeEventListener(t, n, !1) + } + var a = e("./ExecutionEnvironment"), + i = { + transitionend: { + transition: "transitionend", + WebkitTransition: "webkitTransitionEnd", + MozTransition: "mozTransitionEnd", + OTransition: "oTransitionEnd", + msTransition: "MSTransitionEnd" + }, + animationend: { + animation: "animationend", + WebkitAnimation: "webkitAnimationEnd", + MozAnimation: "mozAnimationEnd", + OAnimation: "oAnimationEnd", + msAnimation: "MSAnimationEnd" + } + }, + s = []; + a.canUseDOM && n(); + var u = { + addEndEventListener: function(e, t) { + return 0 === s.length ? void window.setTimeout(t, 0) : void s.forEach(function(n) { + r(e, n, t) + }) + }, + removeEndEventListener: function(e, t) { + 0 !== s.length && s.forEach(function(n) { + o(e, n, t) + }) + } + }; + t.exports = u + }, { + "./ExecutionEnvironment": 28 + }], + 104: [function(e, t) { + "use strict"; + var n = e("./React"), + r = e("./ReactTransitionChildMapping"), + o = e("./Object.assign"), + a = e("./cloneWithProps"), + i = e("./emptyFunction"), + s = n.createClass({ + displayName: "ReactTransitionGroup", + propTypes: { + component: n.PropTypes.any, + childFactory: n.PropTypes.func + }, + getDefaultProps: function() { + return { + component: "span", + childFactory: i.thatReturnsArgument + } + }, + getInitialState: function() { + return { + children: r.getChildMapping(this.props.children) + } + }, + componentWillMount: function() { + this.currentlyTransitioningKeys = {}, this.keysToEnter = [], this.keysToLeave = [] + }, + componentDidMount: function() { + var e = this.state.children; + for (var t in e) e[t] && this.performAppear(t) + }, + componentWillReceiveProps: function(e) { + var t = r.getChildMapping(e.children), + n = this.state.children; + this.setState({ + children: r.mergeChildMappings(n, t) + }); + var o; + for (o in t) { + var a = n && n.hasOwnProperty(o); + !t[o] || a || this.currentlyTransitioningKeys[o] || this.keysToEnter.push(o) + } + for (o in n) { + var i = t && t.hasOwnProperty(o); + !n[o] || i || this.currentlyTransitioningKeys[o] || this.keysToLeave.push(o) + } + }, + componentDidUpdate: function() { + var e = this.keysToEnter; + this.keysToEnter = [], e.forEach(this.performEnter); + var t = this.keysToLeave; + this.keysToLeave = [], t.forEach(this.performLeave) + }, + performAppear: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillAppear ? t.componentWillAppear(this._handleDoneAppearing.bind(this, e)) : this._handleDoneAppearing(e) + }, + _handleDoneAppearing: function(e) { + var t = this.refs[e]; + t.componentDidAppear && t.componentDidAppear(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performEnter: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillEnter ? t.componentWillEnter(this._handleDoneEntering.bind(this, e)) : this._handleDoneEntering(e) + }, + _handleDoneEntering: function(e) { + var t = this.refs[e]; + t.componentDidEnter && t.componentDidEnter(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performLeave: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillLeave ? t.componentWillLeave(this._handleDoneLeaving.bind(this, e)) : this._handleDoneLeaving(e) + }, + _handleDoneLeaving: function(e) { + var t = this.refs[e]; + t.componentDidLeave && t.componentDidLeave(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + if (n && n.hasOwnProperty(e)) this.performEnter(e); + else { + var a = o({}, this.state.children); + delete a[e], this.setState({ + children: a + }) + } + }, + render: function() { + var e = []; + for (var t in this.state.children) { + var r = this.state.children[t]; + r && e.push(a(this.props.childFactory(r), { + ref: t, + key: t + })) + } + return n.createElement(this.props.component, this.props, e) + } + }); + t.exports = s + }, { + "./Object.assign": 35, + "./React": 37, + "./ReactTransitionChildMapping": 102, + "./cloneWithProps": 129, + "./emptyFunction": 136 + }], + 105: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e !== a.currentlyMountingInstance && c.enqueueUpdate(e) + } + + function o(e, t) { + "production" !== n.env.NODE_ENV ? p(null == i.current, "%s(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.", t) : p(null == i.current); + var r = u.get(e); + return r ? r === a.currentlyUnmountingInstance ? null : r : ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? d(!t, "%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op.", t, t) : null), null) + } + var a = e("./ReactLifeCycle"), + i = e("./ReactCurrentOwner"), + s = e("./ReactElement"), + u = e("./ReactInstanceMap"), + c = e("./ReactUpdates"), + l = e("./Object.assign"), + p = e("./invariant"), + d = e("./warning"), + f = { + enqueueCallback: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t); + var i = o(e); + return i && i !== a.currentlyMountingInstance ? (i._pendingCallbacks ? i._pendingCallbacks.push(t) : i._pendingCallbacks = [t], void r(i)) : null + }, + enqueueCallbackInternal: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t), e._pendingCallbacks ? e._pendingCallbacks.push(t) : e._pendingCallbacks = [t], r(e) + }, + enqueueForceUpdate: function(e) { + var t = o(e, "forceUpdate"); + t && (t._pendingForceUpdate = !0, r(t)) + }, + enqueueReplaceState: function(e, t) { + var n = o(e, "replaceState"); + n && (n._pendingStateQueue = [t], n._pendingReplaceState = !0, r(n)) + }, + enqueueSetState: function(e, t) { + var n = o(e, "setState"); + if (n) { + var a = n._pendingStateQueue || (n._pendingStateQueue = []); + a.push(t), r(n) + } + }, + enqueueSetProps: function(e, t) { + var a = o(e, "setProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "setProps(...): You called `setProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement, + u = l({}, i.props, t); + a._pendingElement = s.cloneAndReplaceProps(i, u), r(a) + } + }, + enqueueReplaceProps: function(e, t) { + var a = o(e, "replaceProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "replaceProps(...): You called `replaceProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement; + a._pendingElement = s.cloneAndReplaceProps(i, t), r(a) + } + }, + enqueueElementInternal: function(e, t) { + e._pendingElement = t, r(e) + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactUpdates": 106, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 106: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + "production" !== n.env.NODE_ENV ? y(M.ReactReconcileTransaction && _, "ReactUpdates: must inject a reconcile transaction class and batching strategy") : y(M.ReactReconcileTransaction && _) + } + + function o() { + this.reinitializeTransaction(), this.dirtyComponentsLength = null, this.callbackQueue = l.getPooled(), this.reconcileTransaction = M.ReactReconcileTransaction.getPooled() + } + + function a(e, t, n, o, a) { + r(), _.batchedUpdates(e, t, n, o, a) + } + + function i(e, t) { + return e._mountOrder - t._mountOrder + } + + function s(e) { + var t = e.dirtyComponentsLength; + "production" !== n.env.NODE_ENV ? y(t === E.length, "Expected flush transaction's stored dirty-components length (%s) to match dirty-components array length (%s).", t, E.length) : y(t === E.length), E.sort(i); + for (var r = 0; t > r; r++) { + var o = E[r], + a = o._pendingCallbacks; + if (o._pendingCallbacks = null, h.performUpdateIfNecessary(o, e.reconcileTransaction), a) + for (var s = 0; s < a.length; s++) e.callbackQueue.enqueue(a[s], o.getPublicInstance()) + } + } + + function u(e) { + return r(), "production" !== n.env.NODE_ENV ? g(null == d.current, "enqueueUpdate(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null, _.isBatchingUpdates ? void E.push(e) : void _.batchedUpdates(u, e) + } + + function c(e, t) { + "production" !== n.env.NODE_ENV ? y(_.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched.") : y(_.isBatchingUpdates), C.enqueue(e, t), b = !0 + } + var l = e("./CallbackQueue"), + p = e("./PooledClass"), + d = e("./ReactCurrentOwner"), + f = e("./ReactPerf"), + h = e("./ReactReconciler"), + v = e("./Transaction"), + m = e("./Object.assign"), + y = e("./invariant"), + g = e("./warning"), + E = [], + C = l.getPooled(), + b = !1, + _ = null, + N = { + initialize: function() { + this.dirtyComponentsLength = E.length + }, + close: function() { + this.dirtyComponentsLength !== E.length ? (E.splice(0, this.dirtyComponentsLength), D()) : E.length = 0 + } + }, + O = { + initialize: function() { + this.callbackQueue.reset() + }, + close: function() { + this.callbackQueue.notifyAll() + } + }, + R = [N, O]; + m(o.prototype, v.Mixin, { + getTransactionWrappers: function() { + return R + }, + destructor: function() { + this.dirtyComponentsLength = null, l.release(this.callbackQueue), this.callbackQueue = null, M.ReactReconcileTransaction.release(this.reconcileTransaction), this.reconcileTransaction = null + }, + perform: function(e, t, n) { + return v.Mixin.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, e, t, n) + } + }), p.addPoolingTo(o); + var D = function() { + for (; E.length || b;) { + if (E.length) { + var e = o.getPooled(); + e.perform(s, null, e), o.release(e) + } + if (b) { + b = !1; + var t = C; + C = l.getPooled(), t.notifyAll(), l.release(t) + } + } + }; + D = f.measure("ReactUpdates", "flushBatchedUpdates", D); + var w = { + injectReconcileTransaction: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a reconcile transaction class") : y(e), M.ReactReconcileTransaction = e + }, + injectBatchingStrategy: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a batching strategy") : y(e), "production" !== n.env.NODE_ENV ? y("function" == typeof e.batchedUpdates, "ReactUpdates: must provide a batchedUpdates() function") : y("function" == typeof e.batchedUpdates), "production" !== n.env.NODE_ENV ? y("boolean" == typeof e.isBatchingUpdates, "ReactUpdates: must provide an isBatchingUpdates boolean attribute") : y("boolean" == typeof e.isBatchingUpdates), _ = e + } + }, + M = { + ReactReconcileTransaction: null, + batchedUpdates: a, + enqueueUpdate: u, + flushBatchedUpdates: D, + injection: w, + asap: c + }; + t.exports = M + }).call(this, e("_process")) + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactCurrentOwner": 51, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./Transaction": 123, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 107: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./LinkedStateMixin"), + o = e("./React"), + a = e("./ReactComponentWithPureRenderMixin"), + i = e("./ReactCSSTransitionGroup"), + s = e("./ReactFragment"), + u = e("./ReactTransitionGroup"), + c = e("./ReactUpdates"), + l = e("./cx"), + p = e("./cloneWithProps"), + d = e("./update"); + o.addons = { + CSSTransitionGroup: i, + LinkedStateMixin: r, + PureRenderMixin: a, + TransitionGroup: u, + batchedUpdates: c.batchedUpdates, + classSet: l, + cloneWithProps: p, + createFragment: s.create, + update: d + }, "production" !== n.env.NODE_ENV && (o.addons.Perf = e("./ReactDefaultPerf"), o.addons.TestUtils = e("./ReactTestUtils")), t.exports = o + }).call(this, e("_process")) + }, { + "./LinkedStateMixin": 31, + "./React": 37, + "./ReactCSSTransitionGroup": 40, + "./ReactComponentWithPureRenderMixin": 48, + "./ReactDefaultPerf": 67, + "./ReactFragment": 75, + "./ReactTestUtils": 101, + "./ReactTransitionGroup": 104, + "./ReactUpdates": 106, + "./cloneWithProps": 129, + "./cx": 134, + "./update": 177, + _process: 2 + }], + 108: [function(e, t) { + "use strict"; + var n = e("./DOMProperty"), + r = n.injection.MUST_USE_ATTRIBUTE, + o = { + Properties: { + cx: r, + cy: r, + d: r, + dx: r, + dy: r, + fill: r, + fillOpacity: r, + fontFamily: r, + fontSize: r, + fx: r, + fy: r, + gradientTransform: r, + gradientUnits: r, + markerEnd: r, + markerMid: r, + markerStart: r, + offset: r, + opacity: r, + patternContentUnits: r, + patternUnits: r, + points: r, + preserveAspectRatio: r, + r: r, + rx: r, + ry: r, + spreadMethod: r, + stopColor: r, + stopOpacity: r, + stroke: r, + strokeDasharray: r, + strokeLinecap: r, + strokeOpacity: r, + strokeWidth: r, + textAnchor: r, + transform: r, + version: r, + viewBox: r, + x1: r, + x2: r, + x: r, + y1: r, + y2: r, + y: r + }, + DOMAttributeNames: { + fillOpacity: "fill-opacity", + fontFamily: "font-family", + fontSize: "font-size", + gradientTransform: "gradientTransform", + gradientUnits: "gradientUnits", + markerEnd: "marker-end", + markerMid: "marker-mid", + markerStart: "marker-start", + patternContentUnits: "patternContentUnits", + patternUnits: "patternUnits", + preserveAspectRatio: "preserveAspectRatio", + spreadMethod: "spreadMethod", + stopColor: "stop-color", + stopOpacity: "stop-opacity", + strokeDasharray: "stroke-dasharray", + strokeLinecap: "stroke-linecap", + strokeOpacity: "stroke-opacity", + strokeWidth: "stroke-width", + textAnchor: "text-anchor", + viewBox: "viewBox" + } + }; + t.exports = o + }, { + "./DOMProperty": 17 + }], + 109: [function(e, t) { + "use strict"; + + function n(e) { + if ("selectionStart" in e && i.hasSelectionCapabilities(e)) return { + start: e.selectionStart, + end: e.selectionEnd + }; + if (window.getSelection) { + var t = window.getSelection(); + return { + anchorNode: t.anchorNode, + anchorOffset: t.anchorOffset, + focusNode: t.focusNode, + focusOffset: t.focusOffset + } + } + if (document.selection) { + var n = document.selection.createRange(); + return { + parentElement: n.parentElement(), + text: n.text, + top: n.boundingTop, + left: n.boundingLeft + } + } + } + + function r(e) { + if (y || null == h || h !== u()) return null; + var t = n(h); + if (!m || !p(m, t)) { + m = t; + var r = s.getPooled(f.select, v, e); + return r.type = "select", r.target = h, a.accumulateTwoPhaseDispatches(r), r + } + } + var o = e("./EventConstants"), + a = e("./EventPropagators"), + i = e("./ReactInputSelection"), + s = e("./SyntheticEvent"), + u = e("./getActiveElement"), + c = e("./isTextInputElement"), + l = e("./keyOf"), + p = e("./shallowEqual"), + d = o.topLevelTypes, + f = { + select: { + phasedRegistrationNames: { + bubbled: l({ + onSelect: null + }), + captured: l({ + onSelectCapture: null + }) + }, + dependencies: [d.topBlur, d.topContextMenu, d.topFocus, d.topKeyDown, d.topMouseDown, d.topMouseUp, d.topSelectionChange] + } + }, + h = null, + v = null, + m = null, + y = !1, + g = { + eventTypes: f, + extractEvents: function(e, t, n, o) { + switch (e) { + case d.topFocus: + (c(t) || "true" === t.contentEditable) && (h = t, v = n, m = null); + break; + case d.topBlur: + h = null, v = null, m = null; + break; + case d.topMouseDown: + y = !0; + break; + case d.topContextMenu: + case d.topMouseUp: + return y = !1, r(o); + case d.topSelectionChange: + case d.topKeyDown: + case d.topKeyUp: + return r(o) + } + } + }; + t.exports = g + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ReactInputSelection": 77, + "./SyntheticEvent": 115, + "./getActiveElement": 143, + "./isTextInputElement": 160, + "./keyOf": 164, + "./shallowEqual": 173 + }], + 110: [function(e, t) { + "use strict"; + var n = Math.pow(2, 53), + r = { + createReactRootIndex: function() { + return Math.ceil(Math.random() * n) + } + }; + t.exports = r + }, {}], + 111: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./EventConstants"), + o = e("./EventPluginUtils"), + a = e("./EventPropagators"), + i = e("./SyntheticClipboardEvent"), + s = e("./SyntheticEvent"), + u = e("./SyntheticFocusEvent"), + c = e("./SyntheticKeyboardEvent"), + l = e("./SyntheticMouseEvent"), + p = e("./SyntheticDragEvent"), + d = e("./SyntheticTouchEvent"), + f = e("./SyntheticUIEvent"), + h = e("./SyntheticWheelEvent"), + v = e("./getEventCharCode"), + m = e("./invariant"), + y = e("./keyOf"), + g = e("./warning"), + E = r.topLevelTypes, + C = { + blur: { + phasedRegistrationNames: { + bubbled: y({ + onBlur: !0 + }), + captured: y({ + onBlurCapture: !0 + }) + } + }, + click: { + phasedRegistrationNames: { + bubbled: y({ + onClick: !0 + }), + captured: y({ + onClickCapture: !0 + }) + } + }, + contextMenu: { + phasedRegistrationNames: { + bubbled: y({ + onContextMenu: !0 + }), + captured: y({ + onContextMenuCapture: !0 + }) + } + }, + copy: { + phasedRegistrationNames: { + bubbled: y({ + onCopy: !0 + }), + captured: y({ + onCopyCapture: !0 + }) + } + }, + cut: { + phasedRegistrationNames: { + bubbled: y({ + onCut: !0 + }), + captured: y({ + onCutCapture: !0 + }) + } + }, + doubleClick: { + phasedRegistrationNames: { + bubbled: y({ + onDoubleClick: !0 + }), + captured: y({ + onDoubleClickCapture: !0 + }) + } + }, + drag: { + phasedRegistrationNames: { + bubbled: y({ + onDrag: !0 + }), + captured: y({ + onDragCapture: !0 + }) + } + }, + dragEnd: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnd: !0 + }), + captured: y({ + onDragEndCapture: !0 + }) + } + }, + dragEnter: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnter: !0 + }), + captured: y({ + onDragEnterCapture: !0 + }) + } + }, + dragExit: { + phasedRegistrationNames: { + bubbled: y({ + onDragExit: !0 + }), + captured: y({ + onDragExitCapture: !0 + }) + } + }, + dragLeave: { + phasedRegistrationNames: { + bubbled: y({ + onDragLeave: !0 + }), + captured: y({ + onDragLeaveCapture: !0 + }) + } + }, + dragOver: { + phasedRegistrationNames: { + bubbled: y({ + onDragOver: !0 + }), + captured: y({ + onDragOverCapture: !0 + }) + } + }, + dragStart: { + phasedRegistrationNames: { + bubbled: y({ + onDragStart: !0 + }), + captured: y({ + onDragStartCapture: !0 + }) + } + }, + drop: { + phasedRegistrationNames: { + bubbled: y({ + onDrop: !0 + }), + captured: y({ + onDropCapture: !0 + }) + } + }, + focus: { + phasedRegistrationNames: { + bubbled: y({ + onFocus: !0 + }), + captured: y({ + onFocusCapture: !0 + }) + } + }, + input: { + phasedRegistrationNames: { + bubbled: y({ + onInput: !0 + }), + captured: y({ + onInputCapture: !0 + }) + } + }, + keyDown: { + phasedRegistrationNames: { + bubbled: y({ + onKeyDown: !0 + }), + captured: y({ + onKeyDownCapture: !0 + }) + } + }, + keyPress: { + phasedRegistrationNames: { + bubbled: y({ + onKeyPress: !0 + }), + captured: y({ + onKeyPressCapture: !0 + }) + } + }, + keyUp: { + phasedRegistrationNames: { + bubbled: y({ + onKeyUp: !0 + }), + captured: y({ + onKeyUpCapture: !0 + }) + } + }, + load: { + phasedRegistrationNames: { + bubbled: y({ + onLoad: !0 + }), + captured: y({ + onLoadCapture: !0 + }) + } + }, + error: { + phasedRegistrationNames: { + bubbled: y({ + onError: !0 + }), + captured: y({ + onErrorCapture: !0 + }) + } + }, + mouseDown: { + phasedRegistrationNames: { + bubbled: y({ + onMouseDown: !0 + }), + captured: y({ + onMouseDownCapture: !0 + }) + } + }, + mouseMove: { + phasedRegistrationNames: { + bubbled: y({ + onMouseMove: !0 + }), + captured: y({ + onMouseMoveCapture: !0 + }) + } + }, + mouseOut: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOut: !0 + }), + captured: y({ + onMouseOutCapture: !0 + }) + } + }, + mouseOver: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOver: !0 + }), + captured: y({ + onMouseOverCapture: !0 + }) + } + }, + mouseUp: { + phasedRegistrationNames: { + bubbled: y({ + onMouseUp: !0 + }), + captured: y({ + onMouseUpCapture: !0 + }) + } + }, + paste: { + phasedRegistrationNames: { + bubbled: y({ + onPaste: !0 + }), + captured: y({ + onPasteCapture: !0 + }) + } + }, + reset: { + phasedRegistrationNames: { + bubbled: y({ + onReset: !0 + }), + captured: y({ + onResetCapture: !0 + }) + } + }, + scroll: { + phasedRegistrationNames: { + bubbled: y({ + onScroll: !0 + }), + captured: y({ + onScrollCapture: !0 + }) + } + }, + submit: { + phasedRegistrationNames: { + bubbled: y({ + onSubmit: !0 + }), + captured: y({ + onSubmitCapture: !0 + }) + } + }, + touchCancel: { + phasedRegistrationNames: { + bubbled: y({ + onTouchCancel: !0 + }), + captured: y({ + onTouchCancelCapture: !0 + }) + } + }, + touchEnd: { + phasedRegistrationNames: { + bubbled: y({ + onTouchEnd: !0 + }), + captured: y({ + onTouchEndCapture: !0 + }) + } + }, + touchMove: { + phasedRegistrationNames: { + bubbled: y({ + onTouchMove: !0 + }), + captured: y({ + onTouchMoveCapture: !0 + }) + } + }, + touchStart: { + phasedRegistrationNames: { + bubbled: y({ + onTouchStart: !0 + }), + captured: y({ + onTouchStartCapture: !0 + }) + } + }, + wheel: { + phasedRegistrationNames: { + bubbled: y({ + onWheel: !0 + }), + captured: y({ + onWheelCapture: !0 + }) + } + } + }, + b = { + topBlur: C.blur, + topClick: C.click, + topContextMenu: C.contextMenu, + topCopy: C.copy, + topCut: C.cut, + topDoubleClick: C.doubleClick, + topDrag: C.drag, + topDragEnd: C.dragEnd, + topDragEnter: C.dragEnter, + topDragExit: C.dragExit, + topDragLeave: C.dragLeave, + topDragOver: C.dragOver, + topDragStart: C.dragStart, + topDrop: C.drop, + topError: C.error, + topFocus: C.focus, + topInput: C.input, + topKeyDown: C.keyDown, + topKeyPress: C.keyPress, + topKeyUp: C.keyUp, + topLoad: C.load, + topMouseDown: C.mouseDown, + topMouseMove: C.mouseMove, + topMouseOut: C.mouseOut, + topMouseOver: C.mouseOver, + topMouseUp: C.mouseUp, + topPaste: C.paste, + topReset: C.reset, + topScroll: C.scroll, + topSubmit: C.submit, + topTouchCancel: C.touchCancel, + topTouchEnd: C.touchEnd, + topTouchMove: C.touchMove, + topTouchStart: C.touchStart, + topWheel: C.wheel + }; + for (var _ in b) b[_].dependencies = [_]; + var N = { + eventTypes: C, + executeDispatch: function(e, t, r) { + var a = o.executeDispatch(e, t, r); + "production" !== n.env.NODE_ENV ? g("boolean" != typeof a, "Returning `false` from an event handler is deprecated and will be ignored in a future release. Instead, manually call e.stopPropagation() or e.preventDefault(), as appropriate.") : null, a === !1 && (e.stopPropagation(), e.preventDefault()) + }, + extractEvents: function(e, t, r, o) { + var y = b[e]; + if (!y) return null; + var g; + switch (e) { + case E.topInput: + case E.topLoad: + case E.topError: + case E.topReset: + case E.topSubmit: + g = s; + break; + case E.topKeyPress: + if (0 === v(o)) return null; + case E.topKeyDown: + case E.topKeyUp: + g = c; + break; + case E.topBlur: + case E.topFocus: + g = u; + break; + case E.topClick: + if (2 === o.button) return null; + case E.topContextMenu: + case E.topDoubleClick: + case E.topMouseDown: + case E.topMouseMove: + case E.topMouseOut: + case E.topMouseOver: + case E.topMouseUp: + g = l; + break; + case E.topDrag: + case E.topDragEnd: + case E.topDragEnter: + case E.topDragExit: + case E.topDragLeave: + case E.topDragOver: + case E.topDragStart: + case E.topDrop: + g = p; + break; + case E.topTouchCancel: + case E.topTouchEnd: + case E.topTouchMove: + case E.topTouchStart: + g = d; + break; + case E.topScroll: + g = f; + break; + case E.topWheel: + g = h; + break; + case E.topCopy: + case E.topCut: + case E.topPaste: + g = i + } + "production" !== n.env.NODE_ENV ? m(g, "SimpleEventPlugin: Unhandled event type, `%s`.", e) : m(g); + var C = g.getPooled(y, r, o); + return a.accumulateTwoPhaseDispatches(C), C + } + }; + t.exports = N + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./EventPluginUtils": 26, + "./EventPropagators": 27, + "./SyntheticClipboardEvent": 112, + "./SyntheticDragEvent": 114, + "./SyntheticEvent": 115, + "./SyntheticFocusEvent": 116, + "./SyntheticKeyboardEvent": 118, + "./SyntheticMouseEvent": 119, + "./SyntheticTouchEvent": 120, + "./SyntheticUIEvent": 121, + "./SyntheticWheelEvent": 122, + "./getEventCharCode": 144, + "./invariant": 157, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 112: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + clipboardData: function(e) { + return "clipboardData" in e ? e.clipboardData : window.clipboardData + } + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 113: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 114: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + dataTransfer: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 115: [function(e, t) { + "use strict"; + + function n(e, t, n) { + this.dispatchConfig = e, this.dispatchMarker = t, this.nativeEvent = n; + var r = this.constructor.Interface; + for (var o in r) + if (r.hasOwnProperty(o)) { + var i = r[o]; + this[o] = i ? i(n) : n[o] + } var s = null != n.defaultPrevented ? n.defaultPrevented : n.returnValue === !1; + this.isDefaultPrevented = s ? a.thatReturnsTrue : a.thatReturnsFalse, this.isPropagationStopped = a.thatReturnsFalse + } + var r = e("./PooledClass"), + o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./getEventTarget"), + s = { + type: null, + target: i, + currentTarget: a.thatReturnsNull, + eventPhase: null, + bubbles: null, + cancelable: null, + timeStamp: function(e) { + return e.timeStamp || Date.now() + }, + defaultPrevented: null, + isTrusted: null + }; + o(n.prototype, { + preventDefault: function() { + this.defaultPrevented = !0; + var e = this.nativeEvent; + e.preventDefault ? e.preventDefault() : e.returnValue = !1, this.isDefaultPrevented = a.thatReturnsTrue + }, + stopPropagation: function() { + var e = this.nativeEvent; + e.stopPropagation ? e.stopPropagation() : e.cancelBubble = !0, this.isPropagationStopped = a.thatReturnsTrue + }, + persist: function() { + this.isPersistent = a.thatReturnsTrue + }, + isPersistent: a.thatReturnsFalse, + destructor: function() { + var e = this.constructor.Interface; + for (var t in e) this[t] = null; + this.dispatchConfig = null, this.dispatchMarker = null, this.nativeEvent = null + } + }), n.Interface = s, n.augmentClass = function(e, t) { + var n = this, + a = Object.create(n.prototype); + o(a, e.prototype), e.prototype = a, e.prototype.constructor = e, e.Interface = o({}, n.Interface, t), e.augmentClass = n.augmentClass, r.addPoolingTo(e, r.threeArgumentPooler) + }, r.addPoolingTo(n, r.threeArgumentPooler), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./emptyFunction": 136, + "./getEventTarget": 147 + }], + 116: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = { + relatedTarget: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticUIEvent": 121 + }], + 117: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 118: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventCharCode"), + a = e("./getEventKey"), + i = e("./getEventModifierState"), + s = { + key: a, + location: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + repeat: null, + locale: null, + getModifierState: i, + charCode: function(e) { + return "keypress" === e.type ? o(e) : 0 + }, + keyCode: function(e) { + return "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + }, + which: function(e) { + return "keypress" === e.type ? o(e) : "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + } + }; + r.augmentClass(n, s), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventCharCode": 144, + "./getEventKey": 145, + "./getEventModifierState": 146 + }], + 119: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./ViewportMetrics"), + a = e("./getEventModifierState"), + i = { + screenX: null, + screenY: null, + clientX: null, + clientY: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + getModifierState: a, + button: function(e) { + var t = e.button; + return "which" in e ? t : 2 === t ? 2 : 4 === t ? 1 : 0 + }, + buttons: null, + relatedTarget: function(e) { + return e.relatedTarget || (e.fromElement === e.srcElement ? e.toElement : e.fromElement) + }, + pageX: function(e) { + return "pageX" in e ? e.pageX : e.clientX + o.currentScrollLeft + }, + pageY: function(e) { + return "pageY" in e ? e.pageY : e.clientY + o.currentScrollTop + } + }; + r.augmentClass(n, i), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./ViewportMetrics": 124, + "./getEventModifierState": 146 + }], + 120: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventModifierState"), + a = { + touches: null, + targetTouches: null, + changedTouches: null, + altKey: null, + metaKey: null, + ctrlKey: null, + shiftKey: null, + getModifierState: o + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventModifierState": 146 + }], + 121: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = e("./getEventTarget"), + a = { + view: function(e) { + if (e.view) return e.view; + var t = o(e); + if (null != t && t.window === t) return t; + var n = t.ownerDocument; + return n ? n.defaultView || n.parentWindow : window + }, + detail: function(e) { + return e.detail || 0 + } + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticEvent": 115, + "./getEventTarget": 147 + }], + 122: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + deltaX: function(e) { + return "deltaX" in e ? e.deltaX : "wheelDeltaX" in e ? -e.wheelDeltaX : 0 + }, + deltaY: function(e) { + return "deltaY" in e ? e.deltaY : "wheelDeltaY" in e ? -e.wheelDeltaY : "wheelDelta" in e ? -e.wheelDelta : 0 + }, + deltaZ: null, + deltaMode: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 123: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + reinitializeTransaction: function() { + this.transactionWrappers = this.getTransactionWrappers(), this.wrapperInitData ? this.wrapperInitData.length = 0 : this.wrapperInitData = [], this._isInTransaction = !1 + }, + _isInTransaction: !1, + getTransactionWrappers: null, + isInTransaction: function() { + return !!this._isInTransaction + }, + perform: function(e, t, o, a, i, s, u, c) { + "production" !== n.env.NODE_ENV ? r(!this.isInTransaction(), "Transaction.perform(...): Cannot initialize a transaction when there is already an outstanding transaction.") : r(!this.isInTransaction()); + var l, p; + try { + this._isInTransaction = !0, l = !0, this.initializeAll(0), p = e.call(t, o, a, i, s, u, c), l = !1 + } finally { + try { + if (l) try { + this.closeAll(0) + } catch (d) {} else this.closeAll(0) + } finally { + this._isInTransaction = !1 + } + } + return p + }, + initializeAll: function(e) { + for (var t = this.transactionWrappers, n = e; n < t.length; n++) { + var r = t[n]; + try { + this.wrapperInitData[n] = a.OBSERVED_ERROR, this.wrapperInitData[n] = r.initialize ? r.initialize.call(this) : null + } finally { + if (this.wrapperInitData[n] === a.OBSERVED_ERROR) try { + this.initializeAll(n + 1) + } catch (o) {} + } + } + }, + closeAll: function(e) { + "production" !== n.env.NODE_ENV ? r(this.isInTransaction(), "Transaction.closeAll(): Cannot close transaction when none are open.") : r(this.isInTransaction()); + for (var t = this.transactionWrappers, o = e; o < t.length; o++) { + var i, s = t[o], + u = this.wrapperInitData[o]; + try { + i = !0, u !== a.OBSERVED_ERROR && s.close && s.close.call(this, u), i = !1 + } finally { + if (i) try { + this.closeAll(o + 1) + } catch (c) {} + } + } + this.wrapperInitData.length = 0 + } + }, + a = { + Mixin: o, + OBSERVED_ERROR: {} + }; + t.exports = a + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 124: [function(e, t) { + "use strict"; + var n = { + currentScrollLeft: 0, + currentScrollTop: 0, + refreshScrollValues: function(e) { + n.currentScrollLeft = e.x, n.currentScrollTop = e.y + } + }; + t.exports = n + }, {}], + 125: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + if ("production" !== n.env.NODE_ENV ? o(null != t, "accumulateInto(...): Accumulated items must not be null or undefined.") : o(null != t), null == e) return t; + var r = Array.isArray(e), + a = Array.isArray(t); + return r && a ? (e.push.apply(e, t), e) : r ? (e.push(t), e) : a ? [e].concat(t) : [e, t] + } + var o = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 126: [function(e, t) { + "use strict"; + + function n(e) { + for (var t = 1, n = 0, o = 0; o < e.length; o++) t = (t + e.charCodeAt(o)) % r, n = (n + t) % r; + return t | n << 16 + } + var r = 65521; + t.exports = n + }, {}], + 127: [function(e, t) { + function n(e) { + return e.replace(r, function(e, t) { + return t.toUpperCase() + }) + } + var r = /-(.)/g; + t.exports = n + }, {}], + 128: [function(e, t) { + "use strict"; + + function n(e) { + return r(e.replace(o, "ms-")) + } + var r = e("./camelize"), + o = /^-ms-/; + t.exports = n + }, { + "./camelize": 127 + }], + 129: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? s(!e.ref, "You are calling cloneWithProps() on a child with a ref. This is dangerous because you're creating a new child which will not be added as a ref to its parent.") : null); + var r = a.mergeProps(t, e.props); + return !r.hasOwnProperty(u) && e.props.hasOwnProperty(u) && (r.children = e.props.children), o.createElement(e.type, r) + } + var o = e("./ReactElement"), + a = e("./ReactPropTransferer"), + i = e("./keyOf"), + s = e("./warning"), + u = i({ + children: null + }); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactPropTransferer": 89, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 130: [function(e, t) { + function n(e, t) { + return e && t ? e === t ? !0 : r(e) ? !1 : r(t) ? n(e, t.parentNode) : e.contains ? e.contains(t) : e.compareDocumentPosition ? !!(16 & e.compareDocumentPosition(t)) : !1 : !1 + } + var r = e("./isTextNode"); + t.exports = n + }, { + "./isTextNode": 161 + }], + 131: [function(e, t) { + function n(e) { + return !!e && ("object" == typeof e || "function" == typeof e) && "length" in e && !("setInterval" in e) && "number" != typeof e.nodeType && (Array.isArray(e) || "callee" in e || "item" in e) + } + + function r(e) { + return n(e) ? Array.isArray(e) ? e.slice() : o(e) : [e] + } + var o = e("./toArray"); + t.exports = r + }, { + "./toArray": 175 + }], + 132: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + var t = a.createFactory(e), + r = o.createClass({ + tagName: e.toUpperCase(), + displayName: "ReactFullPageComponent" + e, + componentWillUnmount: function() { + "production" !== n.env.NODE_ENV ? i(!1, "%s tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg , , and ) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.", this.constructor.displayName) : i(!1) + }, + render: function() { + return t(this.props) + } + }); + return r + } + var o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactClass": 44, + "./ReactElement": 69, + "./invariant": 157, + _process: 2 + }], + 133: [function(e, t) { + (function(n) { + function r(e) { + var t = e.match(l); + return t && t[1].toLowerCase() + } + + function o(e, t) { + var o = c; + "production" !== n.env.NODE_ENV ? u(!!c, "createNodesFromMarkup dummy not initialized") : u(!!c); + var a = r(e), + l = a && s(a); + if (l) { + o.innerHTML = l[1] + e + l[2]; + for (var p = l[0]; p--;) o = o.lastChild + } else o.innerHTML = e; + var d = o.getElementsByTagName("script"); + d.length && ("production" !== n.env.NODE_ENV ? u(t, "createNodesFromMarkup(...): Unexpected + + diff --git a/DS4Windows/DS4Control/ControlSerivce.cs b/DS4Windows/DS4Control/ControlSerivce.cs deleted file mode 100644 index 28759a73fc..0000000000 --- a/DS4Windows/DS4Control/ControlSerivce.cs +++ /dev/null @@ -1,851 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using System.IO; -using System.Reflection; -using System.Media; -using System.Threading.Tasks; -using static DS4Windows.Global; -namespace DS4Windows -{ - public class ControlService - { - public X360Device x360Bus; - public DS4Device[] DS4Controllers = new DS4Device[4]; - public Mouse[] touchPad = new Mouse[4]; - private bool running = false; - private DS4State[] MappedState = new DS4State[4]; - private DS4State[] CurrentState = new DS4State[4]; - private DS4State[] PreviousState = new DS4State[4]; - public DS4StateExposed[] ExposedState = new DS4StateExposed[4]; - public bool recordingMacro = false; - public event EventHandler Debug = null; - public bool eastertime = false; - private int eCode = 0; - bool[] buttonsdown = { false, false, false, false }; - List dcs = new List(); - bool[] held = new bool[4]; - int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - SoundPlayer sp = new SoundPlayer(); - - private class X360Data - { - public byte[] Report = new byte[28]; - public byte[] Rumble = new byte[8]; - } - private X360Data[] processingData = new X360Data[4]; - - public ControlService() - { - sp.Stream = Properties.Resources.EE; - x360Bus = new X360Device(); - AddtoDS4List(); - for (int i = 0; i < DS4Controllers.Length; i++) - { - processingData[i] = new X360Data(); - MappedState[i] = new DS4State(); - CurrentState[i] = new DS4State(); - PreviousState[i] = new DS4State(); - ExposedState[i] = new DS4StateExposed(CurrentState[i]); - } - } - - void AddtoDS4List() - { - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Circle); - dcs.Add(DS4Controls.Square); - dcs.Add(DS4Controls.Triangle); - dcs.Add(DS4Controls.Options); - dcs.Add(DS4Controls.Share); - dcs.Add(DS4Controls.DpadUp); - dcs.Add(DS4Controls.DpadDown); - dcs.Add(DS4Controls.DpadLeft); - dcs.Add(DS4Controls.DpadRight); - dcs.Add(DS4Controls.PS); - dcs.Add(DS4Controls.L1); - dcs.Add(DS4Controls.R1); - dcs.Add(DS4Controls.L2); - dcs.Add(DS4Controls.R2); - dcs.Add(DS4Controls.L3); - dcs.Add(DS4Controls.R3); - dcs.Add(DS4Controls.LXPos); - dcs.Add(DS4Controls.LXNeg); - dcs.Add(DS4Controls.LYPos); - dcs.Add(DS4Controls.LYNeg); - dcs.Add(DS4Controls.RXPos); - dcs.Add(DS4Controls.RXNeg); - dcs.Add(DS4Controls.RYPos); - dcs.Add(DS4Controls.RYNeg); - dcs.Add(DS4Controls.SwipeUp); - dcs.Add(DS4Controls.SwipeDown); - dcs.Add(DS4Controls.SwipeLeft); - dcs.Add(DS4Controls.SwipeRight); - } - - private async void WarnExclusiveModeFailure(DS4Device device) - { - if (DS4Devices.isExclusiveMode && !device.IsExclusive) - { - await System.Threading.Tasks.Task.Delay(5); - String message = Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.MacAddress) + " " + Properties.Resources.QuitOtherPrograms; - LogDebug(message, true); - Log.LogToTray(message, true); - } - } - public bool Start(bool showlog = true) - { - if (x360Bus.Open() && x360Bus.Start()) - { - if (showlog) - LogDebug(Properties.Resources.Starting); - DS4Devices.isExclusiveMode = UseExclusiveMode; - if (showlog) - { - LogDebug(Properties.Resources.SearchingController); - LogDebug(DS4Devices.isExclusiveMode ? Properties.Resources.UsingExclusive: Properties.Resources.UsingShared); - } - try - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - int ind = 0; - DS4LightBar.defualtLight = false; - foreach (DS4Device device in devices) - { - if (showlog) - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[ind] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[ind] = new Mouse(ind, device); - device.LightBarColor = MainColor[ind]; - if (!DinputOnly[ind]) - x360Bus.Plugin(ind); - device.Report += this.On_Report; - TouchPadOn(ind, device); - //string filename = ProfilePath[ind]; - ind++; - if (showlog) - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[ind-1] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", ind.ToString()).Replace("*Profile name*", ProfilePath[ind-1]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (ind).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - if (ind >= 4) // out of Xinput devices! - break; - } - } - catch (Exception e) - { - LogDebug(e.Message); - Log.LogToTray(e.Message); - } - running = true; - - } - return true; - } - - public bool Stop(bool showlog = true) - { - if (running) - { - running = false; - if (showlog) - LogDebug(Properties.Resources.StoppingX360); - bool anyUnplugged = false; - for (int i = 0; i < DS4Controllers.Length; i++) - { - if (DS4Controllers[i] != null) - { - if (DCBTatStop && !DS4Controllers[i].Charging && showlog) - DS4Controllers[i].DisconnectBT(); - else - { - DS4LightBar.forcelight[i] = false; - DS4LightBar.forcedFlash[i] = 0; - DS4LightBar.defualtLight = true; - DS4LightBar.updateLightBar(DS4Controllers[i], i, CurrentState[i], ExposedState[i], touchPad[i]); - System.Threading.Thread.Sleep(50); - } - CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(i); - anyUnplugged = true; - DS4Controllers[i] = null; - touchPad[i] = null; - } - } - if (anyUnplugged) - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - x360Bus.UnplugAll(); - x360Bus.Stop(); - if (showlog) - LogDebug(Properties.Resources.StoppingDS4); - DS4Devices.stopControllers(); - if (showlog) - LogDebug(Properties.Resources.StoppedDS4Windows); - ControllerStatusChanged(this); - } - return true; - } - - public bool HotPlug() - { - if (running) - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - foreach (DS4Device device in devices) - { - if (device.IsDisconnecting) - continue; - if (((Func)delegate - { - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] != null && DS4Controllers[Index].MacAddress == device.MacAddress) - return true; - return false; - })()) - continue; - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] == null) - { - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[Index] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[Index] = new Mouse(Index, device); - device.LightBarColor = MainColor[Index]; - device.Report += this.On_Report; - if (!DinputOnly[Index]) - x360Bus.Plugin(Index); - TouchPadOn(Index, device); - //string filename = Path.GetFileName(ProfilePath[Index]); - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - - break; - } - } - } - return true; - } - - public void TouchPadOn(int ind, DS4Device device) - { - ITouchpadBehaviour tPad = touchPad[ind]; - device.Touchpad.TouchButtonDown += tPad.touchButtonDown; - device.Touchpad.TouchButtonUp += tPad.touchButtonUp; - device.Touchpad.TouchesBegan += tPad.touchesBegan; - device.Touchpad.TouchesMoved += tPad.touchesMoved; - device.Touchpad.TouchesEnded += tPad.touchesEnded; - device.Touchpad.TouchUnchanged += tPad.touchUnchanged; - device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; - //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - ControllerStatusChanged(this); - } - - public void TimeoutConnection(DS4Device d) - { - try - { - System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); - sw.Start(); - while (!d.IsAlive()) - { - if (sw.ElapsedMilliseconds < 1000) - System.Threading.Thread.SpinWait(500); - //If weve been waiting less than 1 second let the thread keep its processing chunk - else - System.Threading.Thread.Sleep(500); - //If weve been waiting more than 1 second give up some resources - - if (sw.ElapsedMilliseconds > 5000) throw new TimeoutException(); //Weve waited long enough - } - sw.Reset(); - } - catch (TimeoutException) - { - Stop(false); - Start(false); - } - } - - public string getDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - String battery; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Charged; - else - battery = Properties.Resources.Charging.Replace("*number*", d.Battery.ToString()); - } - else - { - battery = Properties.Resources.Battery.Replace("*number*", d.Battery.ToString()); - } - return d.MacAddress + " (" + d.ConnectionType + "), " + battery; - //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); - } - else - return String.Empty; - } - - public string getDS4MacAddress(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - return d.MacAddress; - } - else - return String.Empty; - } - - public string getShortDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return (d.ConnectionType + " " + battery); - } - else - return Properties.Resources.NoneText; - } - - public string getDS4Battery(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return battery; - } - else - return Properties.Resources.NA; - } - - public string getDS4Status(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - return d.ConnectionType+""; - } - else - return Properties.Resources.NoneText; - } - - - private int XINPUT_UNPLUG_SETTLE_TIME = 250; // Inhibit races that occur with the asynchronous teardown of ScpVBus -> X360 driver instance. - //Called when DS4 is disconnected or timed out - protected virtual void On_DS4Removal(object sender, EventArgs e) - { - DS4Device device = (DS4Device)sender; - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (DS4Controllers[i] != null && device.MacAddress == DS4Controllers[i].MacAddress) - ind = i; - if (ind != -1) - { - CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(ind); - string removed = Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind +1).ToString()); - if (DS4Controllers[ind].Battery <= 20 && - DS4Controllers[ind].ConnectionType == ConnectionType.BT && !DS4Controllers[ind].Charging) - removed += ". " + Properties.Resources.ChargeController; - LogDebug(removed); - Log.LogToTray(removed); - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - DS4Controllers[ind] = null; - touchPad[ind] = null; - ControllerStatusChanged(this); - } - } - public bool[] lag = { false, false, false, false }; - //Called every time the new input report has arrived - protected virtual void On_Report(object sender, EventArgs e) - { - - DS4Device device = (DS4Device)sender; - - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (device == DS4Controllers[i]) - ind = i; - - if (ind != -1) - { - if (FlushHIDQueue[ind]) - device.FlushHID(); - if (!string.IsNullOrEmpty(device.error)) - { - LogDebug(device.error); - } - if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) - { - if (device.Latency >= FlashWhenLateAt && !lag[ind]) - LagFlashWarning(ind, true); - else if (device.Latency < FlashWhenLateAt && lag[ind]) - LagFlashWarning(ind, false); - } - device.getExposedState(ExposedState[ind], CurrentState[ind]); - DS4State cState = CurrentState[ind]; - device.getPreviousState(PreviousState[ind]); - DS4State pState = PreviousState[ind]; - if (pState.Battery != cState.Battery) - ControllerStatusChanged(this); - CheckForHotkeys(ind, cState, pState); - if (eastertime) - EasterTime(ind); - GetInputkeys(ind); - if (LSCurve[ind] != 0 || RSCurve[ind] != 0 || LSDeadzone[ind] != 0 || RSDeadzone[ind] != 0 || - L2Deadzone[ind] != 0 || R2Deadzone[ind] != 0 || LSSens[ind] != 0 || RSSens[ind] != 0 || - L2Sens[ind] != 0 || R2Sens[ind] != 0) //if a curve or deadzone is in place - cState = Mapping.SetCurveAndDeadzone(ind, cState); - if (!recordingMacro && (!string.IsNullOrEmpty(tempprofilename[ind]) || - HasCustomAction(ind) || HasCustomExtras(ind) || ProfileActions[ind].Count > 0)) - { - Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); - cState = MappedState[ind]; - } - //if (HasCustomExtras(ind)) - // DoExtras(ind); - - // Update the GUI/whatever. - DS4LightBar.updateLightBar(device, ind, cState, ExposedState[ind], touchPad[ind]); - - x360Bus.Parse(cState, processingData[ind].Report, ind); - // We push the translated Xinput state, and simultaneously we - // pull back any possible rumble data coming from Xinput consumers. - if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) - { - Byte Big = (Byte)(processingData[ind].Rumble[3]); - Byte Small = (Byte)(processingData[ind].Rumble[4]); - - if (processingData[ind].Rumble[1] == 0x08) - { - setRumble(Big, Small, ind); - } - } - - // Output any synthetic events. - Mapping.Commit(ind); - // Pull settings updates. - device.IdleTimeout = IdleDisconnectTimeout[ind]; - } - } - - public void LagFlashWarning(int ind, bool on) - { - if (on) - { - lag[ind] = true; - LogDebug(Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); - if (FlashWhenLate) - { - DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = 2; - DS4LightBar.forcelight[ind] = true; - } - } - else - { - lag[ind] = false; - LogDebug(Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - } - } - - /* private void DoExtras(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - DS4Controls helddown = DS4Controls.None; - foreach (KeyValuePair p in getCustomExtras(ind)) - { - if (Mapping.getBoolMapping(ind, p.Key, cState, eState, tp)) - { - helddown = p.Key; - break; - } - } - if (helddown != DS4Controls.None) - { - string p = getCustomExtras(ind)[helddown]; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[ind] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - setRumble((byte)extras[0], (byte)extras[1], ind); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = (byte)extras[6]; - DS4LightBar.forcelight[ind] = true; - } - if (extras[7] == 1) - { - if (oldmouse[ind] == -1) - oldmouse[ind] = ButtonMouseSensitivity[ind]; - ButtonMouseSensitivity[ind] = extras[8]; - } - } - catch { } - } - else if (held[ind]) - { - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - ButtonMouseSensitivity[ind] = oldmouse[ind]; - oldmouse[ind] = -1; - setRumble(0, 0, ind); - held[ind] = false; - } - }*/ - - - - public void EasterTime(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - - bool pb = false; - foreach (DS4Controls dc in dcs) - { - if (Mapping.getBoolMapping(ind, dc, cState, eState, tp)) - { - pb = true; - break; - } - } - int temp = eCode; - //Looks like you found the easter egg code, since you're already cheating, - //I scrambled the code for you :) - if (pb && !buttonsdown[ind]) - { - if (cState.Cross && eCode == 9) - eCode++; - else if (!cState.Cross && eCode == 9) - eCode = 0; - else if (cState.DpadLeft && eCode == 6) - eCode++; - else if (!cState.DpadLeft && eCode == 6) - eCode = 0; - else if (cState.DpadRight && eCode == 7) - eCode++; - else if (!cState.DpadRight && eCode == 7) - eCode = 0; - else if (cState.DpadLeft && eCode == 4) - eCode++; - else if (!cState.DpadLeft && eCode == 4) - eCode = 0; - else if (cState.DpadDown && eCode == 2) - eCode++; - else if (!cState.DpadDown && eCode == 2) - eCode = 0; - else if (cState.DpadRight && eCode == 5) - eCode++; - else if (!cState.DpadRight && eCode == 5) - eCode = 0; - else if (cState.DpadUp && eCode == 1) - eCode++; - else if (!cState.DpadUp && eCode == 1) - eCode = 0; - else if (cState.DpadDown && eCode == 3) - eCode++; - else if (!cState.DpadDown && eCode == 3) - eCode = 0; - else if (cState.Circle && eCode == 8) - eCode++; - else if (!cState.Circle && eCode == 8) - eCode = 0; - - if (cState.DpadUp && eCode == 0) - eCode++; - - if (eCode == 10) - { - string message = "(!)"; - sp.Play(); - LogDebug(message, true); - eCode = 0; - } - - if (temp != eCode) - Console.WriteLine(eCode); - buttonsdown[ind] = true; - } - else if (!pb) - buttonsdown[ind] = false; - } - - public string GetInputkeys(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return "Cross"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return "Circle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return "Triangle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return "Square"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return "L1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return "R1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return "L2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return "R2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return "L3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return "R3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return "Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return "Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return "Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return "Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return "Share"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return "Options"; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return "PS"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return "LS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return "LS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return "LS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return "LS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return "RS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return "RS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return "RS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return "RS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return "Touch Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return "Touch Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return "Touch Multi"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return "Touch Upper"; - return "nothing"; - } - - public DS4Controls GetInputkeysDS4(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return DS4Controls.Cross; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return DS4Controls.Circle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return DS4Controls.Triangle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return DS4Controls.Square; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return DS4Controls.L1; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return DS4Controls.R1; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return DS4Controls.L2; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return DS4Controls.R2; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return DS4Controls.L3; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return DS4Controls.R3; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return DS4Controls.DpadUp; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return DS4Controls.DpadDown; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return DS4Controls.DpadLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return DS4Controls.DpadRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return DS4Controls.Share; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return DS4Controls.Options; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return DS4Controls.PS; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return DS4Controls.LXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return DS4Controls.LXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return DS4Controls.LYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return DS4Controls.LYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return DS4Controls.RXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return DS4Controls.RXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return DS4Controls.RYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return DS4Controls.RYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return DS4Controls.TouchLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return DS4Controls.TouchRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return DS4Controls.TouchMulti; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return DS4Controls.TouchUpper; - return DS4Controls.None; - } - - public bool[] touchreleased = { true, true, true, true }, touchslid = { false, false, false, false }; - public byte[] oldtouchvalue = { 0, 0, 0, 0 }; - public int[] oldscrollvalue = { 0, 0, 0, 0 }; - protected virtual void CheckForHotkeys(int deviceID, DS4State cState, DS4State pState) - { - if (!UseTPforControls[deviceID] && cState.Touch1 && pState.PS) - { - if (TouchSensitivity[deviceID] > 0 && touchreleased[deviceID]) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - else if (touchreleased[deviceID]) - { - TouchSensitivity[deviceID] = oldtouchvalue[deviceID]; - ScrollSensitivity[deviceID] = oldscrollvalue[deviceID]; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - } - else - touchreleased[deviceID] = true; - } - - public virtual void StartTPOff(int deviceID) - { - if (deviceID < 4) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - } - } - - public virtual string TouchpadSlide(int ind) - { - DS4State cState = CurrentState[ind]; - string slidedir = "none"; - if (DS4Controllers[ind] != null && cState.Touch2 && !(touchPad[ind].dragging || touchPad[ind].dragging2)) - if (touchPad[ind].slideright && !touchslid[ind]) - { - slidedir = "right"; - touchslid[ind] = true; - } - else if (touchPad[ind].slideleft && !touchslid[ind]) - { - slidedir = "left"; - touchslid[ind] = true; - } - else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) - { - slidedir = ""; - touchslid[ind] = false; - } - return slidedir; - } - public virtual void LogDebug(String Data, bool warning = false) - { - Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); - if (Debug != null) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - OnDebug(this, args); - } - } - - public virtual void OnDebug(object sender, DebugEventArgs args) - { - if (Debug != null) - Debug(this, args); - } - - //sets the rumble adjusted with rumble boost - public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) - { - byte boost = RumbleBoost[deviceNum]; - uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; - if (lightBoosted > 255) - lightBoosted = 255; - uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; - if (heavyBoosted > 255) - heavyBoosted = 255; - if (deviceNum < 4) - if (DS4Controllers[deviceNum] != null) - DS4Controllers[deviceNum].setRumble((byte)lightBoosted, (byte)heavyBoosted); - } - - public DS4State getDS4State(int ind) - { - return CurrentState[ind]; - } - public DS4State getDS4StateMapped(int ind) - { - return MappedState[ind]; - } - } -} diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs new file mode 100644 index 0000000000..067e33d3c4 --- /dev/null +++ b/DS4Windows/DS4Control/ControlService.cs @@ -0,0 +1,1613 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System.Diagnostics; +using static DS4Windows.Global; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class ControlService + { + public ViGEmClient vigemTestClient = null; + public const int DS4_CONTROLLER_COUNT = 4; + public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT]; + public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT]; + public bool running = false; + private DS4State[] MappedState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] CurrentState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] PreviousState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] TempState = new DS4State[DS4_CONTROLLER_COUNT]; + public DS4StateExposed[] ExposedState = new DS4StateExposed[DS4_CONTROLLER_COUNT]; + public ControllerSlotManager slotManager = new ControllerSlotManager(); + public bool recordingMacro = false; + public event EventHandler Debug = null; + bool[] buttonsdown = new bool[4] { false, false, false, false }; + bool[] held = new bool[DS4_CONTROLLER_COUNT]; + int[] oldmouse = new int[DS4_CONTROLLER_COUNT] { -1, -1, -1, -1 }; + public OutputDevice[] outputDevices = new OutputDevice[4] { null, null, null, null }; + Thread tempThread; + Thread tempBusThread; + public List affectedDevs = new List() + { + @"HID\VID_054C&PID_05C4", + @"HID\VID_054C&PID_09CC&MI_03", + @"HID\VID_054C&PID_0BA0&MI_03", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&05c4", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&09cc", + }; + public bool suspending; + //SoundPlayer sp = new SoundPlayer(); + private UdpServer _udpServer; + private OutputSlotManager outputslotMan; + + public event EventHandler ServiceStarted; + public event EventHandler PreServiceStop; + public event EventHandler ServiceStopped; + public event EventHandler RunningChanged; + //public event EventHandler HotplugFinished; + public delegate void HotplugControllerHandler(ControlService sender, DS4Device device, int index); + public event HotplugControllerHandler HotplugController; + + private byte[][] udpOutBuffers = new byte[4][] + { + new byte[100], new byte[100], + new byte[100], new byte[100] + }; + + + void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta) + { + //meta = new DualShockPadMeta(); + meta.PadId = (byte) padIdx; + meta.Model = DsModel.DS4; + + var d = DS4Controllers[padIdx]; + if (d == null) + { + meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + meta.ConnectionType = DsConnection.None; + meta.Model = DsModel.None; + meta.BatteryStatus = 0; + meta.IsActive = false; + return; + //return meta; + } + + bool isValidSerial = false; + //if (d.isValidSerial()) + //{ + string stringMac = d.getMacAddress(); + if (!string.IsNullOrEmpty(stringMac)) + { + stringMac = string.Join("", stringMac.Split(':')); + //stringMac = stringMac.Replace(":", "").Trim(); + meta.PadMacAddress = System.Net.NetworkInformation.PhysicalAddress.Parse(stringMac); + isValidSerial = d.isValidSerial(); + } + //} + + if (!isValidSerial) + { + //meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + } + else + { + if (d.isSynced() || d.IsAlive()) + meta.PadState = DsState.Connected; + else + meta.PadState = DsState.Reserved; + } + + meta.ConnectionType = (d.getConnectionType() == ConnectionType.USB) ? DsConnection.Usb : DsConnection.Bluetooth; + meta.IsActive = !d.isDS4Idle(); + + if (d.isCharging() && d.getBattery() >= 100) + meta.BatteryStatus = DsBattery.Charged; + else + { + if (d.getBattery() >= 95) + meta.BatteryStatus = DsBattery.Full; + else if (d.getBattery() >= 70) + meta.BatteryStatus = DsBattery.High; + else if (d.getBattery() >= 50) + meta.BatteryStatus = DsBattery.Medium; + else if (d.getBattery() >= 20) + meta.BatteryStatus = DsBattery.Low; + else if (d.getBattery() >= 5) + meta.BatteryStatus = DsBattery.Dying; + else + meta.BatteryStatus = DsBattery.None; + } + + //return meta; + } + + private object busThrLck = new object(); + private bool busThrRunning = false; + private Queue busEvtQueue = new Queue(); + private object busEvtQueueLock = new object(); + public ControlService() + { + Crc32Algorithm.InitializeTable(DS4Device.DefaultPolynomial); + + //sp.Stream = DS4WinWPF.Properties.Resources.EE; + // Cause thread affinity to not be tied to main GUI thread + tempBusThread = new Thread(() => + { + //_udpServer = new UdpServer(GetPadDetailForIdx); + busThrRunning = true; + + while (busThrRunning) + { + lock (busEvtQueueLock) + { + Action tempAct = null; + for (int actInd = 0, actLen = busEvtQueue.Count; actInd < actLen; actInd++) + { + tempAct = busEvtQueue.Dequeue(); + tempAct.Invoke(); + } + } + + lock (busThrLck) + Monitor.Wait(busThrLck); + } + }); + tempBusThread.Priority = ThreadPriority.Normal; + tempBusThread.IsBackground = true; + tempBusThread.Start(); + //while (_udpServer == null) + //{ + // Thread.SpinWait(500); + //} + + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + MappedState[i] = new DS4State(); + CurrentState[i] = new DS4State(); + TempState[i] = new DS4State(); + PreviousState[i] = new DS4State(); + ExposedState[i] = new DS4StateExposed(CurrentState[i]); + } + + outputslotMan = new OutputSlotManager(); + DS4Devices.RequestElevation += DS4Devices_RequestElevation; + } + + private void DS4Devices_RequestElevation(RequestElevationArgs args) + { + // Launches an elevated child process to re-enable device + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exelocation); + startInfo.Verb = "runas"; + startInfo.Arguments = "re-enabledevice " + args.InstanceId; + + try + { + Process child = Process.Start(startInfo); + if (!child.WaitForExit(30000)) + { + child.Kill(); + } + else + { + args.StatusCode = child.ExitCode; + } + child.Dispose(); + } + catch { } + } + + public void LaunchHidGuardHelper() + { + if (Global.hidguardInstalled) + { + LogDebug("HidGuardian in use. Launching HidGuardHelper."); + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exedirpath + "\\HidGuardHelper.exe"); + startInfo.Verb = "runas"; + startInfo.Arguments = Process.GetCurrentProcess().Id.ToString(); + startInfo.WorkingDirectory = Global.exedirpath; + try + { Process tempProc = Process.Start(startInfo); tempProc.Dispose(); } + catch { } + } + } + + private void TestQueueBus(Action temp) + { + lock (busEvtQueueLock) + { + busEvtQueue.Enqueue(temp); + } + + lock (busThrLck) + Monitor.Pulse(busThrLck); + } + + public void ChangeUDPStatus(bool state, bool openPort=true) + { + if (state && _udpServer == null) + { + udpChangeStatus = true; + TestQueueBus(() => + { + _udpServer = new UdpServer(GetPadDetailForIdx); + if (openPort) + { + // Change thread affinity of object to have normal priority + Task.Run(() => + { + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + }).Wait(); + } + + udpChangeStatus = false; + }); + } + else if (!state && _udpServer != null) + { + TestQueueBus(() => + { + udpChangeStatus = true; + _udpServer.Stop(); + _udpServer = null; + AppLogger.LogToGui("Closed UDP server", false); + udpChangeStatus = false; + }); + } + } + + public void ChangeMotionEventStatus(bool state) + { + IEnumerable devices = DS4Devices.getDS4Controllers(); + if (state) + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + } + else + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + } + } + + private bool udpChangeStatus = false; + public bool changingUDPPort = false; + public async void UseUDPPort() + { + changingUDPPort = true; + IEnumerable devices = DS4Devices.getDS4Controllers(); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + + await Task.Delay(100); + + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + + changingUDPPort = false; + } + + private void WarnExclusiveModeFailure(DS4Device device) + { + if (DS4Devices.isExclusiveMode && !device.isExclusive()) + { + string message = DS4WinWPF.Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.getMacAddress()) + " " + + DS4WinWPF.Properties.Resources.QuitOtherPrograms; + LogDebug(message, true); + AppLogger.LogToTray(message, true); + } + } + + private void startViGEm() + { + tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } }); + tempThread.Priority = ThreadPriority.AboveNormal; + tempThread.IsBackground = true; + tempThread.Start(); + while (tempThread.IsAlive) + { + Thread.SpinWait(500); + } + + tempThread = null; + } + + private void stopViGEm() + { + if (vigemTestClient != null) + { + vigemTestClient.Dispose(); + vigemTestClient = null; + } + } + + public void PluginOutDev(int index, DS4Device device) + { + OutContType contType = Global.OutContType[index]; + if (useDInputOnly[index]) + { + if (contType == OutContType.X360) + { + LogDebug("Plugging in X360 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.X360; + + //Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient); + Xbox360OutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, vigemTestClient) + as Xbox360OutDevice; + //outputDevices[index] = tempXbox; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) => + { + SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); + }; + tempXbox.cont.FeedbackReceived += p; + tempXbox.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempXbox, index, outputDevices); + //tempXbox.Connect(); + //LogDebug("X360 Controller #" + (index + 1) + " connected"); + } + else if (contType == OutContType.DS4) + { + LogDebug("Plugging in DS4 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.DS4; + //DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient); + DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient) + as DS4OutDevice; + //outputDevices[index] = tempDS4; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => + { + //bool useRumble = false; bool useLight = false; + byte largeMotor = args.LargeMotor; + byte smallMotor = args.SmallMotor; + SetDevRumble(device, largeMotor, smallMotor, devIndex); + //DS4Color color = new DS4Color(args.LightbarColor.Red, + // args.LightbarColor.Green, + // args.LightbarColor.Blue); + ///*Console.WriteLine("IN EVENT"); + //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", + // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToLongTimeString()); + // */ + //if (largeMotor != 0 || smallMotor != 0) + //{ + // useRumble = true; + //} + + //if (color.red != 0 || color.green != 0 || color.blue != 0) + //{ + // useLight = true; + //} + + //if (!useRumble && !useLight) + //{ + // //Console.WriteLine("Fallback"); + // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) + // { + // useRumble = true; + // } + // /*else if (device.LightBarColor.red != 0 || + // device.LightBarColor.green != 0 || + // device.LightBarColor.blue != 0) + // { + // useLight = true; + // } + // */ + //} + + //if (useRumble) + //{ + // //Console.WriteLine("Perform rumble"); + // SetDevRumble(device, largeMotor, smallMotor, devIndex); + //} + + //if (useLight) + //{ + // //Console.WriteLine("Change lightbar color"); + // DS4HapticState haptics = new DS4HapticState + // { + // LightBarColor = color, + // }; + // device.SetHapticState(ref haptics); + //} + + //Console.WriteLine(); + }; + tempDS4.cont.FeedbackReceived += p; + tempDS4.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempDS4, index, outputDevices); + //tempDS4.Connect(); + //LogDebug("DS4 Controller #" + (index + 1) + " connected"); + } + } + + useDInputOnly[index] = false; + } + + public void UnplugOutDev(int index, DS4Device device, bool immediate = false) + { + if (!useDInputOnly[index]) + { + //OutContType contType = Global.OutContType[index]; + string tempType = outputDevices[index].GetDeviceType(); + LogDebug("Unplugging " + tempType + " Controller for input #" + (index + 1), false); + OutputDevice dev = outputDevices[index]; + outputDevices[index] = null; + activeOutDevType[index] = OutContType.None; + outputslotMan.DeferredRemoval(dev, index, outputDevices, immediate); + //dev.Disconnect(); + //LogDebug(tempType + " Controller # " + (index + 1) + " unplugged"); + useDInputOnly[index] = true; + } + } + + public bool Start(bool showlog = true) + { + startViGEm(); + if (vigemTestClient != null) + //if (x360Bus.Open() && x360Bus.Start()) + { + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.Starting); + + LogDebug($"Connection to ViGEmBus {Global.vigembusVersion} established"); + + DS4Devices.isExclusiveMode = getUseExclusiveMode(); + //uiContext = tempui as SynchronizationContext; + if (showlog) + { + LogDebug(DS4WinWPF.Properties.Resources.SearchingController); + LogDebug(DS4Devices.isExclusiveMode ? DS4WinWPF.Properties.Resources.UsingExclusive : DS4WinWPF.Properties.Resources.UsingShared); + } + + if (isUsingUDPServer() && _udpServer == null) + { + ChangeUDPStatus(true, false); + while (udpChangeStatus == true) + { + Thread.SpinWait(500); + } + } + + try + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //int ind = 0; + DS4LightBar.defaultLight = false; + //foreach (DS4Device device in devices) + + //for (int i = 0, devCount = devices.Count(); i < devCount; i++) + int i = 0; + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext(); i++) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + + DS4Controllers[i] = device; + slotManager.AddController(device, i); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[i] = new Mouse(i, device); + + device.SetMouseProxy(touchPad[i]); + + if (!useTempProfile[i]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[i] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[i] = true; + } + else + { + ProfilePath[i] = OlderProfilePath[i]; + Global.linkedProfileCheck[i] = false; + } + + LoadProfile(i, false, this, false, false); + } + + device.LightBarColor = getMainColor(i); + + if (!getDInputOnly(i) && device.isSynced()) + { + //useDInputOnly[i] = false; + PluginOutDev(i, device); + + } + else + { + useDInputOnly[i] = true; + Global.activeOutDevType[i] = OutContType.None; + } + + int tempIdx = i; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + TouchPadOn(i, device); + CheckProfileOptions(i, device, true); + device.StartUpdate(); + //string filename = ProfilePath[ind]; + //ind++; + if (showlog) + { + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[i] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (i + 1).ToString()).Replace("*Profile name*", ProfilePath[i]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (i + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + } + + if (i >= 4) // out of Xinput devices! + break; + } + } + catch (Exception e) + { + LogDebug(e.Message, true); + AppLogger.LogToTray(e.Message, true); + } + + running = true; + + if (_udpServer != null) + { + //var UDP_SERVER_PORT = 26760; + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + } + } + else + { + string logMessage = string.Empty; + if (!vigemInstalled) + { + logMessage = "ViGEmBus is not installed"; + } + else + { + logMessage = "Could not connect to ViGEmBus. Please check the status of the System device in Device Manager and if Visual C++ 2017 Redistributable is installed."; + } + + LogDebug(logMessage); + AppLogger.LogToTray(logMessage); + } + + runHotPlug = true; + ServiceStarted?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + private void CheckQuickCharge(object sender, EventArgs e) + { + DS4Device device = sender as DS4Device; + if (device.ConnectionType == ConnectionType.BT && getQuickCharge() && + device.Charging) + { + device.DisconnectBT(); + } + } + + public void PrepareAbort() + { + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + tempDevice.PrepareAbort(); + } + } + } + + public bool Stop(bool showlog = true) + { + if (running) + { + running = false; + runHotPlug = false; + PreServiceStop?.Invoke(this, EventArgs.Empty); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingX360); + + LogDebug("Closing connection to ViGEmBus"); + + bool anyUnplugged = false; + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + if ((DCBTatStop && !tempDevice.isCharging()) || suspending) + { + if (tempDevice.getConnectionType() == ConnectionType.BT) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectBT(true); + } + else if (tempDevice.getConnectionType() == ConnectionType.SONYWA) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectDongle(true); + } + else + { + tempDevice.StopUpdate(); + } + } + else + { + DS4LightBar.forcelight[i] = false; + DS4LightBar.forcedFlash[i] = 0; + DS4LightBar.defaultLight = true; + DS4LightBar.updateLightBar(DS4Controllers[i], i); + tempDevice.IsRemoved = true; + tempDevice.StopUpdate(); + DS4Devices.RemoveDevice(tempDevice); + Thread.Sleep(50); + } + + CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. + OutputDevice tempout = outputDevices[i]; + if (tempout != null) + { + UnplugOutDev(i, tempDevice, true); + anyUnplugged = true; + } + + //outputDevices[i] = null; + //useDInputOnly[i] = true; + //Global.activeOutDevType[i] = OutContType.None; + DS4Controllers[i] = null; + touchPad[i] = null; + lag[i] = false; + inWarnMonitor[i] = false; + } + } + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingDS4); + + DS4Devices.stopControllers(); + slotManager.ClearControllerList(); + + if (_udpServer != null) + ChangeUDPStatus(false); + //_udpServer.Stop(); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppedDS4Windows); + + while (outputslotMan.RunningQueue) + { + Thread.SpinWait(500); + } + + if (anyUnplugged) + { + Thread.Sleep(OutputSlotManager.DELAY_TIME); + } + + stopViGEm(); + } + + runHotPlug = false; + ServiceStopped?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + public bool HotPlug() + { + if (running) + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //foreach (DS4Device device in devices) + //for (int i = 0, devlen = devices.Count(); i < devlen; i++) + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext();) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + + if (device.isDisconnectingStatus()) + continue; + + if (((Func)delegate + { + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] != null && + DS4Controllers[Index].getMacAddress() == device.getMacAddress()) + return true; + } + + return false; + })()) + { + continue; + } + + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] == null) + { + //LogDebug(DS4WinWPF.Properties.Resources.FoundController + device.getMacAddress() + " (" + device.getConnectionType() + ")"); + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + DS4Controllers[Index] = device; + slotManager.AddController(device, Index); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[Index] = new Mouse(Index, device); + + device.SetMouseProxy(touchPad[Index]); + + if (!useTempProfile[Index]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[Index] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[Index] = true; + } + else + { + ProfilePath[Index] = OlderProfilePath[Index]; + Global.linkedProfileCheck[Index] = false; + } + + LoadProfile(Index, false, this, false, false); + } + + device.LightBarColor = getMainColor(Index); + + int tempIdx = Index; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + if (!getDInputOnly(Index) && device.isSynced()) + { + //useDInputOnly[Index] = false; + PluginOutDev(Index, device); + } + else + { + useDInputOnly[Index] = true; + Global.activeOutDevType[Index] = OutContType.None; + } + + TouchPadOn(Index, device); + CheckProfileOptions(Index, device); + device.StartUpdate(); + + //string filename = Path.GetFileName(ProfilePath[Index]); + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + + HotplugController?.Invoke(this, device, Index); + + break; + } + } + } + } + + return true; + } + + private void CheckProfileOptions(int ind, DS4Device device, bool startUp=false) + { + device.setIdleTimeout(getIdleDisconnectTimeout(ind)); + device.setBTPollRate(getBTPollRate(ind)); + touchPad[ind].ResetTrackAccel(getTrackballFriction(ind)); + + if (!startUp) + { + CheckLauchProfileOption(ind, device); + } + } + + private void CheckLauchProfileOption(int ind, DS4Device device) + { + string programPath = LaunchProgram[ind]; + if (programPath != string.Empty) + { + System.Diagnostics.Process[] localAll = System.Diagnostics.Process.GetProcesses(); + bool procFound = false; + for (int procInd = 0, procsLen = localAll.Length; !procFound && procInd < procsLen; procInd++) + { + try + { + string temp = localAll[procInd].MainModule.FileName; + if (temp == programPath) + { + procFound = true; + } + } + // Ignore any process for which this information + // is not exposed + catch { } + } + + if (!procFound) + { + Task processTask = new Task(() => + { + Thread.Sleep(5000); + System.Diagnostics.Process tempProcess = new System.Diagnostics.Process(); + tempProcess.StartInfo.FileName = programPath; + tempProcess.StartInfo.WorkingDirectory = new FileInfo(programPath).Directory.ToString(); + //tempProcess.StartInfo.UseShellExecute = false; + try { tempProcess.Start(); } + catch { } + }); + + processTask.Start(); + } + } + } + + public void TouchPadOn(int ind, DS4Device device) + { + Mouse tPad = touchPad[ind]; + //ITouchpadBehaviour tPad = touchPad[ind]; + device.Touchpad.TouchButtonDown += tPad.touchButtonDown; + device.Touchpad.TouchButtonUp += tPad.touchButtonUp; + device.Touchpad.TouchesBegan += tPad.touchesBegan; + device.Touchpad.TouchesMoved += tPad.touchesMoved; + device.Touchpad.TouchesEnded += tPad.touchesEnded; + device.Touchpad.TouchUnchanged += tPad.touchUnchanged; + //device.Touchpad.PreTouchProcess += delegate { touchPad[ind].populatePriorButtonStates(); }; + device.Touchpad.PreTouchProcess += (sender, args) => { touchPad[ind].populatePriorButtonStates(); }; + device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; + //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + } + + public string getDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + string battery; + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Charged; + else + battery = DS4WinWPF.Properties.Resources.Charging.Replace("*number*", d.getBattery().ToString()); + } + else + { + battery = DS4WinWPF.Properties.Resources.Battery.Replace("*number*", d.getBattery().ToString()); + } + + return d.getMacAddress() + " (" + d.getConnectionType() + "), " + battery; + //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); + } + else + return string.Empty; + } + + public string getDS4MacAddress(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + return d.getMacAddress(); + } + else + return string.Empty; + } + + public string getShortDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return (d.getConnectionType() + " " + battery); + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + public string getDS4Battery(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return battery; + } + else + return DS4WinWPF.Properties.Resources.NA; + } + + public string getDS4Status(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + return d.getConnectionType() + ""; + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + protected void On_SerialChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + OnDeviceSerialChange(this, ind, device.getMacAddress()); + } + } + + protected void On_SyncChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + bool synced = device.isSynced(); + + if (!synced) + { + if (!useDInputOnly[ind]) + { + Global.activeOutDevType[ind] = OutContType.None; + UnplugOutDev(ind, device); + } + } + else + { + if (!getDInputOnly(ind)) + { + PluginOutDev(ind, device); + } + } + } + } + + //Called when DS4 is disconnected or timed out + protected virtual void On_DS4Removal(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4Controllers.Length; ind == -1 && i < arlength; i++) + { + if (DS4Controllers[i] != null && device.getMacAddress() == DS4Controllers[i].getMacAddress()) + ind = i; + } + + if (ind != -1) + { + bool removingStatus = false; + lock (device.removeLocker) + { + if (!device.IsRemoving) + { + removingStatus = true; + device.IsRemoving = true; + } + } + + if (removingStatus) + { + CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. + if (!useDInputOnly[ind]) + { + UnplugOutDev(ind, device); + } + + // Use Task to reset device synth state and commit it + Task.Run(() => + { + Mapping.Commit(ind); + }).Wait(); + + string removed = DS4WinWPF.Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind + 1).ToString()); + if (device.getBattery() <= 20 && + device.getConnectionType() == ConnectionType.BT && !device.isCharging()) + { + removed += ". " + DS4WinWPF.Properties.Resources.ChargeController; + } + + LogDebug(removed); + AppLogger.LogToTray(removed); + /*Stopwatch sw = new Stopwatch(); + sw.Start(); + while (sw.ElapsedMilliseconds < XINPUT_UNPLUG_SETTLE_TIME) + { + // Use SpinWait to keep control of current thread. Using Sleep could potentially + // cause other events to get run out of order + System.Threading.Thread.SpinWait(500); + } + sw.Stop(); + */ + + device.IsRemoved = true; + device.Synced = false; + DS4Controllers[ind] = null; + slotManager.RemoveController(device, ind); + touchPad[ind] = null; + lag[ind] = false; + inWarnMonitor[ind] = false; + useDInputOnly[ind] = true; + Global.activeOutDevType[ind] = OutContType.None; + /*uiContext?.Post(new SendOrPostCallback((state) => + { + OnControllerRemoved(this, ind); + }), null); + */ + //Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); + } + } + } + + public bool[] lag = new bool[4] { false, false, false, false }; + public bool[] inWarnMonitor = new bool[4] { false, false, false, false }; + private byte[] currentBattery = new byte[4] { 0, 0, 0, 0 }; + private bool[] charging = new bool[4] { false, false, false, false }; + private string[] tempStrings = new string[4] { string.Empty, string.Empty, string.Empty, string.Empty }; + + // Called every time a new input report has arrived + //protected virtual void On_Report(object sender, EventArgs e, int ind) + protected virtual void On_Report(DS4Device device, EventArgs e, int ind) + { + //DS4Device device = (DS4Device)sender; + + if (ind != -1) + { + if (getFlushHIDQueue(ind)) + device.FlushHID(); + + string devError = tempStrings[ind] = device.error; + if (!string.IsNullOrEmpty(devError)) + { + LogDebug(devError); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LogDebug(devError); + }), null); + */ + } + + if (inWarnMonitor[ind]) + { + int flashWhenLateAt = getFlashWhenLateAt(); + if (!lag[ind] && device.Latency >= flashWhenLateAt) + { + lag[ind] = true; + LagFlashWarning(ind, true); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, true); + }), null); + */ + } + else if (lag[ind] && device.Latency < flashWhenLateAt) + { + lag[ind] = false; + LagFlashWarning(ind, false); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, false); + }), null); + */ + } + } + else + { + if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) + { + inWarnMonitor[ind] = true; + } + } + + device.getCurrentState(CurrentState[ind]); + DS4State cState = CurrentState[ind]; + DS4State pState = device.getPreviousStateRef(); + //device.getPreviousState(PreviousState[ind]); + //DS4State pState = PreviousState[ind]; + + if (device.firstReport && device.IsAlive()) + { + device.firstReport = false; + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + OnDeviceStatusChanged(this, ind); + }), null); + */ + } + //else if (pState.Battery != cState.Battery || device.oldCharging != device.isCharging()) + //{ + // byte tempBattery = currentBattery[ind] = cState.Battery; + // bool tempCharging = charging[ind] = device.isCharging(); + // /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + // { + // OnBatteryStatusChange(this, ind, tempBattery, tempCharging); + // }), null); + // */ + //} + + if (getEnableTouchToggle(ind)) + CheckForTouchToggle(ind, cState, pState); + + cState = Mapping.SetCurveAndDeadzone(ind, cState, TempState[ind]); + + if (!recordingMacro && (useTempProfile[ind] || + containsCustomAction(ind) || containsCustomExtras(ind) || + getProfileActionCount(ind) > 0)) + { + Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); + cState = MappedState[ind]; + } + + if (!useDInputOnly[ind]) + { + outputDevices[ind]?.ConvertandSendReport(cState, ind); + //testNewReport(ref x360reports[ind], cState, ind); + //x360controls[ind]?.SendReport(x360reports[ind]); + + //x360Bus.Parse(cState, processingData[ind].Report, ind); + // We push the translated Xinput state, and simultaneously we + // pull back any possible rumble data coming from Xinput consumers. + /*if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) + { + byte Big = processingData[ind].Rumble[3]; + byte Small = processingData[ind].Rumble[4]; + + if (processingData[ind].Rumble[1] == 0x08) + { + SetDevRumble(device, Big, Small, ind); + } + } + */ + } + else + { + // UseDInputOnly profile may re-map sixaxis gyro sensor values as a VJoy joystick axis (steering wheel emulation mode using VJoy output device). Handle this option because VJoy output works even in USeDInputOnly mode. + // If steering wheel emulation uses LS/RS/R2/L2 output axies then the profile should NOT use UseDInputOnly option at all because those require a virtual output device. + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(ind); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: break; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + break; + } + } + + // Output any synthetic events. + Mapping.Commit(ind); + + // Update the GUI/whatever. + DS4LightBar.updateLightBar(device, ind); + } + } + + public void LagFlashWarning(int ind, bool on) + { + if (on) + { + lag[ind] = true; + LogDebug(DS4WinWPF.Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); + if (getFlashWhenLate()) + { + DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; + DS4LightBar.forcedColor[ind] = color; + DS4LightBar.forcedFlash[ind] = 2; + DS4LightBar.forcelight[ind] = true; + } + } + else + { + lag[ind] = false; + LogDebug(DS4WinWPF.Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); + DS4LightBar.forcelight[ind] = false; + DS4LightBar.forcedFlash[ind] = 0; + } + } + + public DS4Controls GetActiveInputControl(int ind) + { + DS4State cState = CurrentState[ind]; + DS4StateExposed eState = ExposedState[ind]; + Mouse tp = touchPad[ind]; + DS4Controls result = DS4Controls.None; + + if (DS4Controllers[ind] != null) + { + if (Mapping.getBoolButtonMapping(cState.Cross)) + result = DS4Controls.Cross; + else if (Mapping.getBoolButtonMapping(cState.Circle)) + result = DS4Controls.Circle; + else if (Mapping.getBoolButtonMapping(cState.Triangle)) + result = DS4Controls.Triangle; + else if (Mapping.getBoolButtonMapping(cState.Square)) + result = DS4Controls.Square; + else if (Mapping.getBoolButtonMapping(cState.L1)) + result = DS4Controls.L1; + else if (Mapping.getBoolTriggerMapping(cState.L2)) + result = DS4Controls.L2; + else if (Mapping.getBoolButtonMapping(cState.L3)) + result = DS4Controls.L3; + else if (Mapping.getBoolButtonMapping(cState.R1)) + result = DS4Controls.R1; + else if (Mapping.getBoolTriggerMapping(cState.R2)) + result = DS4Controls.R2; + else if (Mapping.getBoolButtonMapping(cState.R3)) + result = DS4Controls.R3; + else if (Mapping.getBoolButtonMapping(cState.DpadUp)) + result = DS4Controls.DpadUp; + else if (Mapping.getBoolButtonMapping(cState.DpadDown)) + result = DS4Controls.DpadDown; + else if (Mapping.getBoolButtonMapping(cState.DpadLeft)) + result = DS4Controls.DpadLeft; + else if (Mapping.getBoolButtonMapping(cState.DpadRight)) + result = DS4Controls.DpadRight; + else if (Mapping.getBoolButtonMapping(cState.Share)) + result = DS4Controls.Share; + else if (Mapping.getBoolButtonMapping(cState.Options)) + result = DS4Controls.Options; + else if (Mapping.getBoolButtonMapping(cState.PS)) + result = DS4Controls.PS; + else if (Mapping.getBoolAxisDirMapping(cState.LX, true)) + result = DS4Controls.LXPos; + else if (Mapping.getBoolAxisDirMapping(cState.LX, false)) + result = DS4Controls.LXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.LY, true)) + result = DS4Controls.LYPos; + else if (Mapping.getBoolAxisDirMapping(cState.LY, false)) + result = DS4Controls.LYNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RX, true)) + result = DS4Controls.RXPos; + else if (Mapping.getBoolAxisDirMapping(cState.RX, false)) + result = DS4Controls.RXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RY, true)) + result = DS4Controls.RYPos; + else if (Mapping.getBoolAxisDirMapping(cState.RY, false)) + result = DS4Controls.RYNeg; + else if (Mapping.getBoolTouchMapping(tp.leftDown)) + result = DS4Controls.TouchLeft; + else if (Mapping.getBoolTouchMapping(tp.rightDown)) + result = DS4Controls.TouchRight; + else if (Mapping.getBoolTouchMapping(tp.multiDown)) + result = DS4Controls.TouchMulti; + else if (Mapping.getBoolTouchMapping(tp.upperDown)) + result = DS4Controls.TouchUpper; + } + + return result; + } + + public bool[] touchreleased = new bool[4] { true, true, true, true }, + touchslid = new bool[4] { false, false, false, false }; + + protected virtual void CheckForTouchToggle(int deviceID, DS4State cState, DS4State pState) + { + if (!getUseTPforControls(deviceID) && cState.Touch1 && pState.PS) + { + if (GetTouchActive(deviceID) && touchreleased[deviceID]) + { + TouchActive[deviceID] = false; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + touchreleased[deviceID] = false; + } + else if (touchreleased[deviceID]) + { + TouchActive[deviceID] = true; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + touchreleased[deviceID] = false; + } + } + else + touchreleased[deviceID] = true; + } + + public virtual void StartTPOff(int deviceID) + { + if (deviceID < 4) + { + TouchActive[deviceID] = false; + } + } + + public virtual string TouchpadSlide(int ind) + { + DS4State cState = CurrentState[ind]; + string slidedir = "none"; + if (DS4Controllers[ind] != null && cState.Touch2 && + !(touchPad[ind].dragging || touchPad[ind].dragging2)) + { + if (touchPad[ind].slideright && !touchslid[ind]) + { + slidedir = "right"; + touchslid[ind] = true; + } + else if (touchPad[ind].slideleft && !touchslid[ind]) + { + slidedir = "left"; + touchslid[ind] = true; + } + else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) + { + slidedir = ""; + touchslid[ind] = false; + } + } + + return slidedir; + } + + public virtual void LogDebug(String Data, bool warning = false) + { + //Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); + if (Debug != null) + { + DebugEventArgs args = new DebugEventArgs(Data, warning); + OnDebug(this, args); + } + } + + public virtual void OnDebug(object sender, DebugEventArgs args) + { + if (Debug != null) + Debug(this, args); + } + + // sets the rumble adjusted with rumble boost. General use method + public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) + { + if (deviceNum < 4) + { + DS4Device device = DS4Controllers[deviceNum]; + if (device != null) + SetDevRumble(device, heavyMotor, lightMotor, deviceNum); + //device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + } + + // sets the rumble adjusted with rumble boost. Method more used for + // report handling. Avoid constant checking for a device. + public void SetDevRumble(DS4Device device, + byte heavyMotor, byte lightMotor, int deviceNum) + { + byte boost = getRumbleBoost(deviceNum); + uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; + if (lightBoosted > 255) + lightBoosted = 255; + uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; + if (heavyBoosted > 255) + heavyBoosted = 255; + + device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + + public DS4State getDS4State(int ind) + { + return CurrentState[ind]; + } + + public DS4State getDS4StateMapped(int ind) + { + return MappedState[ind]; + } + + public DS4State getDS4StateTemp(int ind) + { + return TempState[ind]; + } + } +} diff --git a/DS4Windows/DS4Control/ControllerSlotManager.cs b/DS4Windows/DS4Control/ControllerSlotManager.cs new file mode 100644 index 0000000000..df4cffbf83 --- /dev/null +++ b/DS4Windows/DS4Control/ControllerSlotManager.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace DS4Windows +{ + public class ControllerSlotManager + { + private List controllerColl; + public List ControllerColl { get => controllerColl; set => controllerColl = value; } + + private Dictionary controllerDict; + private Dictionary reverseControllerDict; + public Dictionary ControllerDict { get => controllerDict; } + public Dictionary ReverseControllerDict { get => reverseControllerDict; } + + public ControllerSlotManager() + { + controllerColl = new List(); + controllerDict = new Dictionary(); + reverseControllerDict = new Dictionary(); + } + + public void AddController(DS4Device device, int slotIdx) + { + controllerColl.Add(device); + controllerDict.Add(slotIdx, device); + reverseControllerDict.Add(device, slotIdx); + } + + public void RemoveController(DS4Device device, int slotIdx) + { + controllerColl.Remove(device); + controllerDict.Remove(slotIdx); + reverseControllerDict.Remove(device); + } + + public void ClearControllerList() + { + controllerColl.Clear(); + controllerDict.Clear(); + reverseControllerDict.Clear(); + } + } +} diff --git a/DS4Windows/DS4Control/DS4LightBar.cs b/DS4Windows/DS4Control/DS4LightBar.cs index 98483a38a7..c7fb7c005f 100644 --- a/DS4Windows/DS4Control/DS4LightBar.cs +++ b/DS4Windows/DS4Control/DS4LightBar.cs @@ -1,17 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Drawing; using static System.Math; using static DS4Windows.Global; +using System.Diagnostics; + namespace DS4Windows { public class DS4LightBar { private readonly static byte[/* Light On duration */, /* Light Off duration */] BatteryIndicatorDurations = { - { 0, 0 }, // 0 is for "charging" OR anything sufficiently-"charged" + { 28, 252 }, // on 10% of the time at 0 { 28, 252 }, { 56, 224 }, { 84, 196 }, @@ -19,195 +18,335 @@ public class DS4LightBar { 140, 140 }, { 168, 112 }, { 196, 84 }, - { 224, 56}, // on 80% of the time at 80, etc. - { 252, 28 } // on 90% of the time at 90 + { 224, 56 }, // on 80% of the time at 80, etc. + { 252, 28 }, // on 90% of the time at 90 + { 0, 0 } // use on 100%. 0 is for "charging" OR anything sufficiently-"charged" }; + static double[] counters = new double[4] { 0, 0, 0, 0 }; - public static double[] fadetimer = new double[4] { 0, 0, 0, 0 }; + public static Stopwatch[] fadewatches = new Stopwatch[4] + { new Stopwatch(), new Stopwatch(), new Stopwatch(), new Stopwatch() }; + static bool[] fadedirection = new bool[4] { false, false, false, false }; - static DateTime oldnow = DateTime.UtcNow; + static DateTime[] oldnow = new DateTime[4] + { DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow }; + public static bool[] forcelight = new bool[4] { false, false, false, false }; public static DS4Color[] forcedColor = new DS4Color[4]; public static byte[] forcedFlash = new byte[4]; - public static void updateLightBar(DS4Device device, int deviceNum, DS4State cState, DS4StateExposed eState, Mouse tp) + internal const int PULSE_FLASH_DURATION = 2000; + internal const double PULSE_FLASH_SEGMENTS = PULSE_FLASH_DURATION / 40; + internal const int PULSE_CHARGING_DURATION = 4000; + internal const double PULSE_CHARGING_SEGMENTS = (PULSE_CHARGING_DURATION / 40) - 2; + + public static void updateLightBar(DS4Device device, int deviceNum) { - DS4Color color; - if (!defualtLight && !forcelight[deviceNum]) + DS4Color color = new DS4Color(); + bool useForceLight = forcelight[deviceNum]; + LightbarSettingInfo lightbarSettingInfo = getLightbarSettingsInfo(deviceNum); + LightbarDS4WinInfo lightModeInfo = lightbarSettingInfo.ds4winSettings; + bool useLightRoutine = lightbarSettingInfo.mode == LightbarMode.DS4Win; + //bool useLightRoutine = false; + if (!defaultLight && !useForceLight && useLightRoutine) { - if (UseCustomLed[deviceNum]) + if (lightModeInfo.useCustomLed) { - if (LedAsBatteryIndicator[deviceNum]) + if (lightModeInfo.ledAsBattery) { - DS4Color fullColor = CustomColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, device.Battery); + ref DS4Color fullColor = ref lightModeInfo.m_CustomLed; // ref getCustomColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else - color = CustomColor[deviceNum]; + color = lightModeInfo.m_CustomLed; //getCustomColor(deviceNum); } else { - if (Rainbow[deviceNum] > 0) - {// Display rainbow + double rainbow = lightModeInfo.rainbow;// getRainbow(deviceNum); + if (rainbow > 0) + { + // Display rainbow DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion + if (now >= oldnow[deviceNum] + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion { - oldnow = now; - if (device.Charging) - counters[deviceNum] -= 1.5 * 3 / Rainbow[deviceNum]; + oldnow[deviceNum] = now; + if (device.isCharging()) + counters[deviceNum] -= 1.5 * 3 / rainbow; else - counters[deviceNum] += 1.5 * 3 / Rainbow[deviceNum]; + counters[deviceNum] += 1.5 * 3 / rainbow; } + if (counters[deviceNum] < 0) counters[deviceNum] = 180000; - if (counters[deviceNum] > 180000) + else if (counters[deviceNum] > 180000) counters[deviceNum] = 0; - if (LedAsBatteryIndicator[deviceNum]) - color = HuetoRGB((float)counters[deviceNum] % 360, (byte)(2.55 * device.Battery)); + + double maxSat = lightModeInfo.maxRainbowSat; // GetMaxSatRainbow(deviceNum); + if (lightModeInfo.ledAsBattery) + { + byte useSat = (byte)(maxSat == 1.0 ? + device.getBattery() * 2.55 : + device.getBattery() * 2.55 * maxSat); + color = HuetoRGB((float)counters[deviceNum] % 360, useSat); + } else - color = HuetoRGB((float)counters[deviceNum] % 360, 255); + color = HuetoRGB((float)counters[deviceNum] % 360, + (byte)(maxSat == 1.0 ? 255 : 255 * maxSat)); } - else if (LedAsBatteryIndicator[deviceNum]) + else if (lightModeInfo.ledAsBattery) { - //if (device.Charging == false || device.Battery >= 100) // when charged, don't show the charging animation - { - DS4Color fullColor = MainColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, (uint)device.Battery); - } + ref DS4Color fullColor = ref lightModeInfo.m_Led; //ref getMainColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else { - color = MainColor[deviceNum]; + color = getMainColor(deviceNum); } - } - if (device.Battery <= FlashAt[deviceNum] && !defualtLight && !device.Charging) + if (device.getBattery() <= lightModeInfo.flashAt && !defaultLight && !device.isCharging()) { - if (!(FlashColor[deviceNum].red == 0 && - FlashColor[deviceNum].green == 0 && - FlashColor[deviceNum].blue == 0)) - color = FlashColor[deviceNum]; - if (FlashType[deviceNum] == 1) + ref DS4Color flashColor = ref lightModeInfo.m_FlashLed; //ref getFlashColor(deviceNum); + if (!(flashColor.red == 0 && + flashColor.green == 0 && + flashColor.blue == 0)) + color = flashColor; + + if (lightModeInfo.flashType == 1) { - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 100) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += 1; + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= 1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = 100.0 * (elapsed / PULSE_FLASH_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = (0 - 100.0) * (elapsed / PULSE_FLASH_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color tempCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref tempCol, ratio); } } - if (IdleDisconnectTimeout[deviceNum] > 0 && LedAsBatteryIndicator[deviceNum] && (!device.Charging || device.Battery >= 100)) - {//Fade lightbar by idle time + int idleDisconnectTimeout = getIdleDisconnectTimeout(deviceNum); + if (idleDisconnectTimeout > 0 && lightModeInfo.ledAsBattery && + (!device.isCharging() || device.getBattery() >= 100)) + { + // Fade lightbar by idle time TimeSpan timeratio = new TimeSpan(DateTime.UtcNow.Ticks - device.lastActive.Ticks); double botratio = timeratio.TotalMilliseconds; - double topratio = TimeSpan.FromSeconds(IdleDisconnectTimeout[deviceNum]).TotalMilliseconds; - double ratio = ((botratio / topratio) * 100); - if (ratio >= 50 && ratio <= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), (uint)((ratio - 50) * 2)); - else if (ratio >= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), 100); + double topratio = TimeSpan.FromSeconds(idleDisconnectTimeout).TotalMilliseconds; + double ratio = 100.0 * (botratio / topratio), elapsed = ratio; + if (ratio >= 50.0 && ratio < 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, + (uint)(-100.0 * (elapsed = 0.02 * (ratio - 50.0)) * (elapsed - 2.0))); + } + else if (ratio >= 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, 100.0); + } + } - if (device.Charging && device.Battery < 100) - switch (ChargingType[deviceNum]) + + if (device.isCharging() && device.getBattery() < 100) + { + switch (lightModeInfo.chargingType) { case 1: - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 105) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += .1; + { + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= .1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = 100.0 * (elapsed / PULSE_CHARGING_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = (0 - 100.0) * (elapsed / PULSE_CHARGING_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, ratio); break; + } case 2: - counters[deviceNum] += .167; + { + counters[deviceNum] += 0.167; color = HuetoRGB((float)counters[deviceNum] % 360, 255); break; + } case 3: - color = ChargingColor[deviceNum]; - break; - default: + { + color = lightModeInfo.m_ChargingLed; //getChargingColor(deviceNum); break; + } + default: break; } + } } - else if (forcelight[deviceNum]) + else if (useForceLight) { color = forcedColor[deviceNum]; + useLightRoutine = true; } else if (shuttingdown) + { color = new DS4Color(0, 0, 0); - else + useLightRoutine = true; + } + else if (useLightRoutine) { - if (device.ConnectionType == ConnectionType.BT) + if (device.getConnectionType() == ConnectionType.BT) color = new DS4Color(32, 64, 64); else color = new DS4Color(0, 0, 0); } - bool distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); - if (distanceprofile && !defualtLight) - { //Thing I did for Distance - float rumble = device.LeftHeavySlowRumble / 2.55f; - byte max = Max(color.red, Max(color.green, color.blue)); - if (device.LeftHeavySlowRumble > 100) - color = getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), rumble); - else - color = getTransitionedColor(color, getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), 39.6078f), device.LeftHeavySlowRumble); - } - DS4HapticState haptics = new DS4HapticState - { - LightBarColor = color - }; - if (haptics.IsLightBarSet()) + + if (useLightRoutine) { - if (forcelight[deviceNum] && forcedFlash[deviceNum] > 0) + bool distanceprofile = DistanceProfiles[deviceNum] || tempprofileDistance[deviceNum]; + //distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); + if (distanceprofile && !defaultLight) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); - haptics.LightBarExplicitlyOff = true; + // Thing I did for Distance + float rumble = device.getLeftHeavySlowRumble() / 2.55f; + byte max = Max(color.red, Max(color.green, color.blue)); + if (device.getLeftHeavySlowRumble() > 100) + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + color = getTransitionedColor(ref maxCol, ref redCol, rumble); + } + else + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + DS4Color tempCol = getTransitionedColor(ref maxCol, + ref redCol, 39.6078f); + color = getTransitionedColor(ref color, ref tempCol, + device.getLeftHeavySlowRumble()); + } } - else if (device.Battery <= FlashAt[deviceNum] && FlashType[deviceNum] == 0 && !defualtLight && !device.Charging) + + DS4HapticState haptics = new DS4HapticState { - int level = device.Battery / 10; - //if (level >= 10) - //level = 0; // all values of ~0% or >~100% are rendered the same - haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; - haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; - } - else if (distanceprofile && device.LeftHeavySlowRumble > 155) //also part of Distance + LightBarColor = color + }; + + if (haptics.IsLightBarSet()) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.LeftHeavySlowRumble + 265)); - haptics.LightBarExplicitlyOff = true; + if (useForceLight && forcedFlash[deviceNum] > 0) + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); + haptics.LightBarExplicitlyOff = true; + } + else if (device.getBattery() <= lightModeInfo.flashAt && lightModeInfo.flashType == 0 && !defaultLight && !device.isCharging()) + { + int level = device.getBattery() / 10; + if (level >= 10) + level = 10; // all values of >~100% are rendered the same + + haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; + haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; + } + else if (distanceprofile && device.getLeftHeavySlowRumble() > 155) //also part of Distance + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.getLeftHeavySlowRumble() + 265)); + haptics.LightBarExplicitlyOff = true; + } + else + { + //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; + haptics.LightBarExplicitlyOff = true; + } } else { - //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; haptics.LightBarExplicitlyOff = true; } + + byte tempLightBarOnDuration = device.getLightBarOnDuration(); + if (tempLightBarOnDuration != haptics.LightBarFlashDurationOn && tempLightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + + device.SetHapticState(ref haptics); + //device.pushHapticState(ref haptics); } - else - { - haptics.LightBarExplicitlyOff = true; - } - if (device.LightBarOnDuration != haptics.LightBarFlashDurationOn && device.LightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - if (device.LightBarOnDuration == 1) //helps better reset the color - System.Threading.Thread.Sleep(5); - device.pushHapticState(haptics); } - public static bool defualtLight = false, shuttingdown = false; + public static bool defaultLight = false, shuttingdown = false; public static DS4Color HuetoRGB(float hue, byte sat) { diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs new file mode 100644 index 0000000000..33cbdb8caf --- /dev/null +++ b/DS4Windows/DS4Control/DS4OutDevice.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; +using Nefarius.ViGEm.Client.Targets; +using Nefarius.ViGEm.Client.Targets.DualShock4; + +namespace DS4Windows +{ + class DS4OutDevice : OutputDevice + { + public const string devtype = "DS4"; + + public DualShock4Controller cont; + private DualShock4Report report; + public DualShock4FeedbackReceivedEventHandler forceFeedbackCall; + + public DS4OutDevice(ViGEmClient client) + { + cont = new DualShock4Controller(client); + report = new DualShock4Report(); + } + + public override void ConvertandSendReport(DS4State state, int device) + { + DualShock4Buttons tempButtons = 0; + DualShock4DPadValues tempDPad = DualShock4DPadValues.None; + DualShock4SpecialButtons tempSpecial = 0; + + unchecked + { + if (state.Share) tempButtons |= DualShock4Buttons.Share; + if (state.L3) tempButtons |= DualShock4Buttons.ThumbLeft; + if (state.R3) tempButtons |= DualShock4Buttons.ThumbRight; + if (state.Options) tempButtons |= DualShock4Buttons.Options; + + if (state.DpadUp && state.DpadRight) tempDPad = DualShock4DPadValues.Northeast; + else if (state.DpadUp && state.DpadLeft) tempDPad = DualShock4DPadValues.Northwest; + else if (state.DpadUp) tempDPad = DualShock4DPadValues.North; + else if (state.DpadRight && state.DpadDown) tempDPad = DualShock4DPadValues.Southeast; + else if (state.DpadRight) tempDPad = DualShock4DPadValues.East; + else if (state.DpadDown && state.DpadLeft) tempDPad = DualShock4DPadValues.Southwest; + else if (state.DpadDown) tempDPad = DualShock4DPadValues.South; + else if (state.DpadLeft) tempDPad = DualShock4DPadValues.West; + + /*if (state.DpadUp) tempDPad = (state.DpadRight) ? DualShock4DPadValues.Northeast : DualShock4DPadValues.North; + if (state.DpadRight) tempDPad = (state.DpadDown) ? DualShock4DPadValues.Southeast : DualShock4DPadValues.East; + if (state.DpadDown) tempDPad = (state.DpadLeft) ? DualShock4DPadValues.Southwest : DualShock4DPadValues.South; + if (state.DpadLeft) tempDPad = (state.DpadUp) ? DualShock4DPadValues.Northwest : DualShock4DPadValues.West; + */ + + if (state.L1) tempButtons |= DualShock4Buttons.ShoulderLeft; + if (state.R1) tempButtons |= DualShock4Buttons.ShoulderRight; + //if (state.L2Btn) tempButtons |= DualShock4Buttons.TriggerLeft; + //if (state.R2Btn) tempButtons |= DualShock4Buttons.TriggerRight; + if (state.L2 > 0) tempButtons |= DualShock4Buttons.TriggerLeft; + if (state.R2 > 0) tempButtons |= DualShock4Buttons.TriggerRight; + + if (state.Triangle) tempButtons |= DualShock4Buttons.Triangle; + if (state.Circle) tempButtons |= DualShock4Buttons.Circle; + if (state.Cross) tempButtons |= DualShock4Buttons.Cross; + if (state.Square) tempButtons |= DualShock4Buttons.Square; + if (state.PS) tempSpecial |= DualShock4SpecialButtons.Ps; + if (state.TouchButton) tempSpecial |= DualShock4SpecialButtons.Touchpad; + //report.SetButtonsFull(tempButtons); + report.Buttons = (ushort)tempButtons; + report.SetDPad(tempDPad); + report.SpecialButtons = (byte)tempSpecial; + } + + + report.LeftTrigger = state.L2; + report.RightTrigger = state.R2; + + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LX: + report.LeftThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LY: + report.LeftThumbX = state.LX; + report.LeftThumbY = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RX: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RY: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = (byte)state.SASteeringWheelEmulationUnit; + break; + + case SASteeringWheelEmulationAxisType.L2R2: + report.LeftTrigger = report.RightTrigger = 0; + if (state.SASteeringWheelEmulationUnit >= 0) report.LeftTrigger = (Byte)state.SASteeringWheelEmulationUnit; + else report.RightTrigger = (Byte)state.SASteeringWheelEmulationUnit; + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + goto case SASteeringWheelEmulationAxisType.None; + + default: + // Should never come here but just in case use the NONE case as default handler.... + goto case SASteeringWheelEmulationAxisType.None; + } + + cont.SendReport(report); + } + + public override void Connect() => cont.Connect(); + public override void Disconnect() + { + if (forceFeedbackCall != null) + { + cont.FeedbackReceived -= forceFeedbackCall; + forceFeedbackCall = null; + } + + cont.Disconnect(); + cont.Dispose(); + cont = null; + } + public override string GetDeviceType() => devtype; + } +} diff --git a/DS4Windows/DS4Control/DS4StateFieldMapping.cs b/DS4Windows/DS4Control/DS4StateFieldMapping.cs new file mode 100644 index 0000000000..d1c12adf10 --- /dev/null +++ b/DS4Windows/DS4Control/DS4StateFieldMapping.cs @@ -0,0 +1,164 @@ + +namespace DS4Windows +{ + public class DS4StateFieldMapping + { + public enum ControlType: int { Unknown = 0, Button, AxisDir, Trigger, Touch, GyroDir, SwipeDir } + + public bool[] buttons = new bool[(int)DS4Controls.SwipeDown + 1]; + public byte[] axisdirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public byte[] triggers = new byte[(int)DS4Controls.SwipeDown + 1]; + public int[] gryodirs = new int[(int)DS4Controls.SwipeDown + 1]; + public byte[] swipedirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public bool[] swipedirbools = new bool[(int)DS4Controls.SwipeDown + 1]; + public bool touchButton = false; + + public static ControlType[] mappedType = new ControlType[38] { ControlType.Unknown, // DS4Controls.None + ControlType.AxisDir, // DS4Controls.LXNeg + ControlType.AxisDir, // DS4Controls.LXPos + ControlType.AxisDir, // DS4Controls.LYNeg + ControlType.AxisDir, // DS4Controls.LYPos + ControlType.AxisDir, // DS4Controls.RXNeg + ControlType.AxisDir, // DS4Controls.RXPos + ControlType.AxisDir, // DS4Controls.RYNeg + ControlType.AxisDir, // DS4Controls.RYPos + ControlType.Button, // DS4Controls.L1 + ControlType.Trigger, // DS4Controls.L2 + ControlType.Button, // DS4Controls.L3 + ControlType.Button, // DS4Controls.R1 + ControlType.Trigger, // DS4Controls.R2 + ControlType.Button, // DS4Controls.R3 + ControlType.Button, // DS4Controls.Square + ControlType.Button, // DS4Controls.Triangle + ControlType.Button, // DS4Controls.Circle + ControlType.Button, // DS4Controls.Cross + ControlType.Button, // DS4Controls.DpadUp + ControlType.Button, // DS4Controls.DpadRight + ControlType.Button, // DS4Controls.DpadDown + ControlType.Button, // DS4Controls.DpadLeft + ControlType.Button, // DS4Controls.PS + ControlType.Touch, // DS4Controls.TouchLeft + ControlType.Touch, // DS4Controls.TouchUpper + ControlType.Touch, // DS4Controls.TouchMulti + ControlType.Touch, // DS4Controls.TouchRight + ControlType.Button, // DS4Controls.Share + ControlType.Button, // DS4Controls.Options + ControlType.GyroDir, // DS4Controls.GyroXPos + ControlType.GyroDir, // DS4Controls.GyroXNeg + ControlType.GyroDir, // DS4Controls.GyroZPos + ControlType.GyroDir, // DS4Controls.GyroZNeg + ControlType.SwipeDir, // DS4Controls.SwipeLeft + ControlType.SwipeDir, // DS4Controls.SwipeRight + ControlType.SwipeDir, // DS4Controls.SwipeUp + ControlType.SwipeDir, // DS4Controls.SwipeDown + }; + + public DS4StateFieldMapping() + { + } + + public DS4StateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse=false) + { + populateFieldMapping(cState, exposeState, tp, priorMouse); + } + + public void populateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse = false) + { + unchecked + { + axisdirs[(int)DS4Controls.LXNeg] = cState.LX; + axisdirs[(int)DS4Controls.LXPos] = cState.LX; + axisdirs[(int)DS4Controls.LYNeg] = cState.LY; + axisdirs[(int)DS4Controls.LYPos] = cState.LY; + + axisdirs[(int)DS4Controls.RXNeg] = cState.RX; + axisdirs[(int)DS4Controls.RXPos] = cState.RX; + axisdirs[(int)DS4Controls.RYNeg] = cState.RY; + axisdirs[(int)DS4Controls.RYPos] = cState.RY; + + triggers[(int)DS4Controls.L2] = cState.L2; + triggers[(int)DS4Controls.R2] = cState.R2; + + buttons[(int)DS4Controls.L1] = cState.L1; + buttons[(int)DS4Controls.L3] = cState.L3; + buttons[(int)DS4Controls.R1] = cState.R1; + buttons[(int)DS4Controls.R3] = cState.R3; + + buttons[(int)DS4Controls.Cross] = cState.Cross; + buttons[(int)DS4Controls.Triangle] = cState.Triangle; + buttons[(int)DS4Controls.Circle] = cState.Circle; + buttons[(int)DS4Controls.Square] = cState.Square; + buttons[(int)DS4Controls.PS] = cState.PS; + buttons[(int)DS4Controls.Options] = cState.Options; + buttons[(int)DS4Controls.Share] = cState.Share; + + buttons[(int)DS4Controls.DpadUp] = cState.DpadUp; + buttons[(int)DS4Controls.DpadRight] = cState.DpadRight; + buttons[(int)DS4Controls.DpadDown] = cState.DpadDown; + buttons[(int)DS4Controls.DpadLeft] = cState.DpadLeft; + + buttons[(int)DS4Controls.TouchLeft] = tp != null ? (!priorMouse ? tp.leftDown : tp.priorLeftDown) : false; + buttons[(int)DS4Controls.TouchRight] = tp != null ? (!priorMouse ? tp.rightDown : tp.priorRightDown) : false; + buttons[(int)DS4Controls.TouchUpper] = tp != null ? (!priorMouse ? tp.upperDown : tp.priorUpperDown) : false; + buttons[(int)DS4Controls.TouchMulti] = tp != null ? (!priorMouse ? tp.multiDown : tp.priorMultiDown) : false; + + int sixAxisX = -exposeState.getOutputAccelX(); + gryodirs[(int)DS4Controls.GyroXPos] = sixAxisX > 0 ? sixAxisX : 0; + gryodirs[(int)DS4Controls.GyroXNeg] = sixAxisX < 0 ? sixAxisX : 0; + + int sixAxisZ = exposeState.getOutputAccelZ(); + gryodirs[(int)DS4Controls.GyroZPos] = sixAxisZ > 0 ? sixAxisZ : 0; + gryodirs[(int)DS4Controls.GyroZNeg] = sixAxisZ < 0 ? sixAxisZ : 0; + + swipedirs[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeftB : tp.priorSwipeLeftB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRightB : tp.priorSwipeRightB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUpB : tp.priorSwipeUpB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDownB : tp.priorSwipeDownB) : (byte)0; + + swipedirbools[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeft : tp.priorSwipeLeft) : false; + swipedirbools[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRight : tp.priorSwipeRight) : false; + swipedirbools[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUp : tp.priorSwipeUp) : false; + swipedirbools[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDown : tp.priorSwipeDown) : false; + touchButton = cState.TouchButton; + } + } + + public void populateState(DS4State state) + { + unchecked + { + state.LX = axisdirs[(int)DS4Controls.LXNeg]; + state.LX = axisdirs[(int)DS4Controls.LXPos]; + state.LY = axisdirs[(int)DS4Controls.LYNeg]; + state.LY = axisdirs[(int)DS4Controls.LYPos]; + + state.RX = axisdirs[(int)DS4Controls.RXNeg]; + state.RX = axisdirs[(int)DS4Controls.RXPos]; + state.RY = axisdirs[(int)DS4Controls.RYNeg]; + state.RY = axisdirs[(int)DS4Controls.RYPos]; + + state.L2 = triggers[(int)DS4Controls.L2]; + state.R2 = triggers[(int)DS4Controls.R2]; + + state.L1 = buttons[(int)DS4Controls.L1]; + state.L3 = buttons[(int)DS4Controls.L3]; + state.R1 = buttons[(int)DS4Controls.R1]; + state.R3 = buttons[(int)DS4Controls.R3]; + + state.Cross = buttons[(int)DS4Controls.Cross]; + state.Triangle = buttons[(int)DS4Controls.Triangle]; + state.Circle = buttons[(int)DS4Controls.Circle]; + state.Square = buttons[(int)DS4Controls.Square]; + state.PS = buttons[(int)DS4Controls.PS]; + state.Options = buttons[(int)DS4Controls.Options]; + state.Share = buttons[(int)DS4Controls.Share]; + + state.DpadUp = buttons[(int)DS4Controls.DpadUp]; + state.DpadRight = buttons[(int)DS4Controls.DpadRight]; + state.DpadDown = buttons[(int)DS4Controls.DpadDown]; + state.DpadLeft = buttons[(int)DS4Controls.DpadLeft]; + state.TouchButton = touchButton; + } + } + } +} diff --git a/DS4Windows/DS4Control/ITouchpadBehaviour.cs b/DS4Windows/DS4Control/ITouchpadBehaviour.cs index 24a5ee74ff..80a4343c44 100644 --- a/DS4Windows/DS4Control/ITouchpadBehaviour.cs +++ b/DS4Windows/DS4Control/ITouchpadBehaviour.cs @@ -1,18 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { interface ITouchpadBehaviour { - void touchesBegan(object sender, TouchpadEventArgs arg); - void touchesMoved(object sender, TouchpadEventArgs arg); - void touchButtonUp(object sender, TouchpadEventArgs arg); - void touchButtonDown(object sender, TouchpadEventArgs arg); - void touchesEnded(object sender, TouchpadEventArgs arg); - void sixaxisMoved(object sender, SixAxisEventArgs unused); - void touchUnchanged(object sender, EventArgs unused); + void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg); + void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs unused); + void touchUnchanged(DS4Touchpad sender, EventArgs unused); } } diff --git a/DS4Windows/DS4Control/InputMethods.cs b/DS4Windows/DS4Control/InputMethods.cs index 278f86e312..09f4c845f5 100644 --- a/DS4Windows/DS4Control/InputMethods.cs +++ b/DS4Windows/DS4Control/InputMethods.cs @@ -1,210 +1,209 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Runtime.InteropServices; +using System.Security; + namespace DS4Windows { + [SuppressUnmanagedCodeSecurity] class InputMethods { - private static INPUT[] sendInputs = new INPUT[2]; // will allow for keyboard + mouse/tablet input within one SendInput call, or two mouse events - private static object lockob = new object(); public static void MoveCursorBy(int x, int y) { - lock (lockob) + if (x != 0 || y != 0) { - if (x != 0 || y != 0) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = MOUSEEVENTF_MOVE; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = x; - sendInputs[0].Data.Mouse.Y = y; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_MOVE; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = x; + temp.Data.Mouse.Y = y; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } } public static void MouseWheel(int vertical, int horizontal) { - lock (lockob) + INPUT[] tempInput = new INPUT[2]; + uint inputs = 0; + ref INPUT temp = ref tempInput[inputs]; + if (vertical != 0) { - uint inputs = 0; - if (vertical != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_WHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)vertical; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - if (horizontal != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)horizontal; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - SendInput(inputs, sendInputs, (int)inputs * Marshal.SizeOf(sendInputs[0])); + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_WHEEL; + temp.Data.Mouse.MouseData = (uint)vertical; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } - } - public static void MouseEvent(uint mouseButton) - { - lock (lockob) + if (horizontal != 0) { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + temp = ref tempInput[inputs]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; + temp.Data.Mouse.MouseData = (uint)horizontal; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } + + SendInput(inputs, tempInput, (int)inputs * Marshal.SizeOf(tempInput[0])); } - public static void MouseEvent(uint mouseButton, int type) + public static void MouseEvent(uint mouseButton) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = (uint)type; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performLeftClick() + public static void MouseEvent(uint mouseButton, int type) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = (uint)type; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performRightClick() + public static void performSCKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performMiddleClick() + public static void performKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = 1; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performFourthClick() + public static void performSCKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_XBUTTONDOWN | MOUSEEVENTF_XBUTTONUP; - sendInputs[0].Data.Mouse.MouseData = 1; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performSCKeyPress(ushort key) + + public static void performKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags | KEYEVENTF_KEYUP; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performKeyPress(ushort key) + private static ushort scancodeFromVK(uint vkey) { - lock (lockob) + ushort scancode = 0; + if (vkey == VK_PAUSE) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = 1; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + // MapVirtualKey does not work with VK_PAUSE + scancode = 0x45; } - } - - public static void performSCKeyRelease(ushort key) - { - lock (lockob) + else { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + scancode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC); } - } - public static void performKeyRelease(ushort key) - { - lock (lockob) + switch (vkey) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_INSERT: + case VK_DELETE: + case VK_DIVIDE: + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_VOLUME_MUTE: + case VK_VOLUME_DOWN: + case VK_VOLUME_UP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_LAUNCH_MEDIA_SELECT: + case VK_BROWSER_HOME: + case VK_LAUNCH_MAIL: + case VK_LAUNCH_APP1: + case VK_LAUNCH_APP2: + case VK_APPS: + { + scancode |= (ushort)EXTENDED_FLAG; // set extended bit + break; + } } + + return scancode; } /// @@ -277,7 +276,14 @@ internal struct MOUSEINPUT MOUSEEVENTF_XBUTTONDOWN = 128, MOUSEEVENTF_XBUTTONUP = 256, KEYEVENTF_EXTENDEDKEY = 1, KEYEVENTF_KEYUP = 2, MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x1000, MOUSEEVENTF_MIDDLEWDOWN = 0x0020, MOUSEEVENTF_MIDDLEWUP = 0x0040, - KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004; + KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004, EXTENDED_FLAG = 0x100; + + internal const uint VK_PAUSE = 0x13, VK_LEFT = 0x25, VK_UP = 0x26, VK_RIGHT = 0x27, VK_DOWN = 0x28, + VK_PRIOR = 0x21, VK_NEXT = 0x22, VK_END = 0x23, VK_HOME = 0x24, VK_INSERT = 0x2D, VK_DELETE = 0x2E, VK_APPS = 0x5D, + VK_DIVIDE = 0x6F, VK_NUMLOCK = 0x90, VK_RCONTROL = 0xA3, VK_RMENU = 0xA5, VK_BROWSER_HOME = 0xAC, + VK_VOLUME_MUTE = 0xAD, VK_VOLUME_DOWN = 0xAE, VK_VOLUME_UP = 0xAF, + VK_MEDIA_NEXT_TRACK = 0xB0, VK_MEDIA_PREV_TRACK = 0xB1, VK_MEDIA_STOP = 0xB2, VK_MEDIA_PLAY_PAUSE = 0xB3, + VK_LAUNCH_MAIL = 0xB4, VK_LAUNCH_MEDIA_SELECT = 0xB5, VK_LAUNCH_APP1 = 0xB6, VK_LAUNCH_APP2 = 0xB7; [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputs); @@ -285,6 +291,7 @@ internal struct MOUSEINPUT private static extern ushort MapVirtualKey(uint uCode, uint uMapType); [DllImport("user32.dll", SetLastError = true)] static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + //Not used, just here public static void DownKeys(ushort key) { @@ -301,5 +308,5 @@ public static void ReleaseKeys(ushort key) keybd_event((byte)key, 0, (int)KEYEVENTF_KEYUP, 0); } } - } + diff --git a/DS4Windows/DS4Control/Log.cs b/DS4Windows/DS4Control/Log.cs index f581f77e1f..a2d8815d08 100644 --- a/DS4Windows/DS4Control/Log.cs +++ b/DS4Windows/DS4Control/Log.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { - public class Log + public class AppLogger { public static event EventHandler TrayIconLog; public static event EventHandler GuiLog; diff --git a/DS4Windows/DS4Control/MacroParser.cs b/DS4Windows/DS4Control/MacroParser.cs new file mode 100644 index 0000000000..0950eb7863 --- /dev/null +++ b/DS4Windows/DS4Control/MacroParser.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace DS4Windows +{ + public class MacroParser + { + private bool loaded; + private List macroSteps; + private int[] inputMacro; + private Dictionary keydown = new Dictionary(); + public static Dictionary macroInputNames = new Dictionary() + { + [256] = "Left Mouse Button", [257] = "Right Mouse Button", + [258] = "Middle Mouse Button", [259] = "4th Mouse Button", + [260] = "5th Mouse Button", [261] = "A Button", + [262] = "B Button", [263] = "X Button", + [264] = "Y Button", [265] = "Start", + [266] = "Back", [267] = "Up Button", + [268] = "Down Button", [269] = "Left Button", + [270] = "Right Button", [271] = "Guide", + [272] = "Left Bumper", [273] = "Right Bumper", + [274] = "Left Trigger", [275] = "Right Trigger", + [276] = "Left Stick", [277] = "Right Stick", + [278] = "LS Right", [279] = "LS Left", + [280] = "LS Down", [281] = "LS Up", + [282] = "RS Right", [283] = "RS Left", + [284] = "RS Down", [285] = "RS Up", + }; + + public List MacroSteps { get => macroSteps; } + + public MacroParser(int[] macro) + { + macroSteps = new List(); + inputMacro = macro; + } + + public void LoadMacro() + { + if (loaded) + { + return; + } + + keydown.Clear(); + for(int i = 0; i < inputMacro.Length; i++) + { + int value = inputMacro[i]; + MacroStep step = ParseStep(value); + macroSteps.Add(step); + } + + loaded = true; + } + + public List GetMacroStrings() + { + if (!loaded) + { + LoadMacro(); + } + + List result = new List(); + foreach(MacroStep step in macroSteps) + { + result.Add(step.Name); + } + + return result; + } + + private MacroStep ParseStep(int value) + { + string name = string.Empty; + MacroStep.StepType type = MacroStep.StepType.ActDown; + MacroStep.StepOutput outType = MacroStep.StepOutput.Key; + + if (value >= 1000000000) + { + outType = MacroStep.StepOutput.Lightbar; + if (value > 1000000000) + { + type = MacroStep.StepType.ActDown; + string lb = value.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + name = $"Lightbar Color: {r},{g},{b}"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Reset Lightbar"; + } + } + else if (value >= 1000000) + { + outType = MacroStep.StepOutput.Rumble; + if (value > 1000000) + { + type = MacroStep.StepType.ActDown; + string r = value.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + name = $"Rumble {heavy}, {light} ({Math.Round((heavy * .75f + light * .25f) / 2.55f, 1)}%)"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Stop Rumble"; + } + } + else if (value >= 300) // ints over 300 used to delay + { + type = MacroStep.StepType.Wait; + outType = MacroStep.StepOutput.None; + name = $"Wait {(value - 300).ToString()} ms"; + } + else + { + // anything above 255 is not a key value + outType = value <= 255 ? MacroStep.StepOutput.Key : MacroStep.StepOutput.Button; + keydown.TryGetValue(value, out bool isdown); + if (!isdown) + { + type = MacroStep.StepType.ActDown; + keydown.Add(value, true); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + else + { + type = MacroStep.StepType.ActUp; + keydown.Remove(value); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + } + + MacroStep step = new MacroStep(value, name, type, outType); + return step; + } + + public void Reset() + { + loaded = false; + } + } + + public class MacroStep + { + public enum StepType : uint + { + ActDown, + ActUp, + Wait, + } + + public enum StepOutput : uint + { + None, + Key, + Button, + Rumble, + Lightbar, + } + + private string name; + private int value; + private StepType actType; + private StepOutput outputType; + + public string Name + { + get => name; + set + { + name = value; + NameChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler NameChanged; + public int Value + { + get => value; + set + { + this.value = value; + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ValueChanged; + public StepType ActType { get => actType; } + public StepOutput OutputType { get => outputType; } + + public MacroStep(int value, string name, StepType act, StepOutput output) + { + this.value = value; + this.name = name; + actType = act; + outputType = output; + + ValueChanged += MacroStep_ValueChanged; + } + + private void MacroStep_ValueChanged(object sender, EventArgs e) + { + if (actType == StepType.Wait) + { + Name = $"Wait {value-300}ms"; + } + else if (outputType == StepOutput.Rumble) + { + int result = value; + result -= 1000000; + int curHeavy = result / 1000; + int curLight = result - (curHeavy * 1000); + Name = $"Rumble {curHeavy},{curLight}"; + } + else if (outputType == StepOutput.Lightbar) + { + int temp = value - 1000000000; + int r = temp / 1000000; + temp -= (r * 1000000); + int g = temp / 1000; + temp -= (g * 1000); + int b = temp; + Name = $"Lightbar Color: {r},{g},{b}"; + } + } + } +} diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index 24b06ed4d9..3c057c08c1 100644 --- a/DS4Windows/DS4Control/Mapping.cs +++ b/DS4Windows/DS4Control/Mapping.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; using System.Diagnostics; using static DS4Windows.Global; +using System.Drawing; // Point struct + namespace DS4Windows { public class Mapping @@ -33,13 +34,19 @@ public class KeyPresses } public Dictionary keyPresses = new Dictionary(); - public void SavePrevious(bool performClear) + public void SaveToPrevious(bool performClear) { previousClicks = currentClicks; if (performClear) currentClicks.leftCount = currentClicks.middleCount = currentClicks.rightCount = currentClicks.fourthCount = currentClicks.fifthCount = currentClicks.wUpCount = currentClicks.wDownCount = currentClicks.toggleCount = 0; - foreach (KeyPresses kp in keyPresses.Values) + + //foreach (KeyPresses kp in keyPresses.Values) + Dictionary.ValueCollection keyValues = keyPresses.Values; + for (var keyEnum = keyValues.GetEnumerator(); keyEnum.MoveNext();) + //for (int i = 0, kpCount = keyValues.Count; i < kpCount; i++) { + //KeyPresses kp = keyValues.ElementAt(i); + KeyPresses kp = keyEnum.Current; kp.previous = kp.current; if (performClear) { @@ -55,8 +62,128 @@ public class ActionState public bool[] dev = new bool[4]; } + struct ControlToXInput + { + public DS4Controls ds4input; + public DS4Controls xoutput; + + public ControlToXInput(DS4Controls input, DS4Controls output) + { + ds4input = input; xoutput = output; + } + } + + static Queue[] customMapQueue = new Queue[4] + { + new Queue(), new Queue(), + new Queue(), new Queue() + }; + + struct DS4Vector2 + { + public double x; + public double y; + + public DS4Vector2(double x, double y) + { + this.x = x; + this.y = y; + } + } + + class DS4SquareStick + { + public DS4Vector2 current; + public DS4Vector2 squared; + + public DS4SquareStick() + { + current = new DS4Vector2(0.0, 0.0); + squared = new DS4Vector2(0.0, 0.0); + } + + public void CircleToSquare(double roundness) + { + const double PiOverFour = Math.PI / 4.0; + + // Determine the theta angle + double angle = Math.Atan2(current.y, -current.x); + angle += Math.PI; + double cosAng = Math.Cos(angle); + // Scale according to which wall we're clamping to + // X+ wall + if (angle <= PiOverFour || angle > 7.0 * PiOverFour) + { + double tempVal = 1.0 / cosAng; + //Console.WriteLine("1 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y+ wall + else if (angle > PiOverFour && angle <= 3.0 * PiOverFour) + { + double tempVal = 1.0 / Math.Sin(angle); + //Console.WriteLine("2 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // X- wall + else if (angle > 3.0 * PiOverFour && angle <= 5.0 * PiOverFour) + { + double tempVal = -1.0 / cosAng; + //Console.WriteLine("3 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y- wall + else if (angle > 5.0 * PiOverFour && angle <= 7.0 * PiOverFour) + { + double tempVal = -1.0 / Math.Sin(angle); + //Console.WriteLine("4 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + else return; + + //double lengthOld = Math.Sqrt((x * x) + (y * y)); + double length = current.x / cosAng; + //Console.WriteLine("LENGTH TEST ({0}) ({1}) {2}", lengthOld, length, (lengthOld == length).ToString()); + double factor = Math.Pow(length, roundness); + //double ogX = current.x, ogY = current.y; + current.x += (squared.x - current.x) * factor; + current.y += (squared.y - current.y) * factor; + //Console.WriteLine("INPUT: {0} {1} | {2} {3} | {4} {5} | {6} {7}", + // ogX, ogY, current.x, current.y, squared.x, squared.y, length, factor); + } + } + + static DS4SquareStick[] outSqrStk = new DS4SquareStick[4] { new DS4SquareStick(), + new DS4SquareStick(), new DS4SquareStick(), new DS4SquareStick()}; + + public static byte[] gyroStickX = new byte[4] { 128, 128, 128, 128 }; + public static byte[] gyroStickY = new byte[4] { 128, 128, 128, 128 }; + + static ReaderWriterLockSlim syncStateLock = new ReaderWriterLockSlim(); + public static SyntheticState globalState = new SyntheticState(); - public static SyntheticState[] deviceState = { new SyntheticState(), new SyntheticState(), new SyntheticState(), new SyntheticState() }; + public static SyntheticState[] deviceState = new SyntheticState[4] + { new SyntheticState(), new SyntheticState(), new SyntheticState(), + new SyntheticState() }; + + public static DS4StateFieldMapping[] fieldMappings = new DS4StateFieldMapping[4] { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] outputFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] previousFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; // TODO When we disconnect, process a null/dead state to release any keys or buttons. public static DateTime oldnow = DateTime.UtcNow; @@ -64,232 +191,299 @@ public class ActionState private static int wheel = 0, keyshelddown = 0; //mapcustom - public static bool[] pressedonce = new bool[261], macrodone = new bool[34]; + public static bool[] pressedonce = new bool[261], macrodone = new bool[38]; static bool[] macroControl = new bool[25]; + static uint macroCount = 0; + static Dictionary[] macroTaskQueue = new Dictionary[4] { new Dictionary(), new Dictionary(), new Dictionary(), new Dictionary() }; //actions - public static int[] fadetimer = { 0, 0, 0, 0 }; - public static int[] prevFadetimer = { 0, 0, 0, 0 }; + public static int[] fadetimer = new int[4] { 0, 0, 0, 0 }; + public static int[] prevFadetimer = new int[4] { 0, 0, 0, 0 }; public static DS4Color[] lastColor = new DS4Color[4]; public static List actionDone = new List(); - //public static List[] actionDone = { new List(), new List(), new List(), new List() }; - //public static bool[,] actionDone = new bool[4, 50]; public static SpecialAction[] untriggeraction = new SpecialAction[4]; public static DateTime[] nowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; public static DateTime[] oldnowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - public static int[] untriggerindex = { -1, -1, -1, -1 }; - public static DateTime[] oldnowKeyAct = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - private static bool tappedOnce = false, firstTouch = false, secondtouchbegin = false; - private static DateTime pastTime, firstTap, TimeofEnd; + public static int[] untriggerindex = new int[4] { -1, -1, -1, -1 }; + public static DateTime[] oldnowKeyAct = new DateTime[4] { DateTime.MinValue, + DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; + + private static DS4Controls[] shiftTriggerMapping = new DS4Controls[26] { DS4Controls.None, DS4Controls.Cross, DS4Controls.Circle, DS4Controls.Square, + DS4Controls.Triangle, DS4Controls.Options, DS4Controls.Share, DS4Controls.DpadUp, DS4Controls.DpadDown, + DS4Controls.DpadLeft, DS4Controls.DpadRight, DS4Controls.PS, DS4Controls.L1, DS4Controls.R1, DS4Controls.L2, + DS4Controls.R2, DS4Controls.L3, DS4Controls.R3, DS4Controls.TouchLeft, DS4Controls.TouchUpper, DS4Controls.TouchMulti, + DS4Controls.TouchRight, DS4Controls.GyroZNeg, DS4Controls.GyroZPos, DS4Controls.GyroXPos, DS4Controls.GyroXNeg, + }; + + private static int[] ds4ControlMapping = new int[38] { 0, // DS4Control.None + 16, // DS4Controls.LXNeg + 20, // DS4Controls.LXPos + 17, // DS4Controls.LYNeg + 21, // DS4Controls.LYPos + 18, // DS4Controls.RXNeg + 22, // DS4Controls.RXPos + 19, // DS4Controls.RYNeg + 23, // DS4Controls.RYPos + 3, // DS4Controls.L1 + 24, // DS4Controls.L2 + 5, // DS4Controls.L3 + 4, // DS4Controls.R1 + 25, // DS4Controls.R2 + 6, // DS4Controls.R3 + 13, // DS4Controls.Square + 14, // DS4Controls.Triangle + 15, // DS4Controls.Circle + 12, // DS4Controls.Cross + 7, // DS4Controls.DpadUp + 10, // DS4Controls.DpadRight + 8, // DS4Controls.DpadDown + 9, // DS4Controls.DpadLeft + 11, // DS4Controls.PS + 27, // DS4Controls.TouchLeft + 29, // DS4Controls.TouchUpper + 26, // DS4Controls.TouchMulti + 28, // DS4Controls.TouchRight + 1, // DS4Controls.Share + 2, // DS4Controls.Options + 31, // DS4Controls.GyroXPos + 30, // DS4Controls.GyroXNeg + 33, // DS4Controls.GyroZPos + 32, // DS4Controls.GyroZNeg + 34, // DS4Controls.SwipeLeft + 35, // DS4Controls.SwipeRight + 36, // DS4Controls.SwipeUp + 37 // DS4Controls.SwipeDown + }; + + // Define here to save some time processing. + // It is enough to feel a difference during gameplay. + // 201907: Commented out these temp variables because those were not actually used anymore (value was assigned but it was never used anywhere) + //private static int[] rsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //private static int[] lsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //static bool tempBool = false; + //private static double[] tempDoubleArray = new double[4] { 0.0, 0.0, 0.0, 0.0 }; + //private static int[] tempIntArray = new int[4] { 0, 0, 0, 0 }; - //special macros + // Special macros static bool altTabDone = true; - static DateTime altTabNow = DateTime.UtcNow, oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); + static DateTime altTabNow = DateTime.UtcNow, + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); - //mouse + // Mouse public static int mcounter = 34; public static int mouseaccel = 0; public static int prevmouseaccel = 0; private static double horizontalRemainder = 0.0, verticalRemainder = 0.0; + public const int MOUSESPEEDFACTOR = 48; + private const double MOUSESTICKOFFSET = 0.54; public static void Commit(int device) { SyntheticState state = deviceState[device]; - lock (globalState) - { - globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; - globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; - globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; - globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; - globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; - globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; - globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; - globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; - globalState.currentClicks.toggle = state.currentClicks.toggle; - - if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - } - else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - } - - if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - - if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 100); - oldnow = DateTime.UtcNow; - wheel = 100; - } - else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) - wheel = 0; + syncStateLock.EnterWriteLock(); - if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -100); - oldnow = DateTime.UtcNow; - wheel = -100; - } - if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) - wheel = 0; + globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; + globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; + globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; + globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; + globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; + globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; + globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; + globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; + globalState.currentClicks.toggle = state.currentClicks.toggle; + + if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + } + else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + } + + if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + + if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 120); + oldnow = DateTime.UtcNow; + wheel = 120; + } + else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) + wheel = 0; + + if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -120); + oldnow = DateTime.UtcNow; + wheel = -120; } + if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) + wheel = 0; + } - if (wheel != 0) //Continue mouse wheel movement + if (wheel != 0) //Continue mouse wheel movement + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) - { - oldnow = now; - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); - } + oldnow = now; + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); } + } + + // Merge and synthesize all key presses/releases that are present in this device's mapping. + // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule + Dictionary.KeyCollection kvpKeys = state.keyPresses.Keys; + //foreach (KeyValuePair kvp in state.keyPresses) + //for (int i = 0, keyCount = kvpKeys.Count; i < keyCount; i++) + for (var keyEnum = kvpKeys.GetEnumerator(); keyEnum.MoveNext();) + { + //UInt16 kvpKey = kvpKeys.ElementAt(i); + UInt16 kvpKey = keyEnum.Current; + SyntheticState.KeyPresses kvpValue = state.keyPresses[kvpKey]; - // Merge and synthesize all key presses/releases that are present in this device's mapping. - // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule - foreach (KeyValuePair kvp in state.keyPresses) + SyntheticState.KeyPresses gkp; + if (globalState.keyPresses.TryGetValue(kvpKey, out gkp)) { - SyntheticState.KeyPresses gkp; - if (globalState.keyPresses.TryGetValue(kvp.Key, out gkp)) - { - gkp.current.vkCount += kvp.Value.current.vkCount - kvp.Value.previous.vkCount; - gkp.current.scanCodeCount += kvp.Value.current.scanCodeCount - kvp.Value.previous.scanCodeCount; - gkp.current.repeatCount += kvp.Value.current.repeatCount - kvp.Value.previous.repeatCount; - gkp.current.toggle = kvp.Value.current.toggle; - gkp.current.toggleCount += kvp.Value.current.toggleCount - kvp.Value.previous.toggleCount; - } + gkp.current.vkCount += kvpValue.current.vkCount - kvpValue.previous.vkCount; + gkp.current.scanCodeCount += kvpValue.current.scanCodeCount - kvpValue.previous.scanCodeCount; + gkp.current.repeatCount += kvpValue.current.repeatCount - kvpValue.previous.repeatCount; + gkp.current.toggle = kvpValue.current.toggle; + gkp.current.toggleCount += kvpValue.current.toggleCount - kvpValue.previous.toggleCount; + } + else + { + gkp = new SyntheticState.KeyPresses(); + gkp.current = kvpValue.current; + globalState.keyPresses[kvpKey] = gkp; + } + if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) + { + if (gkp.current.scanCodeCount != 0) + InputMethods.performSCKeyPress(kvpKey); else + InputMethods.performKeyPress(kvpKey); + } + else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC + InputMethods.performSCKeyRelease(kvpKey); + else + InputMethods.performKeyRelease(kvpKey); + } + else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + { + if (gkp.current.scanCodeCount != 0) { - gkp = new SyntheticState.KeyPresses(); - gkp.current = kvp.Value.current; - globalState.keyPresses[kvp.Key] = gkp; - } - if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) - { - if (gkp.current.scanCodeCount != 0) - InputMethods.performSCKeyPress(kvp.Key); - else - InputMethods.performKeyPress(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performSCKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + else { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - InputMethods.performSCKeyRelease(kvp.Key); - else - InputMethods.performKeyRelease(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + } + else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition + ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms + { + if (keyshelddown == kvpKey) { - if (gkp.current.scanCodeCount != 0) - { - oldnow = DateTime.UtcNow; - InputMethods.performSCKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; - } - else + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) { - oldnow = DateTime.UtcNow; - InputMethods.performKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; + oldnow = now; + pressagain = true; } - } - else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition - ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms - { - if (keyshelddown == kvp.Key) + if (pressagain && gkp.current.scanCodeCount != 0) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { oldnow = now; - pressagain = true; - } - if (pressagain && gkp.current.scanCodeCount != 0) - { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performSCKeyPress(kvp.Key); - } + InputMethods.performSCKeyPress(kvpKey); } - else if (pressagain) + } + else if (pressagain) + { + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performKeyPress(kvp.Key); - } + oldnow = now; + InputMethods.performKeyPress(kvpKey); } } } - if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + } + if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - { - InputMethods.performSCKeyRelease(kvp.Key); - pressagain = false; - } - else - { - InputMethods.performKeyRelease(kvp.Key); - pressagain = false; - } + InputMethods.performSCKeyRelease(kvpKey); + pressagain = false; + } + else + { + InputMethods.performKeyRelease(kvpKey); + pressagain = false; } } - globalState.SavePrevious(false); } - state.SavePrevious(true); + globalState.SaveToPrevious(false); + + syncStateLock.ExitWriteLock(); + state.SaveToPrevious(true); } + public enum Click { None, Left, Middle, Right, Fourth, Fifth, WUP, WDOWN }; public static void MapClick(int device, Click mouseClick) { @@ -316,48 +510,19 @@ public static void MapClick(int device, Click mouseClick) case Click.WDOWN: deviceState[device].currentClicks.wDownCount++; break; + default: break; } } public static int DS4ControltoInt(DS4Controls ctrl) { - switch (ctrl) - { - case DS4Controls.Share: return 1; - case DS4Controls.Options: return 2; - case DS4Controls.L1: return 3; - case DS4Controls.R1: return 4; - case DS4Controls.L3: return 5; - case DS4Controls.R3: return 6; - case DS4Controls.DpadUp: return 7; - case DS4Controls.DpadDown: return 8; - case DS4Controls.DpadLeft: return 9; - case DS4Controls.DpadRight: return 10; - case DS4Controls.PS: return 11; - case DS4Controls.Cross: return 12; - case DS4Controls.Square: return 13; - case DS4Controls.Triangle: return 14; - case DS4Controls.Circle: return 15; - case DS4Controls.LXNeg: return 16; - case DS4Controls.LYNeg: return 17; - case DS4Controls.RXNeg: return 18; - case DS4Controls.RYNeg: return 19; - case DS4Controls.LXPos: return 20; - case DS4Controls.LYPos: return 21; - case DS4Controls.RXPos: return 22; - case DS4Controls.RYPos: return 23; - case DS4Controls.L2: return 24; - case DS4Controls.R2: return 25; - case DS4Controls.TouchMulti: return 26; - case DS4Controls.TouchLeft: return 27; - case DS4Controls.TouchRight: return 28; - case DS4Controls.TouchUpper: return 29; - case DS4Controls.GyroXNeg: return 30; - case DS4Controls.GyroXPos: return 31; - case DS4Controls.GyroZNeg: return 32; - case DS4Controls.GyroZPos: return 33; - } - return 0; + int result = 0; + if (ctrl >= DS4Controls.None && ctrl <= DS4Controls.SwipeDown) + { + result = ds4ControlMapping[(int)ctrl]; + } + + return result; } static double TValue(double value1, double value2, double percent) @@ -365,37 +530,44 @@ static double TValue(double value1, double value2, double percent) percent /= 100f; return value1 * percent + value2 * (1 - percent); } - static double Clamp(double min, double value, double max) + + private static int ClampInt(int min, int value, int max) { - if (value > max) - return max; - else if (value < min) - return min; - else - return value; + return (value < min) ? min : (value > max) ? max : value; } - public static DS4State SetCurveAndDeadzone(int device, DS4State cState) + public static DS4State SetCurveAndDeadzone(int device, DS4State cState, DS4State dState) { - DS4State dState = new DS4State(cState); + double rotation = /*tempDoubleArray[device] =*/ getLSRotation(device); + if (rotation > 0.0 || rotation < 0.0) + cState.rotateLSCoordinates(rotation); + + double rotationRS = /*tempDoubleArray[device] =*/ getRSRotation(device); + if (rotationRS > 0.0 || rotationRS < 0.0) + cState.rotateRSCoordinates(rotationRS); + + cState.CopyTo(dState); + //DS4State dState = new DS4State(cState); int x; int y; int curve; - if (LSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int lsCurve = getLSCurve(device); + if (lsCurve > 0) { x = cState.LX; y = cState.LY; float max = x + y; double curvex; double curvey; - curve = LSCurve[device]; + curve = lsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) { curvex = (x > 127.5f ? Math.Min(x, (x / max) * multimax) : Math.Max(x, (x / max) * multimin)); curvey = (y > 127.5f ? Math.Min(y, (y / max) * multimax) : Math.Max(y, (y / max) * multimin)); - //btnLSTrack.Location = new Point((int)(dpix * curvex / 2.09 + lbLSTrack.Location.X), (int)(dpiy * curvey / 2.09 + lbLSTrack.Location.Y)); } else { @@ -410,17 +582,21 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.LX = (byte)Math.Round(curvex, 0); dState.LY = (byte)Math.Round(curvey, 0); } - if (RSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int rsCurve = getRSCurve(device); + if (rsCurve > 0) { x = cState.RX; y = cState.RY; float max = x + y; double curvex; double curvey; - curve = RSCurve[device]; + curve = rsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) @@ -441,1587 +617,3826 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.RX = (byte)Math.Round(curvex, 0); dState.RY = (byte)Math.Round(curvey, 0); } - double ls = Math.Sqrt(Math.Pow(cState.LX - 127.5f, 2) + Math.Pow(cState.LY - 127.5f, 2)); - //deadzones - if (LSDeadzone[device] > 0 && ls < LSDeadzone[device]) - { - dState.LX = 127; - dState.LY = 127; - } - else if (LSDeadzone[device] < 0 && ls > 127.5f + LSDeadzone[device]) - { - double r = Math.Atan2((dState.LY - 127.5f), (dState.LX - 127.5f)); - dState.LX = (byte)(Math.Cos(r) * (127.5f + LSDeadzone[device]) + 127.5f); - dState.LY = (byte)(Math.Sin(r) * (127.5f + LSDeadzone[device]) + 127.5f); - } - //Console.WriteLine - double rs = Math.Sqrt(Math.Pow(cState.RX - 127.5f, 2) + Math.Pow(cState.RY - 127.5f, 2)); - if (RSDeadzone[device] > 0 && rs < LSDeadzone[device]) - { - dState.RX = 127; - dState.RY = 127; - } - else if (RSDeadzone[device] < 0 && rs > 127.5f + RSDeadzone[device]) - { - double r = Math.Atan2((dState.RY - 127.5f), (dState.RX - 127.5f)); - dState.RX = (byte)(Math.Cos(r) * (127.5f + RSDeadzone[device]) + 127.5f); - dState.RY = (byte)(Math.Sin(r) * (127.5f + RSDeadzone[device]) + 127.5f); - } - if (L2Deadzone[device] > 0 && cState.L2 < L2Deadzone[device]) - dState.L2 = 0; - if (R2Deadzone[device] > 0 && cState.R2 < R2Deadzone[device]) - dState.R2 = 0; - if (LSSens[device] != 1) - { - dState.LX = (byte)Clamp(0, LSSens[device] * (dState.LX - 127) + 127, 255); - dState.LY = (byte)Clamp(0, LSSens[device] * (dState.LY - 127) + 127, 255); - } - if (RSSens[device] != 1) - { - dState.RX = (byte)Clamp(0, RSSens[device] * (dState.RX - 127) + 127, 255); - dState.RY = (byte)Clamp(0, RSSens[device] * (dState.RY - 127) + 127, 255); - } - if (L2Sens[device] != 1) - dState.L2 = (byte)Clamp(0, L2Sens[device] * dState.L2, 255); - if (R2Sens[device] != 1) - dState.R2 = (byte)Clamp(0, R2Sens[device] * dState.R2, 255); - return dState; - } - private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) - { - switch (trigger) - { - case 1: return getBoolMapping(device, DS4Controls.Cross, cState, eState, tp); - case 2: return getBoolMapping(device, DS4Controls.Circle, cState, eState, tp); - case 3: return getBoolMapping(device, DS4Controls.Square, cState, eState, tp); - case 4: return getBoolMapping(device, DS4Controls.Triangle, cState, eState, tp); - case 5: return getBoolMapping(device, DS4Controls.Options, cState, eState, tp); - case 6: return getBoolMapping(device, DS4Controls.Share, cState, eState, tp); - case 7: return getBoolMapping(device, DS4Controls.DpadUp, cState, eState, tp); - case 8: return getBoolMapping(device, DS4Controls.DpadDown, cState, eState, tp); - case 9: return getBoolMapping(device, DS4Controls.DpadLeft, cState, eState, tp); - case 10: return getBoolMapping(device, DS4Controls.DpadRight, cState, eState, tp); - case 11: return getBoolMapping(device, DS4Controls.PS, cState, eState, tp); - case 12: return getBoolMapping(device, DS4Controls.L1, cState, eState, tp); - case 13: return getBoolMapping(device, DS4Controls.R1, cState, eState, tp); - case 14: return getBoolMapping(device, DS4Controls.L2, cState, eState, tp); - case 15: return getBoolMapping(device, DS4Controls.R2, cState, eState, tp); - case 16: return getBoolMapping(device, DS4Controls.L3, cState, eState, tp); - case 17: return getBoolMapping(device, DS4Controls.R3, cState, eState, tp); - case 18: return getBoolMapping(device, DS4Controls.TouchLeft, cState, eState, tp); - case 19: return getBoolMapping(device, DS4Controls.TouchUpper, cState, eState, tp); - case 20: return getBoolMapping(device, DS4Controls.TouchMulti, cState, eState, tp); - case 21: return getBoolMapping(device, DS4Controls.TouchRight, cState, eState, tp); - case 22: return getBoolMapping(device, DS4Controls.GyroZNeg, cState, eState, tp); - case 23: return getBoolMapping(device, DS4Controls.GyroZPos, cState, eState, tp); - case 24: return getBoolMapping(device, DS4Controls.GyroXPos, cState, eState, tp); - case 25: return getBoolMapping(device, DS4Controls.GyroXNeg, cState, eState, tp); - case 26: return cState.Touch1; - default: return false; - } - } - private static X360Controls getX360ControlsByName(string key) - { - X360Controls x3c; - if (Enum.TryParse(key, true, out x3c)) - return x3c; - switch (key) + /*int lsDeadzone = getLSDeadzone(device); + int lsAntiDead = getLSAntiDeadzone(device); + int lsMaxZone = getLSMaxzone(device); + */ + StickDeadZoneInfo lsMod = GetLSDeadInfo(device); + int lsDeadzone = lsMod.deadZone; + int lsAntiDead = lsMod.antiDeadZone; + int lsMaxZone = lsMod.maxZone; + double lsMaxOutput = lsMod.maxOutput; + + if (lsDeadzone > 0 || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) { - case "Back": return X360Controls.Back; - case "Left Stick": return X360Controls.LS; - case "Right Stick": return X360Controls.RS; - case "Start": return X360Controls.Start; - case "Up Button": return X360Controls.DpadUp; - case "Right Button": return X360Controls.DpadRight; - case "Down Button": return X360Controls.DpadDown; - case "Left Button": return X360Controls.DpadLeft; + double lsSquared = Math.Pow(cState.LX - 128f, 2) + Math.Pow(cState.LY - 128f, 2); + double lsDeadzoneSquared = Math.Pow(lsDeadzone, 2); + if (lsDeadzone > 0 && lsSquared <= lsDeadzoneSquared) + { + dState.LX = 128; + dState.LY = 128; + } + else if ((lsDeadzone > 0 && lsSquared > lsDeadzoneSquared) || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) + { + double r = Math.Atan2(-(dState.LY - 128.0), (dState.LX - 128.0)); + double maxXValue = dState.LX >= 128.0 ? 127.0 : -128; + double maxYValue = dState.LY >= 128.0 ? 127.0 : -128; + double ratio = lsMaxZone / 100.0; + double maxOutRatio = lsMaxOutput / 100.0; - case "Left Bumper": return X360Controls.LB; - case "Right Bumper": return X360Controls.RB; - case "Y Button": return X360Controls.Y; - case "B Button": return X360Controls.B; - case "A Button": return X360Controls.A; - case "X Button": return X360Controls.X; + double maxZoneXNegValue = (ratio * -128) + 128; + double maxZoneXPosValue = (ratio * 127) + 128; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.LX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.LY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); - case "Guide": return X360Controls.Guide; - case "Left X-Axis-": return X360Controls.LXNeg; - case "Left Y-Axis-": return X360Controls.LYNeg; - case "Right X-Axis-": return X360Controls.RXNeg; - case "Right Y-Axis-": return X360Controls.RYNeg; + double tempLsXDead = 0.0, tempLsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (lsDeadzone > 0) + { + tempLsXDead = Math.Abs(Math.Cos(r)) * (lsDeadzone / 127.0) * maxXValue; + tempLsYDead = Math.Abs(Math.Sin(r)) * (lsDeadzone / 127.0) * maxYValue; - case "Left X-Axis+": return X360Controls.LXPos; - case "Left Y-Axis+": return X360Controls.LYPos; - case "Right X-Axis+": return X360Controls.RXPos; - case "Right Y-Axis+": return X360Controls.RYPos; - case "Left Trigger": return X360Controls.LT; - case "Right Trigger": return X360Controls.RT; + if (lsSquared > lsDeadzoneSquared) + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = ((currentX - 128.0 - tempLsXDead) / (maxZoneX - tempLsXDead)); + tempOutputY = ((currentY - 128.0 - tempLsYDead) / (maxZoneY - tempLsYDead)); + } + } + else + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; + } - case "Left Mouse Button": return X360Controls.LeftMouse; - case "Right Mouse Button": return X360Controls.RightMouse; - case "Middle Mouse Button": return X360Controls.MiddleMouse; - case "4th Mouse Button": return X360Controls.FourthMouse; - case "5th Mouse Button": return X360Controls.FifthMouse; - case "Mouse Wheel Up": return X360Controls.WUP; - case "Mouse Wheel Down": return X360Controls.WDOWN; - case "Mouse Up": return X360Controls.MouseUp; - case "Mouse Down": return X360Controls.MouseDown; - case "Mouse Left": return X360Controls.MouseLeft; - case "Mouse Right": return X360Controls.MouseRight; - case "Unbound": return X360Controls.Unbound; + if (lsMaxOutput != 100.0) + { + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempLsXAntiDeadPercent = 0.0, tempLsYAntiDeadPercent = 0.0; + if (lsAntiDead > 0) + { + tempLsXAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempLsYAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + if (tempOutputX > 0.0) + { + dState.LX = (byte)((((1.0 - tempLsXAntiDeadPercent) * tempOutputX + tempLsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.LX = 128; + } + + if (tempOutputY > 0.0) + { + dState.LY = (byte)((((1.0 - tempLsYAntiDeadPercent) * tempOutputY + tempLsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.LY = 128; + } + } } - return X360Controls.Unbound; - } - /// - /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. - /// - static bool[] held = new bool[4]; - static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - - MappedState.LX = 127; - MappedState.LY = 127; - MappedState.RX = 127; - MappedState.RY = 127; - int MouseDeltaX = 0; - int MouseDeltaY = 0; - - SyntheticState deviceState = Mapping.deviceState[device]; - if (GetActions().Count > 0 && (ProfileActions[device].Count > 0 || !string.IsNullOrEmpty(tempprofilename[device]))) - MapCustomAction(device, cState, MappedState, eState, tp, ctrl); - if (ctrl.DS4Controllers[device] == null) return; - - cState.CopyTo(MappedState); - - List Cross = new List(); - List Circle = new List(); - List Square = new List(); - List Triangle = new List(); - List Options = new List(); - List Share = new List(); - List DpadUp = new List(); - List DpadDown = new List(); - List DpadLeft = new List(); - List DpadRight = new List(); - List PS = new List(); - List L1 = new List(); - List R1 = new List(); - List L2 = new List(); - List R2 = new List(); - List L3 = new List(); - List R3 = new List(); - List LXN = new List(); - List LXP = new List(); - List LYN = new List(); - List LYP = new List(); - List RXN = new List(); - List RXP = new List(); - List RYN = new List(); - List RYP = new List(); - DS4Controls usingExtra = DS4Controls.None; - foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + /*int rsDeadzone = getRSDeadzone(device); + int rsAntiDead = getRSAntiDeadzone(device); + int rsMaxZone = getRSMaxzone(device); + */ + StickDeadZoneInfo rsMod = GetRSDeadInfo(device); + int rsDeadzone = rsMod.deadZone; + int rsAntiDead = rsMod.antiDeadZone; + int rsMaxZone = rsMod.maxZone; + double rsMaxOutput = rsMod.maxOutput; + + if (rsDeadzone > 0 || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - object action = null; - DS4ControlSettings.ActionType actionType = 0; - DS4KeyType keyType = DS4KeyType.None; - if (dcs.shiftAction != null && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp)) - { - action = dcs.shiftAction; - actionType = dcs.shiftActionType; - keyType = dcs.shiftKeyType; - } - else if (dcs.action != null) + double rsSquared = Math.Pow(cState.RX - 128.0, 2) + Math.Pow(cState.RY - 128.0, 2); + double rsDeadzoneSquared = Math.Pow(rsDeadzone, 2); + if (rsDeadzone > 0 && rsSquared <= rsDeadzoneSquared) { - action = dcs.action; - actionType = dcs.actionType; - keyType = dcs.keyType; + dState.RX = 128; + dState.RY = 128; } - if (action != null) + else if ((rsDeadzone > 0 && rsSquared > rsDeadzoneSquared) || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - if (actionType == DS4ControlSettings.ActionType.Macro) + double r = Math.Atan2(-(dState.RY - 128.0), (dState.RX - 128.0)); + double maxXValue = dState.RX >= 128.0 ? 127 : -128; + double maxYValue = dState.RY >= 128.0 ? 127 : -128; + double ratio = rsMaxZone / 100.0; + double maxOutRatio = rsMaxOutput / 100.0; + + double maxZoneXNegValue = (ratio * -128.0) + 128.0; + double maxZoneXPosValue = (ratio * 127.0) + 128.0; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.RX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.RY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); + + double tempRsXDead = 0.0, tempRsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (rsDeadzone > 0) { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - PlayMacro(device, macroControl, string.Join("/", (int[])action), dcs.control, keyType); - } - else if (!getBoolMapping(device, dcs.control, cState, eState, tp)) + tempRsXDead = Math.Abs(Math.Cos(r)) * (rsDeadzone / 127.0) * maxXValue; + tempRsYDead = Math.Abs(Math.Sin(r)) * (rsDeadzone / 127.0) * maxYValue; + + if (rsSquared > rsDeadzoneSquared) { - EndMacro(device, macroControl, string.Join("/", (int[])action), dcs.control); + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = ((currentX - 128.0 - tempRsXDead) / (maxZoneX - tempRsXDead)); + tempOutputY = ((currentY - 128.0 - tempRsYDead) / (maxZoneY - tempRsYDead)); } } - else if (actionType == DS4ControlSettings.ActionType.Key) + else { - ushort value = ushort.Parse(action.ToString()); - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - SyntheticState.KeyPresses kp; - if (!deviceState.keyPresses.TryGetValue(value, out kp)) - deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); - if (keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (!pressedonce[value]) - { - kp.current.toggle = !kp.current.toggle; - pressedonce[value] = true; - } - kp.current.toggleCount++; - } - kp.current.repeatCount++; - } - else - pressedonce[value] = false; + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; } - else if (actionType == DS4ControlSettings.ActionType.Button) + + if (rsMaxOutput != 100.0) { - int keyvalue = 0; - bool isAnalog = dcs.control.ToString().Contains("LX") || - dcs.control.ToString().Contains("RX") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("R2") || - dcs.control.ToString().Contains("L2") || - dcs.control.ToString().Contains("Gyro"); - switch (getX360ControlsByName(action.ToString())) - { - case X360Controls.A: Cross.Add(dcs.control); break; - case X360Controls.B: Circle.Add(dcs.control); break; - case X360Controls.X: Square.Add(dcs.control); break; - case X360Controls.Y: Triangle.Add(dcs.control); break; - case X360Controls.LB: L1.Add(dcs.control); break; - case X360Controls.LS: L3.Add(dcs.control); break; - case X360Controls.RB: R1.Add(dcs.control); break; - case X360Controls.RS: R3.Add(dcs.control); break; - case X360Controls.DpadUp: DpadUp.Add(dcs.control); break; - case X360Controls.DpadDown: DpadDown.Add(dcs.control); break; - case X360Controls.DpadLeft: DpadLeft.Add(dcs.control); break; - case X360Controls.DpadRight: DpadRight.Add(dcs.control); break; - case X360Controls.Start: Options.Add(dcs.control); break; - case X360Controls.Guide: PS.Add(dcs.control); break; - case X360Controls.Back: Share.Add(dcs.control); break; - case X360Controls.LXNeg: LXN.Add(dcs.control); break; - case X360Controls.LYNeg: LYN.Add(dcs.control); break; - case X360Controls.RXNeg: RXN.Add(dcs.control); break; - case X360Controls.RYNeg: RYN.Add(dcs.control); break; - case X360Controls.LXPos: LXP.Add(dcs.control); break; - case X360Controls.LYPos: LYP.Add(dcs.control); break; - case X360Controls.RXPos: RXP.Add(dcs.control); break; - case X360Controls.RYPos: RYP.Add(dcs.control); break; - case X360Controls.LT: L2.Add(dcs.control); break; - case X360Controls.RT: R2.Add(dcs.control); break; - case X360Controls.LeftMouse: - keyvalue = 256; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.leftCount++; - break; - case X360Controls.RightMouse: - keyvalue = 257; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.rightCount++; - break; - case X360Controls.MiddleMouse: - keyvalue = 258; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.middleCount++; - break; - case X360Controls.FourthMouse: - keyvalue = 259; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fourthCount++; - break; - case X360Controls.FifthMouse: - keyvalue = 260; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fifthCount++; - break; - case X360Controls.WUP: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); - else - deviceState.currentClicks.wUpCount++; - break; - case X360Controls.WDOWN: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); - else - deviceState.currentClicks.wDownCount++; - break; - case X360Controls.MouseUp: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 0); - MouseDeltaY = -Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseDown: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 1); - MouseDeltaY = Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseLeft: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 2); - MouseDeltaX = -Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - case X360Controls.MouseRight: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 3); - MouseDeltaX = Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - } - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - if (!pressedonce[keyvalue]) - { - deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; - pressedonce[keyvalue] = true; - } - deviceState.currentClicks.toggleCount++; - } - else - { - pressedonce[keyvalue] = false; - } - } - resetToDefaultValue(dcs.control, MappedState); // erase default mappings for things that are remapped + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempRsXAntiDeadPercent = 0.0, tempRsYAntiDeadPercent = 0.0; + if (rsAntiDead > 0) + { + tempRsXAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempRsYAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + + if (tempOutputX > 0.0) + { + dState.RX = (byte)((((1.0 - tempRsXAntiDeadPercent) * tempOutputX + tempRsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.RX = 128; + } + + if (tempOutputY > 0.0) + { + dState.RY = (byte)((((1.0 - tempRsYAntiDeadPercent) * tempOutputY + tempRsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.RY = 128; } } + } - if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + /*byte l2Deadzone = getL2Deadzone(device); + int l2AntiDeadzone = getL2AntiDeadzone(device); + int l2Maxzone = getL2Maxzone(device); + */ + + TriggerDeadZoneZInfo l2ModInfo = GetL2ModInfo(device); + byte l2Deadzone = l2ModInfo.deadZone; + int l2AntiDeadzone = l2ModInfo.antiDeadZone; + int l2Maxzone = l2ModInfo.maxZone; + double l2MaxOutput = l2ModInfo.maxOutput; + if (l2Deadzone > 0 || l2AntiDeadzone > 0 || l2Maxzone != 100 || l2MaxOutput != 100.0) + { + double tempL2Output = cState.L2 / 255.0; + double tempL2AntiDead = 0.0; + double ratio = l2Maxzone / 100.0; + double maxValue = 255.0 * ratio; + + if (l2Deadzone > 0) { - bool shiftE = dcs.shiftExtras != "0,0,0,0,0,0,0,0" && dcs.shiftExtras != "" && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp); - bool regE = dcs.extras != "0,0,0,0,0,0,0,0" && dcs.extras != ""; - if ((regE || shiftE) && getBoolMapping(device, dcs.control, cState, eState, tp)) + if (cState.L2 > l2Deadzone) { - usingExtra = dcs.control; - string p; - if (shiftE) - p = dcs.shiftExtras; - else - p = dcs.extras; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[device] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - ctrl.setRumble((byte)extras[0], (byte)extras[1], device); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[device] = color; - DS4LightBar.forcedFlash[device] = (byte)extras[6]; - DS4LightBar.forcelight[device] = true; - } - if (extras[7] == 1) - { - if (oldmouse[device] == -1) - oldmouse[device] = ButtonMouseSensitivity[device]; - ButtonMouseSensitivity[device] = extras[8]; - } - } - catch { } + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = (current - l2Deadzone) / (maxValue - l2Deadzone); } - else if ((regE || shiftE) && held[device]) + else { - DS4LightBar.forcelight[device] = false; - DS4LightBar.forcedFlash[device] = 0; - ButtonMouseSensitivity[device] = oldmouse[device]; - oldmouse[device] = -1; - ctrl.setRumble(0, 0, device); - held[device] = false; - usingExtra = DS4Controls.None; + tempL2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = current / maxValue; + } + + if (l2MaxOutput != 100.0) + { + double maxOutRatio = l2MaxOutput / 100.0; + tempL2Output = Math.Min(Math.Max(tempL2Output, 0.0), maxOutRatio); + } + + if (l2AntiDeadzone > 0) + { + tempL2AntiDead = l2AntiDeadzone * 0.01; + } + + if (tempL2Output > 0.0) + { + dState.L2 = (byte)(((1.0 - tempL2AntiDead) * tempL2Output + tempL2AntiDead) * 255.0); + } + else + { + dState.L2 = 0; + } + } + + /*byte r2Deadzone = getR2Deadzone(device); + int r2AntiDeadzone = getR2AntiDeadzone(device); + int r2Maxzone = getR2Maxzone(device); + */ + TriggerDeadZoneZInfo r2ModInfo = GetR2ModInfo(device); + byte r2Deadzone = r2ModInfo.deadZone; + int r2AntiDeadzone = r2ModInfo.antiDeadZone; + int r2Maxzone = r2ModInfo.maxZone; + double r2MaxOutput = r2ModInfo.maxOutput; + if (r2Deadzone > 0 || r2AntiDeadzone > 0 || r2Maxzone != 100 || r2MaxOutput != 100.0) + { + double tempR2Output = cState.R2 / 255.0; + double tempR2AntiDead = 0.0; + double ratio = r2Maxzone / 100.0; + double maxValue = 255 * ratio; + + if (r2Deadzone > 0) + { + if (cState.R2 > r2Deadzone) + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = (current - r2Deadzone) / (maxValue - r2Deadzone); + } + else + { + tempR2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = current / maxValue; + } + + if (r2MaxOutput != 100.0) + { + double maxOutRatio = r2MaxOutput / 100.0; + tempR2Output = Math.Min(Math.Max(tempR2Output, 0.0), maxOutRatio); + } + + if (r2AntiDeadzone > 0) + { + tempR2AntiDead = r2AntiDeadzone * 0.01; + } + + if (tempR2Output > 0.0) + { + dState.R2 = (byte)(((1.0 - tempR2AntiDead) * tempR2Output + tempR2AntiDead) * 255.0); + } + else + { + dState.R2 = 0; + } + } + + double lsSens = getLSSens(device); + if (lsSens != 1.0) + { + dState.LX = (byte)Global.Clamp(0, lsSens * (dState.LX - 128.0) + 128.0, 255); + dState.LY = (byte)Global.Clamp(0, lsSens * (dState.LY - 128.0) + 128.0, 255); + } + + double rsSens = getRSSens(device); + if (rsSens != 1.0) + { + dState.RX = (byte)Global.Clamp(0, rsSens * (dState.RX - 128.0) + 128.0, 255); + dState.RY = (byte)Global.Clamp(0, rsSens * (dState.RY - 128.0) + 128.0, 255); + } + + double l2Sens = getL2Sens(device); + if (l2Sens != 1.0) + dState.L2 = (byte)Global.Clamp(0, l2Sens * dState.L2, 255); + + double r2Sens = getR2Sens(device); + if (r2Sens != 1.0) + dState.R2 = (byte)Global.Clamp(0, r2Sens * dState.R2, 255); + + SquareStickInfo squStk = GetSquareStickInfo(device); + if (squStk.lsMode && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.lsRoundness); + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + dState.LX = (byte)(tempX * capX + 128.0); + dState.LY = (byte)(tempY * capY + 128.0); + } + + int lsOutCurveMode = getLsOutCurveMode(device); + if (lsOutCurveMode > 0 && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (lsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; + } + + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.LX = (byte)(outputX * capX + 128.0); + dState.LY = (byte)(outputY * capY + 128.0); + } + else if (lsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.LX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.LX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 6) + { + dState.LX = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LX]; + dState.LY = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LY]; + } + } + + if (squStk.rsMode && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.rsRoundness); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + dState.RX = (byte)(tempX * capX + 128.0); + dState.RY = (byte)(tempY * capY + 128.0); + } + + int rsOutCurveMode = getRsOutCurveMode(device); + if (rsOutCurveMode > 0 && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (rsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; } + + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.RX = (byte)(outputX * capX + 128.0); + dState.RY = (byte)(outputY * capY + 128.0); + } + else if (rsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.RX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.RX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 6) + { + dState.RX = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RX]; + dState.RY = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RY]; + } + } + + int l2OutCurveMode = getL2OutCurveMode(device); + if (l2OutCurveMode > 0 && dState.L2 != 0) + { + double temp = dState.L2 / 255.0; + if (l2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 2) + { + double output = temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 6) + { + dState.L2 = l2OutBezierCurveObj[device].arrayBezierLUT[dState.L2]; + } + } + + int r2OutCurveMode = getR2OutCurveMode(device); + if (r2OutCurveMode > 0 && dState.R2 != 0) + { + double temp = dState.R2 / 255.0; + if (r2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 2) + { + double output = temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 6) + { + dState.R2 = r2OutBezierCurveObj[device].arrayBezierLUT[dState.R2]; + } + } + + + bool sOff = /*tempBool =*/ isUsingSAforMouse(device); + if (sOff == false) + { + int SXD = (int)(128d * getSXDeadzone(device)); + int SZD = (int)(128d * getSZDeadzone(device)); + double SXMax = getSXMaxzone(device); + double SZMax = getSZMaxzone(device); + double sxAntiDead = getSXAntiDeadzone(device); + double szAntiDead = getSZAntiDeadzone(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + int result = 0; + + int gyroX = cState.Motion.accelX, gyroZ = cState.Motion.accelZ; + int absx = Math.Abs(gyroX), absz = Math.Abs(gyroZ); + + if (SXD > 0 || SXMax < 1.0 || sxAntiDead > 0) + { + int maxValue = (int)(SXMax * 128d); + if (absx > SXD) + { + double ratioX = absx < maxValue ? (absx - SXD) / (double)(maxValue - SXD) : 1.0; + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * ((1.0 - sxAntiDead) * ratioX + sxAntiDead)); + } + else + { + dState.Motion.outputAccelX = 0; + } + } + else + { + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * (absx / 128d)); + } + + if (SZD > 0 || SZMax < 1.0 || szAntiDead > 0) + { + int maxValue = (int)(SZMax * 128d); + if (absz > SZD) + { + double ratioZ = absz < maxValue ? (absz - SZD) / (double)(maxValue - SZD) : 1.0; + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * ((1.0 - szAntiDead) * ratioZ + szAntiDead)); + } + else + { + dState.Motion.outputAccelZ = 0; + } + } + else + { + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * (absz / 128d)); + } + + int sxOutCurveMode = getSXOutCurveMode(device); + if (sxOutCurveMode > 0) + { + double temp = dState.Motion.outputAccelX / 128.0; + double sign = Math.Sign(temp); + if (sxOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelX = (int)(output * sign * 128.0); + } + else if (sxOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelX = (int)(-1.0 * output * + sign * 128.0); + } + else if (sxOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelX = (int)(output * + sign * 128.0); + } + else if (sxOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelX); + dState.Motion.outputAccelX = sxOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelX), 128)] * signSA; + } + } + + int szOutCurveMode = getSZOutCurveMode(device); + if (szOutCurveMode > 0 && dState.Motion.outputAccelZ != 0) + { + double temp = dState.Motion.outputAccelZ / 128.0; + double sign = Math.Sign(temp); + if (szOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelZ = (int)(output * sign * 128.0); + } + else if (szOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelZ = (int)(-1.0 * output * + sign * 128.0); + } + else if (szOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelZ = (int)(output * + sign * 128.0); + } + else if (szOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelZ); + dState.Motion.outputAccelZ = szOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelZ), 128)] * signSA; + } + } + } + + return dState; + } + + /* TODO: Possibly remove usage of this version of the method */ + private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping(device, ds, cState, eState, tp); + } + + return result; + } + + private static bool ShiftTrigger2(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMapping) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else if (trigger < 26) + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping2(device, ds, cState, eState, tp, fieldMapping); + } + else if (trigger == 26) + { + result = cState.Touch1Finger; + } + + return result; + } + + private static X360Controls getX360ControlsByName(string key) + { + X360Controls x3c; + if (Enum.TryParse(key, true, out x3c)) + return x3c; + + switch (key) + { + case "Back": return X360Controls.Back; + case "Left Stick": return X360Controls.LS; + case "Right Stick": return X360Controls.RS; + case "Start": return X360Controls.Start; + case "Up Button": return X360Controls.DpadUp; + case "Right Button": return X360Controls.DpadRight; + case "Down Button": return X360Controls.DpadDown; + case "Left Button": return X360Controls.DpadLeft; + + case "Left Bumper": return X360Controls.LB; + case "Right Bumper": return X360Controls.RB; + case "Y Button": return X360Controls.Y; + case "B Button": return X360Controls.B; + case "A Button": return X360Controls.A; + case "X Button": return X360Controls.X; + + case "Guide": return X360Controls.Guide; + case "Left X-Axis-": return X360Controls.LXNeg; + case "Left Y-Axis-": return X360Controls.LYNeg; + case "Right X-Axis-": return X360Controls.RXNeg; + case "Right Y-Axis-": return X360Controls.RYNeg; + + case "Left X-Axis+": return X360Controls.LXPos; + case "Left Y-Axis+": return X360Controls.LYPos; + case "Right X-Axis+": return X360Controls.RXPos; + case "Right Y-Axis+": return X360Controls.RYPos; + case "Left Trigger": return X360Controls.LT; + case "Right Trigger": return X360Controls.RT; + + case "Left Mouse Button": return X360Controls.LeftMouse; + case "Right Mouse Button": return X360Controls.RightMouse; + case "Middle Mouse Button": return X360Controls.MiddleMouse; + case "4th Mouse Button": return X360Controls.FourthMouse; + case "5th Mouse Button": return X360Controls.FifthMouse; + case "Mouse Wheel Up": return X360Controls.WUP; + case "Mouse Wheel Down": return X360Controls.WDOWN; + case "Mouse Up": return X360Controls.MouseUp; + case "Mouse Down": return X360Controls.MouseDown; + case "Mouse Left": return X360Controls.MouseLeft; + case "Mouse Right": return X360Controls.MouseRight; + case "Unbound": return X360Controls.Unbound; + default: break; + } + + return X360Controls.Unbound; + } + + /// + /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. + /// + static bool[] held = new bool[4]; + static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; + public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, + Mouse tp, ControlService ctrl) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + double tempMouseDeltaX = 0.0; + double tempMouseDeltaY = 0.0; + int mouseDeltaX = 0; + int mouseDeltaY = 0; + + cState.calculateStickAngles(); + DS4StateFieldMapping fieldMapping = fieldMappings[device]; + fieldMapping.populateFieldMapping(cState, eState, tp); + DS4StateFieldMapping outputfieldMapping = outputFieldMappings[device]; + outputfieldMapping.populateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping fieldMapping = new DS4StateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping outputfieldMapping = new DS4StateFieldMapping(cState, eState, tp); + + SyntheticState deviceState = Mapping.deviceState[device]; + if (getProfileActionCount(device) > 0 || useTempProfile[device]) + MapCustomAction(device, cState, MappedState, eState, tp, ctrl, fieldMapping, outputfieldMapping); + //if (ctrl.DS4Controllers[device] == null) return; + + //cState.CopyTo(MappedState); + + //Dictionary tempControlDict = new Dictionary(); + //MultiValueDict tempControlDict = new MultiValueDict(); + DS4Controls usingExtra = DS4Controls.None; + List tempSettingsList = getDS4CSettings(device); + //foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + //for (int settingIndex = 0, arlen = tempSettingsList.Count; settingIndex < arlen; settingIndex++) + for (var settingEnum = tempSettingsList.GetEnumerator(); settingEnum.MoveNext();) + { + //DS4ControlSettings dcs = tempSettingsList[settingIndex]; + DS4ControlSettings dcs = settingEnum.Current; + object action = null; + DS4ControlSettings.ActionType actionType = 0; + DS4KeyType keyType = DS4KeyType.None; + if (dcs.shiftAction != null && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping)) + { + action = dcs.shiftAction; + actionType = dcs.shiftActionType; + keyType = dcs.shiftKeyType; + } + else if (dcs.action != null) + { + action = dcs.action; + actionType = dcs.actionType; + keyType = dcs.keyType; + } + + if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + { + bool shiftE = !string.IsNullOrEmpty(dcs.shiftExtras) && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping); + bool regE = !string.IsNullOrEmpty(dcs.extras); + if ((regE || shiftE) && getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + usingExtra = dcs.control; + string p; + if (shiftE) + p = dcs.shiftExtras; + else + p = dcs.extras; + + string[] extraS = p.Split(','); + int extrasSLen = extraS.Length; + int[] extras = new int[extrasSLen]; + for (int i = 0; i < extrasSLen; i++) + { + int b; + if (int.TryParse(extraS[i], out b)) + extras[i] = b; + } + + held[device] = true; + try + { + if (!(extras[0] == extras[1] && extras[1] == 0)) + ctrl.setRumble((byte)extras[0], (byte)extras[1], device); + + if (extras[2] == 1) + { + DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; + DS4LightBar.forcedColor[device] = color; + DS4LightBar.forcedFlash[device] = (byte)extras[6]; + DS4LightBar.forcelight[device] = true; + } + + if (extras[7] == 1) + { + if (oldmouse[device] == -1) + oldmouse[device] = ButtonMouseSensitivity[device]; + ButtonMouseSensitivity[device] = extras[8]; + } + } + catch { } + } + else if ((regE || shiftE) && held[device]) + { + DS4LightBar.forcelight[device] = false; + DS4LightBar.forcedFlash[device] = 0; + if (oldmouse[device] != -1) + { + ButtonMouseSensitivity[device] = oldmouse[device]; + oldmouse[device] = -1; + } + + ctrl.setRumble(0, 0, device); + held[device] = false; + usingExtra = DS4Controls.None; + } + } + + if (action != null) + { + if (actionType == DS4ControlSettings.ActionType.Macro) + { + bool active = getBoolMapping2(device, dcs.control, cState, eState, tp, fieldMapping); + if (active) + { + PlayMacro(device, macroControl, String.Empty, null, (int[])action, dcs.control, keyType); + } + else + { + EndMacro(device, macroControl, (int[])action, dcs.control); + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Key) + { + ushort value = Convert.ToUInt16(action); + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + SyntheticState.KeyPresses kp; + if (!deviceState.keyPresses.TryGetValue(value, out kp)) + deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); + + if (keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (!pressedonce[value]) + { + kp.current.toggle = !kp.current.toggle; + pressedonce[value] = true; + } + kp.current.toggleCount++; + } + kp.current.repeatCount++; + } + else + pressedonce[value] = false; + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Button) + { + int keyvalue = 0; + bool isAnalog = false; + + if (dcs.control >= DS4Controls.LXNeg && dcs.control <= DS4Controls.RYPos) + { + isAnalog = true; + } + else if (dcs.control == DS4Controls.L2 || dcs.control == DS4Controls.R2) + { + isAnalog = true; + } + else if (dcs.control >= DS4Controls.GyroXPos && dcs.control <= DS4Controls.GyroZNeg) + { + isAnalog = true; + } + + X360Controls xboxControl = X360Controls.None; + if (action is X360Controls) + { + xboxControl = (X360Controls)action; + } + else if (action is string) + { + xboxControl = getX360ControlsByName(action.ToString()); + } + + if (xboxControl >= X360Controls.LXNeg && xboxControl <= X360Controls.Start) + { + DS4Controls tempDS4Control = reverseX360ButtonMapping[(int)xboxControl]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, tempDS4Control)); + //tempControlDict.Add(dcs.control, tempDS4Control); + } + else if (xboxControl >= X360Controls.LeftMouse && xboxControl <= X360Controls.WDOWN) + { + switch (xboxControl) + { + case X360Controls.LeftMouse: + { + keyvalue = 256; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.leftCount++; + + break; + } + case X360Controls.RightMouse: + { + keyvalue = 257; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.rightCount++; + + break; + } + case X360Controls.MiddleMouse: + { + keyvalue = 258; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.middleCount++; + + break; + } + case X360Controls.FourthMouse: + { + keyvalue = 259; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fourthCount++; + + break; + } + case X360Controls.FifthMouse: + { + keyvalue = 260; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fifthCount++; + + break; + } + case X360Controls.WUP: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); + else + deviceState.currentClicks.wUpCount++; + } + + break; + } + case X360Controls.WDOWN: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); + else + deviceState.currentClicks.wDownCount++; + } + + break; + } + + default: break; + } + } + else if (xboxControl >= X360Controls.MouseUp && xboxControl <= X360Controls.MouseRight) + { + switch (xboxControl) + { + case X360Controls.MouseUp: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 0, ctrl); + tempMouseDeltaY = -Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseDown: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 1, ctrl); + tempMouseDeltaY = Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseLeft: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 2, ctrl); + tempMouseDeltaX = -Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + case X360Controls.MouseRight: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 3, ctrl); + tempMouseDeltaX = Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + + default: break; + } + } + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (!pressedonce[keyvalue]) + { + deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; + pressedonce[keyvalue] = true; + } + deviceState.currentClicks.toggleCount++; + } + else + { + pressedonce[keyvalue] = false; + } + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + } + else + { + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[(int)dcs.control]; + if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + //if (dcs.control > DS4Controls.None && dcs.control < DS4Controls.L1) + { + //int current = (int)dcs.control; + //outputfieldMapping.axisdirs[current] = fieldMapping.axisdirs[current]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, dcs.control)); + } + } + } + + Queue tempControl = customMapQueue[device]; + unchecked + { + for (int i = 0, len = tempControl.Count; i < len; i++) + //while(tempControl.Any()) + { + ControlToXInput tempMap = tempControl.Dequeue(); + int controlNum = (int)tempMap.ds4input; + int tempOutControl = (int)tempMap.xoutput; + if (tempMap.xoutput >= DS4Controls.LXNeg && tempMap.xoutput <= DS4Controls.RYPos) + { + const byte axisDead = 128; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[tempOutControl]; + bool alt = controlType == DS4StateFieldMapping.ControlType.AxisDir && tempOutControl % 2 == 0 ? true : false; + byte axisMapping = getXYAxisMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping, alt); + if (axisMapping != axisDead) + { + int controlRelation = tempOutControl % 2 == 0 ? tempOutControl - 1 : tempOutControl + 1; + outputfieldMapping.axisdirs[tempOutControl] = axisMapping; + outputfieldMapping.axisdirs[controlRelation] = axisMapping; + } + } + else + { + if (tempMap.xoutput == DS4Controls.L2 || tempMap.xoutput == DS4Controls.R2) + { + const byte axisZero = 0; + byte axisMapping = getByteMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (axisMapping != axisZero) + outputfieldMapping.triggers[tempOutControl] = axisMapping; + } + else + { + bool value = getBoolMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (value) + outputfieldMapping.buttons[tempOutControl] = value; + } + } + } + } + + outputfieldMapping.populateState(MappedState); + + if (macroCount > 0) + { + if (macroControl[00]) MappedState.Cross = true; + if (macroControl[01]) MappedState.Circle = true; + if (macroControl[02]) MappedState.Square = true; + if (macroControl[03]) MappedState.Triangle = true; + if (macroControl[04]) MappedState.Options = true; + if (macroControl[05]) MappedState.Share = true; + if (macroControl[06]) MappedState.DpadUp = true; + if (macroControl[07]) MappedState.DpadDown = true; + if (macroControl[08]) MappedState.DpadLeft = true; + if (macroControl[09]) MappedState.DpadRight = true; + if (macroControl[10]) MappedState.PS = true; + if (macroControl[11]) MappedState.L1 = true; + if (macroControl[12]) MappedState.R1 = true; + if (macroControl[13]) MappedState.L2 = 255; + if (macroControl[14]) MappedState.R2 = 255; + if (macroControl[15]) MappedState.L3 = true; + if (macroControl[16]) MappedState.R3 = true; + if (macroControl[17]) MappedState.LX = 255; + if (macroControl[18]) MappedState.LX = 0; + if (macroControl[19]) MappedState.LY = 255; + if (macroControl[20]) MappedState.LY = 0; + if (macroControl[21]) MappedState.RX = 255; + if (macroControl[22]) MappedState.RX = 0; + if (macroControl[23]) MappedState.RY = 255; + if (macroControl[24]) MappedState.RY = 0; + } + + if (GetSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None) + { + MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl); + } + + ref byte gyroTempX = ref gyroStickX[device]; + if (gyroTempX != 128) + { + if (MappedState.RX != 128) + MappedState.RX = Math.Abs(gyroTempX - 128) > Math.Abs(MappedState.RX - 128) ? + gyroTempX : MappedState.RX; + else + MappedState.RX = gyroTempX; + } + + ref byte gyroTempY = ref gyroStickY[device]; + if (gyroTempY != 128) + { + if (MappedState.RY != 128) + MappedState.RY = Math.Abs(gyroTempY - 128) > Math.Abs(MappedState.RY - 128) ? + gyroTempY : MappedState.RY; + else + MappedState.RY = gyroTempY; + } + + gyroTempX = gyroTempY = 128; + + calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY, + out mouseDeltaX, out mouseDeltaY); + if (mouseDeltaX != 0 || mouseDeltaY != 0) + { + InputMethods.MoveCursorBy(mouseDeltaX, mouseDeltaY); + } + } + + private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) + { + return shift ? false : GetDS4Action(device, dc, false) == null; + } + + private static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, + DS4StateExposed eState, Mouse tp, ControlService ctrl, DS4StateFieldMapping fieldMapping, DS4StateFieldMapping outputfieldMapping) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + try + { + int actionDoneCount = actionDone.Count; + int totalActionCount = GetActions().Count; + DS4StateFieldMapping previousFieldMapping = null; + List profileActions = getProfileActions(device); + //foreach (string actionname in profileActions) + for (int actionIndex = 0, profileListLen = profileActions.Count; + actionIndex < profileListLen; actionIndex++) + { + //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); + //SpecialAction action = GetAction(actionname); + //int index = GetActionIndexOf(actionname); + string actionname = profileActions[actionIndex]; + SpecialAction action = GetProfileAction(device, actionname); + int index = GetProfileActionIndexOf(device, actionname); + + if (actionDoneCount < index + 1) + { + actionDone.Add(new ActionState()); + actionDoneCount++; + } + else if (actionDoneCount > totalActionCount) + { + actionDone.RemoveAt(actionDoneCount - 1); + actionDoneCount--; + } + + if (action == null) + { + continue; + } + + double time = 0.0; + //If a key or button is assigned to the trigger, a key special action is used like + //a quick tap to use and hold to use the regular custom button/key + bool triggerToBeTapped = action.typeID == SpecialAction.ActionTypeId.None && action.trigger.Count == 1 && + GetDS4Action(device, action.trigger[0], false) == null; + if (!(action.typeID == SpecialAction.ActionTypeId.None || index < 0)) + { + bool triggeractivated = true; + if (action.delayTime > 0.0) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + time = action.delayTime; + nowAction[device] = DateTime.UtcNow; + if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) + triggeractivated = true; + } + else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) + oldnowAction[device] = DateTime.UtcNow; + } + else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + oldnowKeyAct[device] = DateTime.UtcNow; + } + } + else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + DateTime now = DateTime.UtcNow; + if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) + { + await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up + triggeractivated = true; + oldnowKeyAct[device] = DateTime.MinValue; + } + else if (!subtriggeractivated) + oldnowKeyAct[device] = DateTime.MinValue; + } + else + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + triggeractivated = false; + break; + } + } + + // If special action macro is set to run on key release then activate the trigger status only when the trigger key is released + if (action.typeID == SpecialAction.ActionTypeId.Macro && action.pressRelease && action.firstTouch) + triggeractivated = !triggeractivated; + } + + bool utriggeractivated = true; + int uTriggerCount = action.uTrigger.Count; + if (action.typeID == SpecialAction.ActionTypeId.Key && uTriggerCount > 0) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + if (action.pressRelease) utriggeractivated = !utriggeractivated; + } + + bool actionFound = false; + if (triggeractivated) + { + if (action.typeID == SpecialAction.ActionTypeId.Program) + { + actionFound = true; + + if (!actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + if (!string.IsNullOrEmpty(action.extra)) + Process.Start(action.details, action.extra); + else + Process.Start(action.details); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Profile) + { + actionFound = true; + + if (!actionDone[index].dev[device] && (!useTempProfile[device] || untriggeraction[device] == null || untriggeraction[device].typeID != SpecialAction.ActionTypeId.Profile) ) + { + actionDone[index].dev[device] = true; + // If Loadprofile special action doesn't have untrigger keys or automatic untrigger option is not set then don't set untrigger status. This way the new loaded profile allows yet another loadProfile action key event. + if (action.uTrigger.Count > 0 || action.automaticUntrigger) + { + untriggeraction[device] = action; + untriggerindex[device] = index; + + // If the existing profile is a temp profile then store its name, because automaticUntrigger needs to know where to go back (empty name goes back to default regular profile) + untriggeraction[device].prevProfileName = (useTempProfile[device] ? tempprofilename[device] : string.Empty); + } + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease(ushort.Parse(dcs.action.ToString())); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", action.details); + AppLogger.LogToGui(prolog, false); + LoadTempProfile(device, action.details, true, ctrl); + + if (action.uTrigger.Count == 0 && !action.automaticUntrigger) + { + // If the new profile has any actions with the same action key (controls) than this action (which doesn't have untrigger keys) then set status of those actions to wait for the release of the existing action key. + List profileActionsNext = getProfileActions(device); + for (int actionIndexNext = 0, profileListLenNext = profileActionsNext.Count; actionIndexNext < profileListLenNext; actionIndexNext++) + { + string actionnameNext = profileActionsNext[actionIndexNext]; + SpecialAction actionNext = GetProfileAction(device, actionnameNext); + int indexNext = GetProfileActionIndexOf(device, actionnameNext); + + if (actionNext.controls == action.controls) + actionDone[indexNext].dev[device] = true; + } + } + + return; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Macro) + { + actionFound = true; + if (!action.pressRelease) + { + // Macro run when trigger keys are pressed down (the default behaviour) + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, actionDone[index]); + } + else + { + if (!action.keyType.HasFlag(DS4KeyType.RepeatMacro)) + EndMacro(device, macroControl, action.macro, DS4Controls.None); + } + } + else + { + // Macro is run when trigger keys are released (optional behaviour of macro special action)) + if (action.firstTouch) + { + action.firstTouch = false; + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, null); + } + } + else + action.firstTouch = true; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (uTriggerCount == 0 || (uTriggerCount > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = index; + ushort key; + ushort.TryParse(action.details, out key); + if (uTriggerCount == 0) + { + SyntheticState.KeyPresses kp; + if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) + deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + kp.current.repeatCount++; + } + else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyPress(key); + else + InputMethods.performKeyPress(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + bool synced = /*tempBool =*/ d.isSynced(); + if (synced && !d.isCharging()) + { + ConnectionType deviceConn = d.getConnectionType(); + //bool exclusive = /*tempBool =*/ d.isExclusive(); + if (deviceConn == ConnectionType.BT) + { + d.DisconnectBT(); + ReleaseActionKeys(action, device); + return; + } + else if (deviceConn == ConnectionType.SONYWA) + { + action.pressRelease = true; + } + } + } + else if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + + string[] dets = action.details.Split('|'); + if (dets.Length == 1) + dets = action.details.Split(','); + if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) + { + AppLogger.LogToTray("Controller " + (device + 1) + ": " + + ctrl.getDS4Battery(device), true); + } + if (bool.Parse(dets[2])) + { + DS4Device d = ctrl.DS4Controllers[device]; + if (!actionDone[index].dev[device]) + { + lastColor[device] = d.LightBarColor; + DS4LightBar.forcelight[device] = true; + } + DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); + DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); + DS4Color trans = getTransitionedColor(ref empty, ref full, d.Battery); + if (fadetimer[device] < 100) + DS4LightBar.forcedColor[device] = getTransitionedColor(ref lastColor[device], ref trans, fadetimer[device] += 2); + } + actionDone[index].dev[device] = true; + } + else if (action.typeID == SpecialAction.ActionTypeId.SASteeringWheelEmulationCalibrate) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + // If controller is not already in SASteeringWheelCalibration state then enable it now. If calibration is active then complete it (commit calibration values) + if (d.WheelRecalibrateActiveState == 0 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 1; // Start calibration process + } + else if (d.WheelRecalibrateActiveState == 2 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 3; // Complete calibration process + } + + actionDone[index].dev[device] = true; + } + } + else + { + if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + if (actionDone[index].dev[device]) + { + fadetimer[device] = 0; + /*if (prevFadetimer[device] == fadetimer[device]) + { + prevFadetimer[device] = 0; + fadetimer[device] = 0; + } + else + prevFadetimer[device] = fadetimer[device];*/ + DS4LightBar.forcelight[device] = false; + actionDone[index].dev[device] = false; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT && action.pressRelease) + { + actionFound = true; + DS4Device d = ctrl.DS4Controllers[device]; + ConnectionType deviceConn = d.getConnectionType(); + if (deviceConn == ConnectionType.SONYWA && d.isSynced()) + { + if (d.isDS4Idle()) + { + d.DisconnectDongle(); + ReleaseActionKeys(action, device); + actionDone[index].dev[device] = false; + action.pressRelease = false; + } + } + } + else if (action.typeID != SpecialAction.ActionTypeId.Key && + action.typeID != SpecialAction.ActionTypeId.XboxGameDVR && + action.typeID != SpecialAction.ActionTypeId.MultiAction) + { + // Ignore + actionFound = true; + actionDone[index].dev[device] = false; + } + } + + if (!actionFound) + { + if (uTriggerCount > 0 && utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = -1; + ushort key; + ushort.TryParse(action.details, out key); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyRelease(key); + else + InputMethods.performKeyRelease(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.XboxGameDVR || action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + actionFound = true; + + bool tappedOnce = action.tappedOnce, firstTouch = action.firstTouch, + secondtouchbegin = action.secondtouchbegin; + //DateTime pastTime = action.pastTime, firstTap = action.firstTap, + // TimeofEnd = action.TimeofEnd; + + /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) + getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; + if (getCustomMacro(device, action.trigger[0]) != "0") + getCustomMacros(device).Remove(action.trigger[0]); + if (getCustomKey(device, action.trigger[0]) != 0) + getCustomMacros(device).Remove(action.trigger[0]);*/ + string[] dets = action.details.Split(','); + DS4Device d = ctrl.DS4Controllers[device]; + //cus + + DS4State tempPrevState = d.getPreviousStateRef(); + // Only create one instance of previous DS4StateFieldMapping in case more than one multi-action + // button is assigned + if (previousFieldMapping == null) + { + previousFieldMapping = previousFieldMappings[device]; + previousFieldMapping.populateFieldMapping(tempPrevState, eState, tp, true); + //previousFieldMapping = new DS4StateFieldMapping(tempPrevState, eState, tp, true); + } + + bool activeCur = getBoolSpecialActionMapping(device, action.trigger[0], cState, eState, tp, fieldMapping); + bool activePrev = getBoolSpecialActionMapping(device, action.trigger[0], tempPrevState, eState, tp, previousFieldMapping); + if (activeCur && !activePrev) + { + // pressed down + action.pastTime = DateTime.UtcNow; + if (action.pastTime <= (action.firstTap + TimeSpan.FromMilliseconds(150))) + { + action.tappedOnce = tappedOnce = false; + action.secondtouchbegin = secondtouchbegin = true; + //tappedOnce = false; + //secondtouchbegin = true; + } + else + action.firstTouch = firstTouch = true; + //firstTouch = true; + } + else if (!activeCur && activePrev) + { + // released + if (secondtouchbegin) + { + action.firstTouch = firstTouch = false; + action.secondtouchbegin = secondtouchbegin = false; + //firstTouch = false; + //secondtouchbegin = false; + } + else if (firstTouch) + { + action.firstTouch = firstTouch = false; + //firstTouch = false; + if (DateTime.UtcNow <= (action.pastTime + TimeSpan.FromMilliseconds(150)) && !tappedOnce) + { + action.tappedOnce = tappedOnce = true; + //tappedOnce = true; + action.firstTap = DateTime.UtcNow; + action.TimeofEnd = DateTime.UtcNow; + } + } + } + + int type = 0; + string macro = ""; + if (tappedOnce) //single tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[0]; + } + else if (int.TryParse(dets[0], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if ((DateTime.UtcNow - action.TimeofEnd) > TimeSpan.FromMilliseconds(150)) + { + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + tappedOnce = false; + action.tappedOnce = false; + } + //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) + } + else if (firstTouch && (DateTime.UtcNow - action.pastTime) > TimeSpan.FromMilliseconds(500)) //helddown + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[1]; + } + else if (int.TryParse(dets[1], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + firstTouch = false; + action.firstTouch = false; + } + else if (secondtouchbegin) //if double tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[2]; + } + else if (int.TryParse(dets[2], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + secondtouchbegin = false; + action.secondtouchbegin = false; + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + } + } + catch { return; } + + if (untriggeraction[device] != null) + { + SpecialAction action = untriggeraction[device]; + int index = untriggerindex[device]; + bool utriggeractivated; + + if (!action.automaticUntrigger) + { + // Untrigger keys defined and auto-untrigger (=unload) profile option is NOT set. Unload a temporary profile only when specified untrigger keys have been triggered. + utriggeractivated = true; + + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, uTrigLen = action.uTrigger.Count; i < uTrigLen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + } + else + { + // Untrigger as soon any of the defined regular trigger keys have been released. + utriggeractivated = false; + + for (int i = 0, trigLen = action.trigger.Count; i < trigLen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = true; + break; + } + } + } + + if (utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Profile) + { + if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same + action.controls != action.ucontrols) + { + if (useTempProfile[device]) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + actionDone[index].dev[device] = true; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string profileName = untriggeraction[device].prevProfileName; + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", (profileName == string.Empty ? ProfilePath[device] : profileName)); + AppLogger.LogToGui(prolog, false); + + untriggeraction[device] = null; + + if (profileName == string.Empty) + LoadProfile(device, false, ctrl); // Previous profile was a regular default profile of a controller + else + LoadTempProfile(device, profileName, true, ctrl); // Previous profile was a temporary profile, so re-load it as a temp profile + } + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + + private static void ReleaseActionKeys(SpecialAction action, int device) + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + } + + // Play macro as a background task. Optionally the new macro play waits for completion of a previous macro execution (synchronized macro special action). + // Macro steps are defined either as macrostr string value, macroLst list object or as macroArr integer array. Only one of these should have a valid macro definition when this method is called. + // If the macro definition is a macroStr string value then it will be converted as integer array on the fl. If steps are already defined as list or array of integers then there is no need to do type cast conversion. + private static void PlayMacro(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action = null, ActionState actionDoneState = null) + { + if (action != null && action.synchronized) + { + // Run special action macros in synchronized order (ie. FirstIn-FirstOut). The trigger control name string is the execution queue identifier (ie. each unique trigger combination has an own synchronization queue). + if (!macroTaskQueue[device].TryGetValue(action.controls, out Task prevTask)) + macroTaskQueue[device].Add(action.controls, (Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState))) ); + else + macroTaskQueue[device][action.controls] = prevTask.ContinueWith((x) => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + else + // Run macro as "fire and forget" background task. No need to wait for completion of any of the other macros. + // If the same trigger macro is re-launched while previous macro is still running then the order of parallel macros is not guaranteed. + Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + + // Play through a macro. The macro steps are defined either as string, List or Array object (always only one of those parameters is set to a valid value) + private static void PlayMacroTask(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action, ActionState actionDoneState) + { + if(!String.IsNullOrEmpty(macroStr)) + { + string[] skeys; + + skeys = macroStr.Split('/'); + macroArr = new int[skeys.Length]; + for (int i = 0; i < macroArr.Length; i++) + macroArr[i] = int.Parse(skeys[i]); + } + + // macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18") + if ( (macroLst != null && macroLst.Count >= 4 && ((macroLst[0] == 164 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 164) || (macroLst[0] == 18 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 18))) + || (macroArr != null && macroArr.Length>= 4 && ((macroArr[0] == 164 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 164) || (macroArr[0] == 18 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 18))) + ) + { + int wait; + if(macroLst != null) + wait = macroLst[macroLst.Count - 1]; + else + wait = macroArr[macroArr.Length - 1]; + + if (wait <= 300 || wait > ushort.MaxValue) + wait = 1000; + else + wait -= 300; + + AltTabSwapping(wait, device); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + } + else if(control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + { + int macroCodeValue; + bool[] keydown = new bool[286]; + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + + // Play macro codes and simulate key down/up events (note! The same key may go through several up and down events during the same macro). + // If the return value is TRUE then this method should do a asynchronized delay (the usual Thread.Sleep doesnt work here because it would block the main gamepad reading thread). + if (macroLst != null) + { + for (int i = 0; i < macroLst.Count; i++) + { + macroCodeValue = macroLst[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + else + { + for (int i = 0; i < macroArr.Length; i++) + { + macroCodeValue = macroArr[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + + // The macro is finished. If any of the keys is still in down state then release a key state (ie. simulate key up event) unless special action specified to keep the last state as it is left in a macro + if (action == null || !action.keepKeyState) + { + for (int i = 0, arlength = keydown.Length; i < arlength; i++) + { + if (keydown[i]) + PlayMacroCodeValue(device, macrocontrol, keyType, i, keydown); + } + } + + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + + // Commented out rumble reset. No need to zero out rumble after a macro because it may conflict with a game generated rumble events (ie. macro would stop a game generated rumble effect). + // If macro generates rumble effects then the macro can stop the rumble as a last step or wait for rumble watchdog timer to do it after few seconds. + //Program.rootHub.DS4Controllers[device].setRumble(0, 0); + + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + Task.Delay(50).Wait(); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + } + + // If a special action type of Macro has "Repeat while held" option and actionDoneState object is defined then reset the action back to "not done" status in order to re-fire it if the trigger key is still held down + if (actionDoneState != null && keyType.HasFlag(DS4KeyType.RepeatMacro)) + actionDoneState.dev[device] = false; + } + + private static bool PlayMacroCodeValue(int device, bool[] macrocontrol, DS4KeyType keyType, int macroCodeValue, bool[] keydown) + { + bool doDelayOnCaller = false; + if (macroCodeValue >= 261 && macroCodeValue <= 285) + { + // Gamepad button up or down macro event. macroCodeValue index value is the button identifier (codeValue-261 = idx in 0..24 range) + if (!keydown[macroCodeValue]) + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = true; + macroCount++; + } + else + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = false; + if (macroCount > 0) macroCount--; + } + } + else if (macroCodeValue < 300) + { + // Keyboard key or mouse button macro event + if (!keydown[macroCodeValue]) + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyPress((ushort)macroCodeValue); + else InputMethods.performKeyPress((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = true; + } + else + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyRelease((ushort)macroCodeValue); + else InputMethods.performKeyRelease((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = false; + } + } + else if (macroCodeValue >= 1000000000) + { + // Lightbar color event + if (macroCodeValue > 1000000000) + { + string lb = macroCodeValue.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + DS4LightBar.forcelight[device] = true; + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); + } + else + { + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + } + } + else if (macroCodeValue >= 1000000) + { + // Rumble event + DS4Device d = Program.rootHub.DS4Controllers[device]; + string r = macroCodeValue.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + d.setRumble(light, heavy); + } + else + { + // Delay specification. Indicate to caller that it should do a delay of macroCodeValue-300 msecs + doDelayOnCaller = true; + } + + return doDelayOnCaller; + } + + private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) + { + if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, List macro, DS4Controls control) + { + if(macro.Count >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, int[] macro, DS4Controls control) + { + if (macro.Length >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void AltTabSwapping(int wait, int device) + { + if (altTabDone) + { + altTabDone = false; + InputMethods.performKeyPress(18); + } + else + { + altTabNow = DateTime.UtcNow; + if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) + { + oldAltTabNow = altTabNow; + InputMethods.performKeyPress(9); + InputMethods.performKeyRelease(9); + } + } + } + + private static void AltTabSwappingRelease() + { + if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + { + altTabDone = true; + InputMethods.performKeyRelease(9); + InputMethods.performKeyRelease(18); + altTabNow = DateTime.UtcNow; + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + } + } + + private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, bool down) + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + { + oldnow = now; + InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 8.0f * (down ? -1 : 1)), 0); + } + } + + private static double getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, + DS4StateFieldMapping fieldMapping, int mnum, ControlService ctrl) + { + int controlnum = DS4ControltoInt(control); + + int deadzoneL = 0; + int deadzoneR = 0; + if (getLSDeadzone(device) == 0) + deadzoneL = 3; + if (getRSDeadzone(device) == 0) + deadzoneR = 3; + + double value = 0.0; + int speed = ButtonMouseSensitivity[device]; + double root = 1.002; + double divide = 10000d; + //DateTime now = mousenow[mnum]; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + //long timeElapsed = ctrl.DS4Controllers[device].getLastTimeElapsed(); + double timeElapsed = ctrl.DS4Controllers[device].lastTimeElapsedDouble; + //double mouseOffset = 0.025; + double tempMouseOffsetX = 0.0, tempMouseOffsetY = 0.0; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + bool active = fieldMapping.buttons[controlNum]; + value = (active ? Math.Pow(root + speed / divide, 100) - 1 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + if (cState.LX < 128 - deadzoneL) + { + double diff = -(cState.LX - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LX - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LXPos: + { + if (cState.LX > 128 + deadzoneL) + { + double diff = (cState.LX - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LX - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RXNeg: + { + if (cState.RX < 128 - deadzoneR) + { + double diff = -(cState.RX - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RX - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RXPos: + { + if (cState.RX > 128 + deadzoneR) + { + double diff = (cState.RX - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RX - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.LYNeg: + { + if (cState.LY < 128 - deadzoneL) + { + double diff = -(cState.LY - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LY - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LYPos: + { + if (cState.LY > 128 + deadzoneL) + { + double diff = (cState.LY - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LY - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RYNeg: + { + if (cState.RY < 128 - deadzoneR) + { + double diff = -(cState.RY - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RY - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RYPos: + { + if (cState.RY > 128 + deadzoneR) + { + double diff = (cState.RY - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RY - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + byte trigger = fieldMapping.triggers[controlNum]; + value = Math.Pow(root + speed / divide, trigger / 2d) - 1; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + //double SXD = getSXDeadzone(device); + //double SZD = getSZDeadzone(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX > 0 ? Math.Pow(root + speed / divide, gyroX) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX < 0 ? Math.Pow(root + speed / divide, -gyroX) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ > 0 ? Math.Pow(root + speed / divide, gyroZ) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ < 0 ? Math.Pow(root + speed / divide, -gyroZ) : 0); + break; + } + default: break; + } + } + + if (getMouseAccel(device)) + { + if (value > 0) + { + mcounter = 34; + mouseaccel++; + } + + if (mouseaccel == prevmouseaccel) + { + mcounter--; + } + + if (mcounter <= 0) + { + mouseaccel = 0; + mcounter = 34; + } + + value *= 1 + Math.Min(20000, (mouseaccel)) / 10000d; + prevmouseaccel = mouseaccel; + } + + return value; + } + + private static void calculateFinalMouseMovement(ref double rawMouseX, ref double rawMouseY, + out int mouseX, out int mouseY) + { + if ((rawMouseX > 0.0 && horizontalRemainder > 0.0) || (rawMouseX < 0.0 && horizontalRemainder < 0.0)) + { + rawMouseX += horizontalRemainder; + } + else + { + horizontalRemainder = 0.0; + } + + //double mouseXTemp = rawMouseX - (Math.IEEERemainder(rawMouseX * 1000.0, 1.0) / 1000.0); + double mouseXTemp = rawMouseX - (remainderCutoff(rawMouseX * 1000.0, 1.0) / 1000.0); + //double mouseXTemp = rawMouseX - (rawMouseX * 1000.0 - (1.0 * (int)(rawMouseX * 1000.0 / 1.0))); + mouseX = (int)mouseXTemp; + horizontalRemainder = mouseXTemp - mouseX; + //mouseX = (int)rawMouseX; + //horizontalRemainder = rawMouseX - mouseX; + + if ((rawMouseY > 0.0 && verticalRemainder > 0.0) || (rawMouseY < 0.0 && verticalRemainder < 0.0)) + { + rawMouseY += verticalRemainder; + } + else + { + verticalRemainder = 0.0; + } + + //double mouseYTemp = rawMouseY - (Math.IEEERemainder(rawMouseY * 1000.0, 1.0) / 1000.0); + double mouseYTemp = rawMouseY - (remainderCutoff(rawMouseY * 1000.0, 1.0) / 1000.0); + mouseY = (int)mouseYTemp; + verticalRemainder = mouseYTemp - mouseY; + //mouseY = (int)rawMouseY; + //verticalRemainder = rawMouseY - mouseY; + } + + private static double remainderCutoff(double dividend, double divisor) + { + return dividend - (divisor * (int)(dividend / divisor)); + } + + public static bool compare(byte b1, byte b2) + { + bool result = true; + if (Math.Abs(b1 - b2) > 10) + { + result = false; + } + + return result; + } + + private static byte getByteMapping2(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, + DS4StateFieldMapping fieldMap) + { + byte result = 0; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = (byte)(fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + default: result = (byte)(axisValue - 128.0f < 0 ? 0 : (axisValue - 128.0f) * 2.0078740157480315f); break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = (byte)(tp != null && fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = (byte)(tp != null ? fieldMap.swipedirs[controlNum] : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroZ * 2) : 0); + break; + } + default: break; + } + } + + return result; + } + + public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + byte result = 0; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = (byte)(cState.Cross ? 255 : 0); break; + case DS4Controls.Square: result = (byte)(cState.Square ? 255 : 0); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? 255 : 0); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = (byte)(cState.L1 ? 255 : 0); break; + case DS4Controls.L2: result = cState.L2; break; + case DS4Controls.L3: result = (byte)(cState.L3 ? 255 : 0); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? 255 : 0); break; + case DS4Controls.R2: result = cState.R2; break; + case DS4Controls.R3: result = (byte)(cState.R3 ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? 255 : 0); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? 255 : 0); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? 255 : 0); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(cState.LX - 128.0f >= 0 ? 0 : -(cState.LX - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(cState.LY - 128.0f >= 0 ? 0 : -(cState.LY - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(cState.RX - 128.0f >= 0 ? 0 : -(cState.RX - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(cState.RY - 128.0f >= 0 ? 0 : -(cState.RY - 128.0f) * 1.9921875f); break; + case DS4Controls.LXPos: result = (byte)(cState.LX - 128.0f < 0 ? 0 : (cState.LX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.LYPos: result = (byte)(cState.LY - 128.0f < 0 ? 0 : (cState.LY - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RXPos: result = (byte)(cState.RX - 128.0f < 0 ? 0 : (cState.RX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RYPos: result = (byte)(cState.RY - 128.0f < 0 ? 0 : (cState.RY - 128.0f) * 2.0078740157480315f); break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? 255 : 0); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? 255 : 0); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? 255 : 0); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (byte)(tp != null ? tp.swipeUpB : 0); break; + case DS4Controls.SwipeDown: result = (byte)(tp != null ? tp.swipeDownB : 0); break; + case DS4Controls.SwipeLeft: result = (byte)(tp != null ? tp.swipeLeftB : 0); break; + case DS4Controls.SwipeRight: result = (byte)(tp != null ? tp.swipeRightB : 0); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX > SXD * 10 ? Math.Min(255, sxsens * gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX < -SXD * 10 ? Math.Min(255, sxsens * -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ > SZD * 10 ? Math.Min(255, szsens * gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ < -SZD * 10 ? Math.Min(255, szsens * -gyroZ * 2) : 0); + break; + } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? 255 : 0); break; + case DS4Controls.Options: result = (byte)(cState.Options ? 255 : 0); break; + case DS4Controls.PS: result = (byte)(cState.PS ? 255 : 0); break; + default: break; + } + } + + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = cState.Cross; break; + case DS4Controls.Square: result = cState.Square; break; + case DS4Controls.Triangle: result = cState.Triangle; break; + case DS4Controls.Circle: result = cState.Circle; break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = cState.L1; break; + case DS4Controls.R1: result = cState.R1; break; + case DS4Controls.L2: result = cState.L2 > 100; break; + case DS4Controls.R2: result = cState.R2 > 100; break; + case DS4Controls.L3: result = cState.L3; break; + case DS4Controls.R3: result = cState.R3; break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = cState.DpadUp; break; + case DS4Controls.DpadDown: result = cState.DpadDown; break; + case DS4Controls.DpadLeft: result = cState.DpadLeft; break; + case DS4Controls.DpadRight: result = cState.DpadRight; break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + case DS4Controls.LXPos: result = cState.LX > 128 + 55; break; + case DS4Controls.LYPos: result = cState.LY > 128 + 55; break; + case DS4Controls.RXPos: result = cState.RX > 128 + 55; break; + case DS4Controls.RYPos: result = cState.RY > 128 + 55; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (tp != null ? tp.leftDown : false); break; + case DS4Controls.TouchRight: result = (tp != null ? tp.rightDown : false); break; + case DS4Controls.TouchMulti: result = (tp != null ? tp.multiDown : false); break; + case DS4Controls.TouchUpper: result = (tp != null ? tp.upperDown : false); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (tp != null && tp.swipeUp); break; + case DS4Controls.SwipeDown: result = (tp != null && tp.swipeDown); break; + case DS4Controls.SwipeLeft: result = (tp != null && tp.swipeLeft); break; + case DS4Controls.SwipeRight: result = (tp != null && tp.swipeRight); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: result = !sOff ? SXSens[device] * -eState.AccelX > 67 : false; break; + case DS4Controls.GyroXNeg: result = !sOff ? SXSens[device] * -eState.AccelX < -67 : false; break; + case DS4Controls.GyroZPos: result = !sOff ? SZSens[device] * eState.AccelZ > 67 : false; break; + case DS4Controls.GyroZNeg: result = !sOff ? SZSens[device] * eState.AccelZ < -67 : false; break; + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.PS: result = cState.PS; break; + case DS4Controls.Share: result = cState.Share; break; + case DS4Controls.Options: result = cState.Options; break; + default: break; + } + } + + return result; + } + + private static bool getBoolMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolSpecialActionMapping(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolActionMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool analog = false) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + double angle = cState.LSAngle; + result = cState.LX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.LYNeg: + { + double angle = cState.LSAngle; + result = cState.LY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.RXNeg: + { + double angle = cState.RSAngle; + result = cState.RX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.RYNeg: + { + double angle = cState.RSAngle; + result = cState.RY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.LXPos: + { + double angle = cState.LSAngle; + result = cState.LX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.LYPos: + { + double angle = cState.LSAngle; + result = cState.LY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + case DS4Controls.RXPos: + { + double angle = cState.RSAngle; + result = cState.RX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.RYPos: + { + double angle = cState.RSAngle; + result = cState.RY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + public static bool getBoolButtonMapping(bool stateButton) + { + return stateButton; + } + + public static bool getBoolAxisDirMapping(byte stateAxis, bool positive) + { + return positive ? stateAxis > 128 + 55 : stateAxis < 128 - 55; + } + + public static bool getBoolTriggerMapping(byte stateAxis) + { + return stateAxis > 100; + } + + public static bool getBoolTouchMapping(bool touchButton) + { + return touchButton; + } + + private static byte getXYAxisMapping2(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool alt = false) + { + const byte falseVal = 128; + byte result = 0; + byte trueVal = 0; + + if (alt) + trueVal = 255; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.LYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + default: if (!alt) result = axisValue > falseVal ? (byte)(255 - axisValue) : falseVal; else result = axisValue > falseVal ? axisValue : falseVal; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + if (alt) + { + result = (byte)(128.0f + fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + else + { + result = (byte)(128.0f - fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + if (alt) + { + result = (byte)(tp != null ? 128.0f + fieldMap.swipedirs[controlNum] / 2f : 0); } - } - - if (macroControl[00]) MappedState.Cross = true; - if (macroControl[01]) MappedState.Circle = true; - if (macroControl[02]) MappedState.Square = true; - if (macroControl[03]) MappedState.Triangle = true; - if (macroControl[04]) MappedState.Options = true; - if (macroControl[05]) MappedState.Share = true; - if (macroControl[06]) MappedState.DpadUp = true; - if (macroControl[07]) MappedState.DpadDown = true; - if (macroControl[08]) MappedState.DpadLeft = true; - if (macroControl[09]) MappedState.DpadRight = true; - if (macroControl[10]) MappedState.PS = true; - if (macroControl[11]) MappedState.L1 = true; - if (macroControl[12]) MappedState.R1 = true; - if (macroControl[13]) MappedState.L2 = 255; - if (macroControl[14]) MappedState.R2 = 255; - if (macroControl[15]) MappedState.L3 = true; - if (macroControl[16]) MappedState.R3 = true; - if (macroControl[17]) MappedState.LX = 255; - if (macroControl[18]) MappedState.LX = 0; - if (macroControl[19]) MappedState.LY = 255; - if (macroControl[20]) MappedState.LY = 0; - if (macroControl[21]) MappedState.RX = 255; - if (macroControl[22]) MappedState.RX = 0; - if (macroControl[23]) MappedState.RY = 255; - if (macroControl[24]) MappedState.RY = 0; - foreach (DS4Controls dc in Cross) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Cross = true; - foreach (DS4Controls dc in Circle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Circle = true; - foreach (DS4Controls dc in Square) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Square = true; - foreach (DS4Controls dc in Triangle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Triangle = true; - foreach (DS4Controls dc in L1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L1 = true; - foreach (DS4Controls dc in L2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.L2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in L3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L3 = true; - foreach (DS4Controls dc in R1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R1 = true; - foreach (DS4Controls dc in R2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.R2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in R3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R3 = true; - foreach (DS4Controls dc in DpadUp) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadUp = true; - foreach (DS4Controls dc in DpadRight) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadRight = true; - foreach (DS4Controls dc in DpadLeft) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadLeft = true; - foreach (DS4Controls dc in DpadDown) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadDown = true; - foreach (DS4Controls dc in Options) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Options = true; - foreach (DS4Controls dc in Share) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Share = true; - foreach (DS4Controls dc in PS) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.PS = true; - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXNeg.ToString()), device, cState, eState, tp), DS4Controls.LXNeg)) - LXN.Add(DS4Controls.LXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXPos.ToString()), device, cState, eState, tp), DS4Controls.LXPos)) - LXP.Add(DS4Controls.LXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYNeg.ToString()), device, cState, eState, tp), DS4Controls.LYNeg)) - LYN.Add(DS4Controls.LYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYPos.ToString()), device, cState, eState, tp), DS4Controls.LYPos)) - LYP.Add(DS4Controls.LYPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXNeg.ToString()), device, cState, eState, tp), DS4Controls.RXNeg)) - RXN.Add(DS4Controls.RXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXPos.ToString()), device, cState, eState, tp), DS4Controls.RXPos)) - RXP.Add(DS4Controls.RXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYNeg.ToString()), device, cState, eState, tp), DS4Controls.RYNeg)) - RYN.Add(DS4Controls.RYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYPos.ToString()), device, cState, eState, tp), DS4Controls.RYPos)) - RYP.Add(DS4Controls.RYPos); - - if (Math.Abs(MappedState.LX - 127) < 10) - if (LXN.Count > 0 || LXP.Count > 0) - { - foreach (DS4Controls dc in LXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp, true); - foreach (DS4Controls dc in LXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp); - } - //else - //MappedState.LX = cState.LX; - if (Math.Abs(MappedState.LY - 127) < 10) - if (LYN.Count > 0 || LYP.Count > 0) - { - foreach (DS4Controls dc in LYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in LYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - //MappedState.LY = cState.LY; - if (Math.Abs(MappedState.RX - 127) < 10) - if (RXN.Count > 0 || RXP.Count > 0) - { - foreach (DS4Controls dc in RXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - // MappedState.RX = cState.RX; - if (Math.Abs(MappedState.RY - 127) < 10) - if (RYN.Count > 0 || RYP.Count > 0) - { - foreach (DS4Controls dc in RYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - // else - // MappedState.RY = cState.RY; - InputMethods.MoveCursorBy(MouseDeltaX, MouseDeltaY); - } + else + { + result = (byte)(tp != null ? 128.0f - fieldMap.swipedirs[controlNum] / 2f : 0); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); - private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) - { - return shift ? false : GetDS4Action(device, dc.ToString(), false) == null; - } - public static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - try { - foreach (string actionname in ProfileActions[device]) + switch (control) { - //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); - SpecialAction action = GetAction(actionname); - int index = GetActionIndexOf(actionname); - if (actionDone.Count < index + 1) - actionDone.Add(new ActionState()); - else if (actionDone.Count > GetActions().Count()) - actionDone.RemoveAt(actionDone.Count - 1); - double time; - //If a key or button is assigned to the trigger, a key special action is used like - //a quick tap to use and hold to use the regular custom button/key - bool triggerToBeTapped = action.type == "Key" && action.trigger.Count == 1 && - GetDS4Action(device, action.trigger[0].ToString(), false) == null; - if (!(action.name == "null" || index < 0)) + case DS4Controls.GyroXPos: { - bool triggeractivated = true; - if (action.delayTime > 0) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - time = action.delayTime; - nowAction[device] = DateTime.UtcNow; - if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) - triggeractivated = true; - } - else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) - oldnowAction[device] = DateTime.UtcNow; - } - else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - oldnowKeyAct[device] = DateTime.UtcNow; - } - } - else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - DateTime now = DateTime.UtcNow; - if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) - { - await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up - triggeractivated = true; - oldnowKeyAct[device] = DateTime.MinValue; - } - else if (!subtriggeractivated) - oldnowKeyAct[device] = DateTime.MinValue; - } - else - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - triggeractivated = false; - break; - } - } - - bool utriggeractivated = true; - if (action.type == "Key" && action.uTrigger.Count > 0) - { - foreach (DS4Controls dc in action.uTrigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } - } - if (action.pressRelease) utriggeractivated = !utriggeractivated; - } - - if (triggeractivated && action.type == "Program") - { - if (!actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - if (!string.IsNullOrEmpty(action.extra)) - Process.Start(action.details, action.extra); - else - Process.Start(action.details); - } - } - else if (triggeractivated && action.type == "Profile") - { - if (!actionDone[index].dev[device] && string.IsNullOrEmpty(tempprofilename[device])) - { - actionDone[index].dev[device] = true; - untriggeraction[device] = action; - untriggerindex[device] = index; - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease(ushort.Parse(action.ToString())); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - LoadTempProfile(device, action.details, true, ctrl); - return; - } - } - else if (triggeractivated && action.type == "Macro") - { - if (!actionDone[index].dev[device]) - { - DS4KeyType keyType = action.keyType; - actionDone[index].dev[device] = true; - foreach (DS4Controls dc in action.trigger) - resetToDefaultValue(dc, MappedState); - PlayMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None, keyType); - } - else - EndMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None); - } - else if (triggeractivated && action.type == "Key") + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (action.uTrigger.Count == 0 || (action.uTrigger.Count > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = index; - ushort key; - ushort.TryParse(action.details, out key); - if (action.uTrigger.Count == 0) - { - SyntheticState.KeyPresses kp; - if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) - deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - kp.current.repeatCount++; - } - else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress(key); - else - InputMethods.performKeyPress(key); - } - } - else if (action.uTrigger.Count > 0 && utriggeractivated && action.type == "Key") - { - if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = -1; - ushort key; - ushort.TryParse(action.details, out key); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(key); - else - InputMethods.performKeyRelease(key); - } - } - else if (triggeractivated && action.type == "DisconnectBT") - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!d.Charging) - { - d.DisconnectBT(); - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - return; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - string[] dets = action.details.Split('|'); - if (dets.Length == 1) - dets = action.details.Split(','); - if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) - { - Log.LogToTray("Controller " + (device + 1) + ": " + - ctrl.getDS4Battery(device), true); - } - if (bool.Parse(dets[2])) - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!actionDone[index].dev[device]) - { - lastColor[device] = d.LightBarColor; - DS4LightBar.forcelight[device] = true; - } - DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); - DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); - DS4Color trans = getTransitionedColor(empty, full, d.Battery); - if (fadetimer[device] < 100) - DS4LightBar.forcedColor[device] = getTransitionedColor(lastColor[device], trans, fadetimer[device] += 2); - } - actionDone[index].dev[device] = true; + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else if (!triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroZPos: + { + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (actionDone[index].dev[device]) - { - fadetimer[device] = 0; - /*if (prevFadetimer[device] == fadetimer[device]) - { - prevFadetimer[device] = 0; - fadetimer[device] = 0; - } - else - prevFadetimer[device] = fadetimer[device];*/ - DS4LightBar.forcelight[device] = false; - actionDone[index].dev[device] = false; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (action.type == "XboxGameDVR" || action.type == "MultiAction") + else return falseVal; + break; + } + case DS4Controls.GyroZNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) - getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; - if (getCustomMacro(device, action.trigger[0]) != "0") - getCustomMacros(device).Remove(action.trigger[0]); - if (getCustomKey(device, action.trigger[0]) != 0) - getCustomMacros(device).Remove(action.trigger[0]);*/ - string[] dets = action.details.Split(','); - DS4Device d = ctrl.DS4Controllers[device]; - //cus - if (getBoolMapping(device, action.trigger[0], cState, eState, tp) && !getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//pressed down - pastTime = DateTime.UtcNow; - if (DateTime.UtcNow <= (firstTap + TimeSpan.FromMilliseconds(150))) - { - tappedOnce = false; - secondtouchbegin = true; - } - else - firstTouch = true; - } - else if (!getBoolMapping(device, action.trigger[0], cState, eState, tp) && getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//released - if (secondtouchbegin) - { - firstTouch = false; - secondtouchbegin = false; - } - else if (firstTouch) - { - firstTouch = false; - if (DateTime.UtcNow <= (pastTime + TimeSpan.FromMilliseconds(200)) && !tappedOnce) - { - tappedOnce = true; - firstTap = DateTime.UtcNow; - TimeofEnd = DateTime.UtcNow; - } - } - } - - int type = 0; - string macro = ""; - if (tappedOnce) //single tap - { - if (action.type == "MultiAction") - { - macro = dets[0]; - } - else if (int.TryParse(dets[0], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if ((DateTime.UtcNow - TimeofEnd) > TimeSpan.FromMilliseconds(150)) - { - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - tappedOnce = false; - } - //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) - } - else if (firstTouch && (DateTime.UtcNow - pastTime) > TimeSpan.FromMilliseconds(1000)) //helddown - { - if (action.type == "MultiAction") - { - macro = dets[1]; - } - else if (int.TryParse(dets[1], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - firstTouch = false; - } - else if (secondtouchbegin) //if double tap - { - if (action.type == "MultiAction") - { - macro = dets[2]; - } - else if (int.TryParse(dets[2], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - secondtouchbegin = false; - } + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else - actionDone[index].dev[device] = false; + else result = falseVal; + break; } + default: break; } } - catch { return; } - if (untriggeraction[device] != null) + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) + { + byte result = 0; + byte trueVal = 0; + byte falseVal = 127; + + if (alt) + trueVal = 255; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) { - SpecialAction action = untriggeraction[device]; - int index = untriggerindex[device]; - bool utriggeractivated = true; - foreach (DS4Controls dc in action.uTrigger) + switch (control) { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } + case DS4Controls.Cross: result = (byte)(cState.Cross ? trueVal : falseVal); break; + case DS4Controls.Square: result = (byte)(cState.Square ? trueVal : falseVal); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? trueVal : falseVal); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? trueVal : falseVal); break; + default: break; } - - if (utriggeractivated && action.type == "Profile") + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) { - if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same - action.controls != action.ucontrols) - if (!string.IsNullOrEmpty(tempprofilename[device])) - { - foreach (DS4Controls dc in action.uTrigger) - { - actionDone[index].dev[device] = true; - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - untriggeraction[device] = null; - LoadProfile(device, false, ctrl); - } + case DS4Controls.L1: result = (byte)(cState.L1 ? trueVal : falseVal); break; + case DS4Controls.L2: if (alt) result = (byte)(128.0f + cState.L2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.L2 / 2.0078740157480315f); break; + case DS4Controls.L3: result = (byte)(cState.L3 ? trueVal : falseVal); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? trueVal : falseVal); break; + case DS4Controls.R2: if (alt) result = (byte)(128.0f + cState.R2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.R2 / 2.0078740157480315f); break; + case DS4Controls.R3: result = (byte)(cState.R3 ? trueVal : falseVal); break; + default: break; } - else - actionDone[index].dev[device] = false; } - } - - private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) - { - if (macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) { - string[] skeys; - int wait = 1000; - if (!string.IsNullOrEmpty(macro)) + switch (control) { - skeys = macro.Split('/'); - ushort delay; - if (ushort.TryParse(skeys[skeys.Length - 1], out delay) && delay > 300) - wait = delay - 300; + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? trueVal : falseVal); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? trueVal : falseVal); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? trueVal : falseVal); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? trueVal : falseVal); break; + default: break; } - AltTabSwapping(wait, device); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; } - else + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) { - string[] skeys; - int[] keys; - if (!string.IsNullOrEmpty(macro)) + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = cState.LX; else result = (byte)(255 - cState.LX); break; + case DS4Controls.LYNeg: if (!alt) result = cState.LY; else result = (byte)(255 - cState.LY); break; + case DS4Controls.RXNeg: if (!alt) result = cState.RX; else result = (byte)(255 - cState.RX); break; + case DS4Controls.RYNeg: if (!alt) result = cState.RY; else result = (byte)(255 - cState.RY); break; + case DS4Controls.LXPos: if (!alt) result = (byte)(255 - cState.LX); else result = cState.LX; break; + case DS4Controls.LYPos: if (!alt) result = (byte)(255 - cState.LY); else result = cState.LY; break; + case DS4Controls.RXPos: if (!alt) result = (byte)(255 - cState.RX); else result = cState.RX; break; + case DS4Controls.RYPos: if (!alt) result = (byte)(255 - cState.RY); else result = cState.RY; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) { - skeys = macro.Split('/'); - keys = new int[skeys.Length]; + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? trueVal : falseVal); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? trueVal : falseVal); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? trueVal : falseVal); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? trueVal : falseVal); break; + default: break; } - else + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) { - skeys = new string[0]; - keys = new int[0]; + case DS4Controls.SwipeUp: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeUpB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeUpB / 2f : 0); break; + case DS4Controls.SwipeDown: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeDownB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeDownB / 2f : 0); break; + case DS4Controls.SwipeLeft: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeLeftB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeLeftB / 2f : 0); break; + case DS4Controls.SwipeRight: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeRightB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeRightB / 2f : 0); break; + default: break; } - for (int i = 0; i < keys.Length; i++) - keys[i] = int.Parse(skeys[i]); - bool[] keydown = new bool[286]; - if (control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + + switch (control) { - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; - foreach (int i in keys) + case DS4Controls.GyroXPos: { - if (i >= 1000000000) - { - string lb = i.ToString().Substring(1); - if (i > 1000000000) - { - byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); - byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); - byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); - DS4LightBar.forcelight[device] = true; - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); - } - else - { - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - } - } - else if (i >= 1000000) - { - DS4Device d = Program.rootHub.DS4Controllers[device]; - string r = i.ToString().Substring(1); - byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); - byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); - d.setRumble(light, heavy); - } - else if (i >= 300) //ints over 300 used to delay - await Task.Delay(i - 300); - else if (!keydown[i]) + if (!sOff && -eState.AccelX > SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (i == 261) macroControl[0] = true; - else if (i == 262) macroControl[1] = true; - else if (i == 263) macroControl[2] = true; - else if (i == 264) macroControl[3] = true; - else if (i == 265) macroControl[4] = true; - else if (i == 266) macroControl[5] = true; - else if (i == 267) macroControl[6] = true; - else if (i == 268) macroControl[7] = true; - else if (i == 269) macroControl[8] = true; - else if (i == 270) macroControl[9] = true; - else if (i == 271) macroControl[10] = true; - else if (i == 272) macroControl[11] = true; - else if (i == 273) macroControl[12] = true; - else if (i == 274) macroControl[13] = true; - else if (i == 275) macroControl[14] = true; - else if (i == 276) macroControl[15] = true; - else if (i == 277) macroControl[16] = true; - else if (i == 278) macroControl[17] = true; - else if (i == 279) macroControl[18] = true; - else if (i == 280) macroControl[19] = true; - else if (i == 281) macroControl[20] = true; - else if (i == 282) macroControl[21] = true; - else if (i == 283) macroControl[22] = true; - else if (i == 284) macroControl[23] = true; - else if (i == 285) macroControl[24] = true; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress((ushort)i); - else - InputMethods.performKeyPress((ushort)i); - keydown[i] = true; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * -eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * -eState.AccelX); } - else + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (!sOff && -eState.AccelX < -SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease((ushort)i); - else - InputMethods.performKeyRelease((ushort)i); - keydown[i] = false; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * eState.AccelX); } + else result = falseVal; + break; } - for (ushort i = 0; i < keydown.Length; i++) + case DS4Controls.GyroZPos: { - if (keydown[i]) - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(i); - else - InputMethods.performKeyRelease(i); + if (!sOff && eState.AccelZ > SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * eState.AccelZ); + } + else return falseVal; + break; } - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - Program.rootHub.DS4Controllers[device].setRumble(0, 0); - if (keyType.HasFlag(DS4KeyType.HoldMacro)) + case DS4Controls.GyroZNeg: { - await Task.Delay(50); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + if (!sOff && eState.AccelZ < -SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * -eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * -eState.AccelZ); + } + else result = falseVal; + break; } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? trueVal : falseVal); break; + case DS4Controls.Options: result = (byte)(cState.Options ? trueVal : falseVal); break; + case DS4Controls.PS: result = (byte)(cState.PS ? trueVal : falseVal); break; + default: break; } } - } - private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) - { - if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) - AltTabSwappingRelease(); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + return result; } - private static void AltTabSwapping(int wait, int device) + + private static void resetToDefaultValue2(DS4Controls control, DS4State cState, + DS4StateFieldMapping fieldMap) { - if (altTabDone) + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) { - altTabDone = false; - InputMethods.performKeyPress(18); + fieldMap.buttons[controlNum] = false; } - else + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) { - altTabNow = DateTime.UtcNow; - if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) - { - oldAltTabNow = altTabNow; - InputMethods.performKeyPress(9); - InputMethods.performKeyRelease(9); - } + fieldMap.axisdirs[controlNum] = 128; + int controlRelation = (controlNum % 2 == 0 ? controlNum - 1 : controlNum + 1); + fieldMap.axisdirs[controlRelation] = 128; + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + fieldMap.triggers[controlNum] = 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + fieldMap.buttons[controlNum] = false; } } - private static void AltTabSwappingRelease() + + // SA steering wheel emulation mapping + + private const int C_WHEEL_ANGLE_PRECISION = 10; // Precision of SA angle in 1/10 of degrees + + private static readonly DS4Color calibrationColor_0 = new DS4Color { red = 0xA0, green = 0x00, blue = 0x00 }; + private static readonly DS4Color calibrationColor_1 = new DS4Color { red = 0xFF, green = 0xFF, blue = 0x00 }; + private static readonly DS4Color calibrationColor_2 = new DS4Color { red = 0x00, green = 0x50, blue = 0x50 }; + private static readonly DS4Color calibrationColor_3 = new DS4Color { red = 0x00, green = 0xC0, blue = 0x00 }; + + private static DateTime latestDebugMsgTime; + private static string latestDebugData; + private static void LogToGuiSACalibrationDebugMsg(string data, bool forceOutput = false) { - if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + // Print debug calibration log messages only once per 2 secs to avoid flooding the log receiver + DateTime curTime = DateTime.Now; + if (forceOutput || ((TimeSpan)(curTime - latestDebugMsgTime)).TotalSeconds > 2) { - altTabDone = true; - InputMethods.performKeyRelease(9); - InputMethods.performKeyRelease(18); - altTabNow = DateTime.UtcNow; - oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + latestDebugMsgTime = curTime; + if (data != latestDebugData) + { + AppLogger.LogToGui(data, false); + latestDebugData = data; + } } } - private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool down) + // Return number of bits set in a value + protected static int CountNumOfSetBits(int bitValue) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + int count = 0; + while (bitValue != 0) { - oldnow = now; - InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 51f * (down ? -1 : 1)), 0); + count++; + bitValue &= (bitValue - 1); } + return count; } - private static int getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, int mnum) + // Calculate and return the angle of the controller as -180...0...+180 value. + private static Int32 CalculateControllerAngle(int gyroAccelX, int gyroAccelZ, DS4Device controller) { - int controlnum = DS4ControltoInt(control); - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - int deadzoneL = 3; - int deadzoneR = 3; - if (LSDeadzone[device] >= 3) - deadzoneL = 0; - if (RSDeadzone[device] >= 3) - deadzoneR = 0; - double value = 0; - int speed = ButtonMouseSensitivity[device] + 15; - double root = 1.002; - double divide = 10000d; - //DateTime now = mousenow[mnum]; - switch (control) + Int32 result; + + if (gyroAccelX == controller.wheelCenterPoint.X && Math.Abs(gyroAccelZ - controller.wheelCenterPoint.Y) <= 1) { - case DS4Controls.LXNeg: - if (cState.LX - 127.5f < -deadzoneL) - value = -(cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LXPos: - if (cState.LX - 127.5f > deadzoneL) - value = (cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXNeg: - if (cState.RX - 127.5f < -deadzoneR) - value = -(cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXPos: - if (cState.RX - 127.5f > deadzoneR) - value = (cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYNeg: - if (cState.LY - 127.5f < -deadzoneL) - value = -(cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYPos: - if (cState.LY - 127.5f > deadzoneL) - value = (cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYNeg: - if (cState.RY - 127.5f < -deadzoneR) - value = -(cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYPos: - if (cState.RY - 127.5f > deadzoneR) - value = (cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.Share: value = (cState.Share ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Options: value = (cState.Options ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L1: value = (cState.L1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R1: value = (cState.R1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L3: value = (cState.L3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R3: value = (cState.R3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadUp: value = (cState.DpadUp ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadDown: value = (cState.DpadDown ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadLeft: value = (cState.DpadLeft ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadRight: value = (cState.DpadRight ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.PS: value = (cState.PS ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Cross: value = (cState.Cross ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Square: value = (cState.Square ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Triangle: value = (cState.Triangle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Circle: value = (cState.Circle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L2: value = Math.Pow(root + speed / divide, cState.L2 / 2d) - 1; break; - case DS4Controls.R2: value = Math.Pow(root + speed / divide, cState.R2 / 2d) - 1; break; - case DS4Controls.GyroXPos: return (byte)(eState.GyroX > SXD * 10 ? - Math.Pow(root + speed / divide, eState.GyroX) : 0); - case DS4Controls.GyroXNeg: return (byte)(eState.GyroX < -SXD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroX) : 0); - case DS4Controls.GyroZPos: return (byte)(eState.GyroZ > SZD * 10 ? - Math.Pow(root + speed / divide, eState.GyroZ) : 0); - case DS4Controls.GyroZNeg: return (byte)(eState.GyroZ < -SZD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroZ) : 0); - } - bool LXChanged = (Math.Abs(127 - cState.LX) < deadzoneL); - bool LYChanged = (Math.Abs(127 - cState.LY) < deadzoneL); - bool RXChanged = (Math.Abs(127 - cState.RX) < deadzoneR); - bool RYChanged = (Math.Abs(127 - cState.RY) < deadzoneR); - bool contains = (control.ToString().Contains("LX") || - control.ToString().Contains("LY") || - control.ToString().Contains("RX") || - control.ToString().Contains("RY")); - if (MouseAccel[device]) + // When the current gyro position is "close enough" the wheel center point then no need to go through the hassle of calculating an angle + result = 0; + } + else { - if (value > 0) + // Calculate two vectors based on "circle center" (ie. circle represents the 360 degree wheel turn and wheelCenterPoint and currentPosition vectors both start from circle center). + // To improve accuracy both left and right turns use a decicated calibration "circle" because DS4 gyro and DoItYourselfWheelRig may return slightly different SA sensor values depending on the tilt direction (well, only one or two degree difference so nothing major). + Point vectorAB; + Point vectorCD; + + if (gyroAccelX >= controller.wheelCenterPoint.X) { - mcounter = 34; - mouseaccel++; + // "DS4 gyro wheel" tilted to right + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointRight.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointRight.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointRight.X, gyroAccelZ - controller.wheelCircleCenterPointRight.Y); } - if (mouseaccel == prevmouseaccel) + else { - mcounter--; + // "DS4 gyro wheel" tilted to left + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointLeft.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointLeft.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointLeft.X, gyroAccelZ - controller.wheelCircleCenterPointLeft.Y); } - if (mcounter <= 0) + + // Calculate dot product and magnitude of vectors (center vector and the current tilt vector) + double dotProduct = vectorAB.X * vectorCD.X + vectorAB.Y * vectorCD.Y; + double magAB = Math.Sqrt(vectorAB.X * vectorAB.X + vectorAB.Y * vectorAB.Y); + double magCD = Math.Sqrt(vectorCD.X * vectorCD.X + vectorCD.Y * vectorCD.Y); + + // Calculate angle between vectors and convert radian to degrees + if (magAB == 0 || magCD == 0) { - mouseaccel = 0; - mcounter = 34; + result = 0; } - value *= 1 + (double)Math.Min(20000, (mouseaccel)) / 10000d; - prevmouseaccel = mouseaccel; - } - int intValue; - if (mnum > 1) - { - if ((value > 0.0 && horizontalRemainder > 0.0) || (value < 0.0 && horizontalRemainder < 0.0)) - value += horizontalRemainder; - intValue = (int)value; - horizontalRemainder = value - intValue; - } - else - { - if ((value > 0.0 && verticalRemainder > 0.0) || (value < 0.0 && verticalRemainder < 0.0)) - value += verticalRemainder; - intValue = (int)value; - verticalRemainder = value - intValue; + else + { + double angle = Math.Acos(dotProduct / (magAB * magCD)); + result = Convert.ToInt32(Global.Clamp( + -180.0 * C_WHEEL_ANGLE_PRECISION, + Math.Round((angle * (180.0 / Math.PI)), 1) * C_WHEEL_ANGLE_PRECISION, + 180.0 * C_WHEEL_ANGLE_PRECISION) + ); + } + + // Left turn is -180..0 and right turn 0..180 degrees + if (gyroAccelX < controller.wheelCenterPoint.X) result = -result; } - return intValue; + + return result; } - public static bool compare(byte b1, byte b2) + // Calibrate sixaxis steering wheel emulation. Use DS4Windows configuration screen to start a calibration or press a special action key (if defined) + private static void SAWheelEmulationCalibration(int device, DS4StateExposed exposedState, ControlService ctrl, DS4State currentDeviceState, DS4Device controller) { - if (Math.Abs(b1 - b2) > 10) + int gyroAccelX, gyroAccelZ; + int result; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // State 0=Normal mode (ie. calibration process is not running), 1=Activating calibration, 2=Calibration process running, 3=Completing calibration, 4=Cancelling calibration + if (controller.WheelRecalibrateActiveState == 1) { - return false; + AppLogger.LogToGui($"Controller {1 + device} activated re-calibration of SA steering wheel emulation", false); + + controller.WheelRecalibrateActiveState = 2; + + controller.wheelPrevPhysicalAngle = 0; + controller.wheelPrevFullAngle = 0; + controller.wheelFullTurnCount = 0; + + // Clear existing calibration value and use current position as "center" point. + // This initial center value may be off-center because of shaking the controller while button was pressed. The value will be overriden with correct value once controller is stabilized and hold still few secs at the center point + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + controller.wheel90DegPointRight.X = gyroAccelX + 20; + controller.wheel90DegPointLeft.X = gyroAccelX - 20; + + // Clear bitmask for calibration points. All three calibration points need to be set before re-calibration process is valid + controller.wheelCalibratedAxisBitmask = DS4Device.WheelCalibrationPoint.None; + + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); } - return true; - } + else if (controller.WheelRecalibrateActiveState == 3) + { + AppLogger.LogToGui($"Controller {1 + device} completed the calibration of SA steering wheel emulation. center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y})", false); - public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? 255 : 0); - case DS4Controls.Options: return (byte)(cState.Options ? 255 : 0); - case DS4Controls.L1: return (byte)(cState.L1 ? 255 : 0); - case DS4Controls.R1: return (byte)(cState.R1 ? 255 : 0); - case DS4Controls.L3: return (byte)(cState.L3 ? 255 : 0); - case DS4Controls.R3: return (byte)(cState.R3 ? 255 : 0); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? 255 : 0); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? 255 : 0); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? 255 : 0); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? 255 : 0); - case DS4Controls.PS: return (byte)(cState.PS ? 255 : 0); - case DS4Controls.Cross: return (byte)(cState.Cross ? 255 : 0); - case DS4Controls.Square: return (byte)(cState.Square ? 255 : 0); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? 255 : 0); - case DS4Controls.Circle: return (byte)(cState.Circle ? 255 : 0); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? 255 : 0); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? 255 : 0); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? 255 : 0); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? 255 : 0); - case DS4Controls.LXNeg: return (byte)(cState.LX - 127.5f > 0 ? 0 : -(cState.LX - 127.5f) * 2); - case DS4Controls.LYNeg: return (byte)(cState.LY - 127.5f > 0 ? 0 : -(cState.LY - 127.5f) * 2); - case DS4Controls.RXNeg: return (byte)(cState.RX - 127.5f > 0 ? 0 : -(cState.RX - 127.5f) * 2); - case DS4Controls.RYNeg: return (byte)(cState.RY - 127.5f > 0 ? 0 : -(cState.RY - 127.5f) * 2); - case DS4Controls.LXPos: return (byte)(cState.LX - 127.5f < 0 ? 0 : (cState.LX - 127.5f) * 2); - case DS4Controls.LYPos: return (byte)(cState.LY - 127.5f < 0 ? 0 : (cState.LY - 127.5f) * 2); - case DS4Controls.RXPos: return (byte)(cState.RX - 127.5f < 0 ? 0 : (cState.RX - 127.5f) * 2); - case DS4Controls.RYPos: return (byte)(cState.RY - 127.5f < 0 ? 0 : (cState.RY - 127.5f) * 2); - case DS4Controls.L2: return cState.L2; - case DS4Controls.R2: return cState.R2; - case DS4Controls.GyroXPos: return (byte)(!sOff && SXSens[device] * eState.GyroX > SXD * 10 ? Math.Min(255, SXSens[device] * eState.GyroX * 2) : 0); - case DS4Controls.GyroXNeg: return (byte)(!sOff && SXSens[device] * eState.GyroX < -SXD * 10 ? Math.Min(255, SXSens[device] * -eState.GyroX * 2) : 0); - case DS4Controls.GyroZPos: return (byte)(!sOff && SZSens[device] * eState.GyroZ > SZD * 10 ? Math.Min(255, SZSens[device] * eState.GyroZ * 2) : 0); - case DS4Controls.GyroZNeg: return (byte)(!sOff && SZSens[device] * eState.GyroZ < -SZD * 10 ? Math.Min(255, SZSens[device] * -eState.GyroZ * 2) : 0); - case DS4Controls.SwipeUp: return (byte)(tp != null ? tp.swipeUpB : 0); - case DS4Controls.SwipeDown: return (byte)(tp != null ? tp.swipeDownB: 0); - case DS4Controls.SwipeLeft: return (byte)(tp != null ? tp.swipeLeftB: 0); - case DS4Controls.SwipeRight: return (byte)(tp != null ? tp.swipeRightB : 0); - } - return 0; - } + // If any of the calibration points (center, left 90deg, right 90deg) are missing then reset back to default calibration values + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.All) == DS4Device.WheelCalibrationPoint.All)) + Global.SaveControllerConfigs(controller); + else + controller.wheelCenterPoint.X = controller.wheelCenterPoint.Y = 0; - public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return cState.Share; - case DS4Controls.Options: return cState.Options; - case DS4Controls.L1: return cState.L1; - case DS4Controls.R1: return cState.R1; - case DS4Controls.L3: return cState.L3; - case DS4Controls.R3: return cState.R3; - case DS4Controls.DpadUp: return cState.DpadUp; - case DS4Controls.DpadDown: return cState.DpadDown; - case DS4Controls.DpadLeft: return cState.DpadLeft; - case DS4Controls.DpadRight: return cState.DpadRight; - case DS4Controls.PS: return cState.PS; - case DS4Controls.Cross: return cState.Cross; - case DS4Controls.Square: return cState.Square; - case DS4Controls.Triangle: return cState.Triangle; - case DS4Controls.Circle: return cState.Circle; - case DS4Controls.TouchLeft: return (tp != null ? tp.leftDown : false); - case DS4Controls.TouchRight: return (tp != null ? tp.rightDown : false); - case DS4Controls.TouchMulti: return (tp != null ? tp.multiDown : false); - case DS4Controls.TouchUpper: return (tp != null ? tp.upperDown : false); - case DS4Controls.LXNeg: return cState.LX < 127 - 55; - case DS4Controls.LYNeg: return cState.LY < 127 - 55; - case DS4Controls.RXNeg: return cState.RX < 127 - 55; - case DS4Controls.RYNeg: return cState.RY < 127 - 55; - case DS4Controls.LXPos: return cState.LX > 127 + 55; - case DS4Controls.LYPos: return cState.LY > 127 + 55; - case DS4Controls.RXPos: return cState.RX > 127 + 55; - case DS4Controls.RYPos: return cState.RY > 127 + 55; - case DS4Controls.L2: return cState.L2 > 100; - case DS4Controls.R2: return cState.R2 > 100; - case DS4Controls.GyroXPos: return !sOff ? SXSens[device] * eState.GyroX > 67 : false; - case DS4Controls.GyroXNeg: return !sOff ? SXSens[device] * eState.GyroX < -67 : false; - case DS4Controls.GyroZPos: return !sOff ? SZSens[device] * eState.GyroZ > 67 : false; - case DS4Controls.GyroZNeg: return !sOff ? SZSens[device] * eState.GyroZ < -67 : false; - case DS4Controls.SwipeUp: return (tp != null && tp.swipeUp); - case DS4Controls.SwipeDown: return (tp != null && tp.swipeDown); - case DS4Controls.SwipeLeft: return (tp != null && tp.swipeLeft); - case DS4Controls.SwipeRight: return (tp != null && tp.swipeRight); - } - return false; - } + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + else if (controller.WheelRecalibrateActiveState == 4) + { + AppLogger.LogToGui($"Controller {1 + device} cancelled the calibration of SA steering wheel emulation.", false); - public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) - { - byte trueVal = 0; - byte falseVal = 127; - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - if (alt) - trueVal = 255; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? trueVal : falseVal); - case DS4Controls.Options: return (byte)(cState.Options ? trueVal : falseVal); - case DS4Controls.L1: return (byte)(cState.L1 ? trueVal : falseVal); - case DS4Controls.R1: return (byte)(cState.R1 ? trueVal : falseVal); - case DS4Controls.L3: return (byte)(cState.L3 ? trueVal : falseVal); - case DS4Controls.R3: return (byte)(cState.R3 ? trueVal : falseVal); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? trueVal : falseVal); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? trueVal : falseVal); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? trueVal : falseVal); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? trueVal : falseVal); - case DS4Controls.PS: return (byte)(cState.PS ? trueVal : falseVal); - case DS4Controls.Cross: return (byte)(cState.Cross ? trueVal : falseVal); - case DS4Controls.Square: return (byte)(cState.Square ? trueVal : falseVal); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? trueVal : falseVal); - case DS4Controls.Circle: return (byte)(cState.Circle ? trueVal : falseVal); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? trueVal : falseVal); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? trueVal : falseVal); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? trueVal : falseVal); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? trueVal : falseVal); - case DS4Controls.L2: if (alt) return (byte)(127.5f + cState.L2 / 2f); else return (byte)(127.5f - cState.L2 / 2f); - case DS4Controls.R2: if (alt) return (byte)(127.5f + cState.R2 / 2f); else return (byte)(127.5f - cState.R2 / 2f); - case DS4Controls.SwipeUp: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeUpB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeUpB / 2f : 0); - case DS4Controls.SwipeDown: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeDownB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeDownB / 2f : 0); - case DS4Controls.SwipeLeft: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeLeftB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeLeftB / 2f : 0); - case DS4Controls.SwipeRight: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeRightB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeRightB / 2f : 0); - case DS4Controls.GyroXPos: if (!sOff && eState.GyroX > SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * eState.GyroX); - else return falseVal; - case DS4Controls.GyroXNeg: if (!sOff && eState.GyroX < -SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * -eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * -eState.GyroX); - else return falseVal; - case DS4Controls.GyroZPos: if (!sOff && eState.GyroZ > SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * eState.GyroZ); - else return falseVal; - case DS4Controls.GyroZNeg: if (!sOff && eState.GyroZ < -SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * -eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * -eState.GyroZ); - else return falseVal; - } - if (!alt) + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + if (controller.WheelRecalibrateActiveState > 0) { - switch (control) + // Cross "X" key pressed. Set calibration point when the key is released and controller hold steady for a few seconds + if (currentDeviceState.Cross == true) controller.wheelPrevRecalibrateTime = DateTime.Now; + + // Make sure controller is hold steady (velocity of gyro axis) to avoid misaligments and set calibration few secs after the "X" key was released + if (Math.Abs(currentDeviceState.Motion.angVelPitch) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelYaw) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelRoll) < 0.5 + && ((TimeSpan)(DateTime.Now - controller.wheelPrevRecalibrateTime)).TotalSeconds > 1) { - case DS4Controls.LXNeg: return cState.LX; - case DS4Controls.LYNeg: return cState.LY; - case DS4Controls.RXNeg: return cState.RX; - case DS4Controls.RYNeg: return cState.RY; - case DS4Controls.LXPos: return (byte)(255 - cState.LX); - case DS4Controls.LYPos: return (byte)(255 - cState.LY); - case DS4Controls.RXPos: return (byte)(255 - cState.RX); - case DS4Controls.RYPos: return (byte)(255 - cState.RY); + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); + + if (controller.wheelCalibratedAxisBitmask == DS4Device.WheelCalibrationPoint.None) + { + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Center; + } + else if (controller.wheel90DegPointRight.X < gyroAccelX) + { + controller.wheel90DegPointRight.X = gyroAccelX; + controller.wheel90DegPointRight.Y = gyroAccelZ; + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Right90; + } + else if (controller.wheel90DegPointLeft.X > gyroAccelX) + { + controller.wheel90DegPointLeft.X = gyroAccelX; + controller.wheel90DegPointLeft.Y = gyroAccelZ; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Left90; + } } + + // Show lightbar color feedback how the calibration process is proceeding. + // red / yellow / blue / green = No calibration anchors/one anchor/two anchors/all three anchors calibrated when color turns to green (center, 90DegLeft, 90DegRight). + int bitsSet = CountNumOfSetBits((int)controller.wheelCalibratedAxisBitmask); + if (bitsSet >= 3) DS4LightBar.forcedColor[device] = calibrationColor_3; + else if (bitsSet == 2) DS4LightBar.forcedColor[device] = calibrationColor_2; + else if (bitsSet == 1) DS4LightBar.forcedColor[device] = calibrationColor_1; + else DS4LightBar.forcedColor[device] = calibrationColor_0; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Force lightbar flashing when controller is currently at calibration point (user can verify the calibration before accepting it by looking at flashing lightbar) + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Center) != 0 && Math.Abs(result) <= 1 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && result <= -89 * C_WHEEL_ANGLE_PRECISION && result >= -91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Right90) != 0 && result >= 89 * C_WHEEL_ANGLE_PRECISION && result <= 91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && Math.Abs(result) >= 179 * C_WHEEL_ANGLE_PRECISION)) + DS4LightBar.forcedFlash[device] = 2; + else + DS4LightBar.forcedFlash[device] = 0; + + DS4LightBar.forcelight[device] = true; + + LogToGuiSACalibrationDebugMsg($"Calibration values ({gyroAccelX}, {gyroAccelZ}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)}\n"); } else { - switch (control) - { - case DS4Controls.LXNeg: return (byte)(255 - cState.LX); - case DS4Controls.LYNeg: return (byte)(255 - cState.LY); - case DS4Controls.RXNeg: return (byte)(255 - cState.RX); - case DS4Controls.RYNeg: return (byte)(255 - cState.RY); - case DS4Controls.LXPos: return cState.LX; - case DS4Controls.LYPos: return cState.LY; - case DS4Controls.RXPos: return cState.RX; - case DS4Controls.RYPos: return cState.RY; - } + // Re-calibration completed or cancelled. Set lightbar color back to normal color + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = Global.getMainColor(device); + DS4LightBar.forcelight[device] = false; + DS4LightBar.updateLightBar(controller, device); } - return 0; } - //Returns false for any bool, - //if control is one of the xy axis returns 127 - //if its a trigger returns 0 - public static void resetToDefaultValue(DS4Controls control, DS4State cState) + protected static Int32 Scale360degreeGyroAxis(int device, DS4StateExposed exposedState, ControlService ctrl) { - switch (control) - { - case DS4Controls.Share: cState.Share = false; break; - case DS4Controls.Options: cState.Options = false; break; - case DS4Controls.L1: cState.L1 = false; break; - case DS4Controls.R1: cState.R1 = false; break; - case DS4Controls.L3: cState.L3 = false; break; - case DS4Controls.R3: cState.R3 = false; break; - case DS4Controls.DpadUp: cState.DpadUp = false; break; - case DS4Controls.DpadDown: cState.DpadDown = false; break; - case DS4Controls.DpadLeft: cState.DpadLeft = false; break; - case DS4Controls.DpadRight: cState.DpadRight = false; break; - case DS4Controls.PS: cState.PS = false; break; - case DS4Controls.Cross: cState.Cross = false; break; - case DS4Controls.Square: cState.Square = false; break; - case DS4Controls.Triangle: cState.Triangle = false; break; - case DS4Controls.Circle: cState.Circle = false; break; - case DS4Controls.LXNeg: cState.LX = 127; break; - case DS4Controls.LYNeg: cState.LY = 127; break; - case DS4Controls.RXNeg: cState.RX = 127; break; - case DS4Controls.RYNeg: cState.RY = 127; break; - case DS4Controls.LXPos: cState.LX = 127; break; - case DS4Controls.LYPos: cState.LY = 127; break; - case DS4Controls.RXPos: cState.RX = 127; break; - case DS4Controls.RYPos: cState.RY = 127; break; - case DS4Controls.L2: cState.L2 = 0; break; - case DS4Controls.R2: cState.R2 = 0; break; + unchecked + { + DS4Device controller; + DS4State currentDeviceState; + + int gyroAccelX, gyroAccelZ; + int result; + + controller = ctrl.DS4Controllers[device]; + if (controller == null) return 0; + + currentDeviceState = controller.getCurrentStateRef(); + + // If calibration is active then do the calibration process instead of the normal "angle calculation" + if (controller.WheelRecalibrateActiveState > 0) + { + SAWheelEmulationCalibration(device, exposedState, ctrl, currentDeviceState, controller); + + // Return center wheel position while SA wheel emuation is being calibrated + return 0; + } + + // Do nothing if connection is active but the actual DS4 controller is still missing or not yet synchronized + if (!controller.Synced) + return 0; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // If calibration values are missing then use "educated guesses" about good starting values + if (controller.wheelCenterPoint.IsEmpty) + { + if (!Global.LoadControllerConfigs(controller)) + { + AppLogger.LogToGui($"Controller {1 + device} sixaxis steering wheel calibration data missing. It is recommended to run steering wheel calibration process by pressing SASteeringWheelEmulationCalibration special action key. Using estimated values until the controller is calibrated at least once.", false); + + // Use current controller position as "center point". Assume DS4Windows was started while controller was hold in center position (yes, dangerous assumption but can't do much until controller is calibrated) + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheel90DegPointRight.X = controller.wheelCenterPoint.X + 113; + controller.wheel90DegPointRight.Y = controller.wheelCenterPoint.Y + 110; + + controller.wheel90DegPointLeft.X = controller.wheelCenterPoint.X - 127; + controller.wheel90DegPointLeft.Y = controller.wheel90DegPointRight.Y; + } + + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + AppLogger.LogToGui($"Controller {1 + device} steering wheel emulation calibration values. Center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y}) Range={Global.GetSASteeringWheelEmulationRange(device)}", false); + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + + int maxRangeRight = Global.GetSASteeringWheelEmulationRange(device) / 2 * C_WHEEL_ANGLE_PRECISION; + int maxRangeLeft = -maxRangeRight; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Apply deadzone (SA X-deadzone value). This code assumes that 20deg is the max deadzone anyone ever might wanna use (in practice effective deadzone + // is probably just few degrees by using SXDeadZone values 0.01...0.05) + double sxDead = getSXDeadzone(device); + if (sxDead > 0) + { + int sxDeadInt = Convert.ToInt32(20.0 * C_WHEEL_ANGLE_PRECISION * sxDead); + if (Math.Abs(result) <= sxDeadInt) + { + result = 0; + } + else + { + // Smooth steering angle based on deadzone range instead of just clipping the deadzone gap + result -= (result < 0 ? -sxDeadInt : sxDeadInt); + } + } + + // If wrapped around from +180 to -180 side (or vice versa) then SA steering wheel keeps on turning beyond 360 degrees (if range is >360). + // Keep track of how many times the steering wheel has been turned beyond the full 360 circle and clip the result to max range. + int wheelFullTurnCount = controller.wheelFullTurnCount; + if (controller.wheelPrevPhysicalAngle < 0 && result > 0) + { + if ((result - controller.wheelPrevPhysicalAngle) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount--; + else + result = maxRangeLeft; + } + } + else if (controller.wheelPrevPhysicalAngle > 0 && result < 0) + { + if ((controller.wheelPrevPhysicalAngle - result) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount++; + else + result = maxRangeRight; + } + } + controller.wheelPrevPhysicalAngle = result; + + if (wheelFullTurnCount != 0) + { + // Adjust value of result (steering wheel angle) based on num of full 360 turn counts + result += (wheelFullTurnCount * 180 * C_WHEEL_ANGLE_PRECISION * 2); + } + + // If the new angle is more than 180 degrees further away then this is probably bogus value (controller shaking too much and gyro and velocity sensors went crazy). + // Accept the new angle only when the new angle is within a "stability threshold", otherwise use the previous full angle value and wait for controller to be stabilized. + if (Math.Abs(result - controller.wheelPrevFullAngle) <= 180 * C_WHEEL_ANGLE_PRECISION) + { + controller.wheelPrevFullAngle = result; + controller.wheelFullTurnCount = wheelFullTurnCount; + } + else + { + result = controller.wheelPrevFullAngle; + } + + result = Mapping.ClampInt(maxRangeLeft, result, maxRangeRight); + + // Debug log output of SA sensor values + //LogToGuiSACalibrationDebugMsg($"DBG gyro=({gyroAccelX}, {gyroAccelZ}) output=({exposedState.OutputAccelX}, {exposedState.OutputAccelZ}) PitRolYaw=({currentDeviceState.Motion.gyroPitch}, {currentDeviceState.Motion.gyroRoll}, {currentDeviceState.Motion.gyroYaw}) VelPitRolYaw=({currentDeviceState.Motion.angVelPitch}, {currentDeviceState.Motion.angVelRoll}, {currentDeviceState.Motion.angVelYaw}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)} fullTurns={controller.wheelFullTurnCount}", false); + + // Apply anti-deadzone (SA X-antideadzone value) + double sxAntiDead = getSXAntiDeadzone(device); + + int outputAxisMax, outputAxisMin, outputAxisZero; + if ( Global.OutContType[device] == OutContType.DS4 ) + { + // DS4 analog stick axis supports only 0...255 output value range (not the best one for steering wheel usage) + outputAxisMax = 255; + outputAxisMin = 0; + outputAxisZero = 128; + } + else + { + // x360 (xinput) analog stick axis supports -32768...32767 output value range (more than enough for steering wheel usage) + outputAxisMax = 32767; + outputAxisMin = -32768; + outputAxisZero = 0; + } + + switch (Global.GetSASteeringWheelEmulationAxis(device)) + { + case SASteeringWheelEmulationAxisType.LX: + case SASteeringWheelEmulationAxisType.LY: + case SASteeringWheelEmulationAxisType.RX: + case SASteeringWheelEmulationAxisType.RY: + // DS4 thumbstick axis output (-32768..32767 raw value range) + //return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768); + if (result == 0) return outputAxisZero; + + if (sxAntiDead > 0) + { + sxAntiDead *= (outputAxisMax - outputAxisZero); + if (result < 0) return (((result - maxRangeLeft) * (outputAxisZero - Convert.ToInt32(sxAntiDead) - (outputAxisMin))) / (0 - maxRangeLeft)) + (outputAxisMin); + else return (((result - 0) * (outputAxisMax - (outputAxisZero + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (outputAxisZero + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (outputAxisMax - (outputAxisMin))) / (maxRangeRight - maxRangeLeft)) + (outputAxisMin); + } + + case SASteeringWheelEmulationAxisType.L2R2: + // DS4 Trigger axis output. L2+R2 triggers share the same axis in x360 xInput/DInput controller, + // so L2+R2 steering output supports only 360 turn range (-255..255 raw value range in the shared trigger axis) + if (result == 0) return 0; + + result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION))); + if (result < 0) result = -181 - result; + + if (sxAntiDead > 0) + { + sxAntiDead *= 255; + if (result < 0) return (((result - (-180)) * (-Convert.ToInt32(sxAntiDead) - (-255))) / (0 - (-180))) + (-255); + else return (((result - (0)) * (255 - (Convert.ToInt32(sxAntiDead)))) / (180 - (0))) + (Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255); + } + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2X: + case SASteeringWheelEmulationAxisType.VJoy2Y: + case SASteeringWheelEmulationAxisType.VJoy2Z: + // SASteeringWheelEmulationAxisType.VJoy1X/VJoy1Y/VJoy1Z/VJoy2X/VJoy2Y/VJoy2Z VJoy axis output (0..32767 raw value range by default) + if (result == 0) return 16384; + + if (sxAntiDead > 0) + { + sxAntiDead *= 16384; + if (result < 0) return (((result - maxRangeLeft) * (16384 - Convert.ToInt32(sxAntiDead) - (-0))) / (0 - maxRangeLeft)) + (-0); + else return (((result - 0) * (32767 - (16384 + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (16384 + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0); + } + + default: + // Should never come here, but C# case statement syntax requires DEFAULT handler + return 0; + } } } + } } diff --git a/DS4Windows/DS4Control/Mouse.cs b/DS4Windows/DS4Control/Mouse.cs index bd90856253..f939d11587 100644 --- a/DS4Windows/DS4Control/Mouse.cs +++ b/DS4Windows/DS4Control/Mouse.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Runtime.InteropServices; +using System.Drawing; namespace DS4Windows { @@ -17,12 +14,35 @@ public class Mouse : ITouchpadBehaviour private readonly MouseWheel wheel; private bool tappedOnce = false, secondtouchbegin = false; public bool swipeLeft, swipeRight, swipeUp, swipeDown; + public bool priorSwipeLeft, priorSwipeRight, priorSwipeUp, priorSwipeDown; public byte swipeLeftB, swipeRightB, swipeUpB, swipeDownB, swipedB; + public byte priorSwipeLeftB, priorSwipeRightB, priorSwipeUpB, priorSwipeDownB, priorSwipedB; public bool slideleft, slideright; + public bool priorSlideLeft, priorSlideright; // touch area stuff public bool leftDown, rightDown, upperDown, multiDown; + public bool priorLeftDown, priorRightDown, priorUpperDown, priorMultiDown; protected DS4Controls pushed = DS4Controls.None; protected Mapping.Click clicked = Mapping.Click.None; + public int CursorGyroDead { get => cursor.GyroCursorDeadZone; set => cursor.GyroCursorDeadZone = value; } + + internal const int TRACKBALL_INIT_FICTION = 10; + internal const int TRACKBALL_MASS = 45; + internal const double TRACKBALL_RADIUS = 0.0245; + + private double TRACKBALL_INERTIA = 2.0 * (TRACKBALL_MASS * TRACKBALL_RADIUS * TRACKBALL_RADIUS) / 5.0; + private double TRACKBALL_SCALE = 0.004; + private const int TRACKBALL_BUFFER_LEN = 8; + private double[] trackballXBuffer = new double[TRACKBALL_BUFFER_LEN]; + private double[] trackballYBuffer = new double[TRACKBALL_BUFFER_LEN]; + private int trackballBufferTail = 0; + private int trackballBufferHead = 0; + private double trackballAccel = 0.0; + private double trackballXVel = 0.0; + private double trackballYVel = 0.0; + private bool trackballActive = false; + private double trackballDXRemain = 0.0; + private double trackballDYRemain = 0.0; public Mouse(int deviceID, DS4Device d) { @@ -30,25 +50,279 @@ public Mouse(int deviceID, DS4Device d) dev = d; cursor = new MouseCursor(deviceNum); wheel = new MouseWheel(deviceNum); + trackballAccel = TRACKBALL_RADIUS * TRACKBALL_INIT_FICTION / TRACKBALL_INERTIA; + firstTouch = new Touch(0, 0, 0, null); + } + + public void ResetTrackAccel(double friction) + { + trackballAccel = TRACKBALL_RADIUS * friction / TRACKBALL_INERTIA; + } + + public Point GetLastMouseMovementXY() { + return cursor.GetLastMouseMovementXY(); } - public virtual void sixaxisMoved(object sender, SixAxisEventArgs arg) + public void ResetToggleGyroM() { - if (Global.UseSAforMouse[deviceNum] && Global.GyroSensitivity[deviceNum] > 0) + currentToggleGyroM = false; + previousTriggerActivated = false; + triggeractivated = false; + } + + bool triggeractivated = false; + bool previousTriggerActivated = false; + bool useReverseRatchet = false; + bool toggleGyroMouse = true; + public bool ToggleGyroMouse { get => toggleGyroMouse; + set { toggleGyroMouse = value; ResetToggleGyroM(); } } + bool currentToggleGyroM = false; + + public virtual void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs arg) + { + GyroOutMode outMode = Global.GetGyroOutMode(deviceNum); + if (outMode == GyroOutMode.Mouse && Global.getGyroSensitivity(deviceNum) > 0) { - bool triggeractivated = true; + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.getGyroTriggerTurns(deviceNum); int i = 0; - string[] ss = Global.SATriggers[deviceNum].Split(','); + string[] ss = Global.getSATriggers(deviceNum).Split(','); + bool andCond = Global.getSATriggerCond(deviceNum); + triggeractivated = andCond ? true : false; if (!string.IsNullOrEmpty(ss[0])) - foreach (string s in ss) - if (!(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { triggeractivated = false; - if (triggeractivated) + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) cursor.sixaxisMoved(arg); - dev.getCurrentState(s); + else if (!useReverseRatchet && !triggeractivated) + cursor.sixaxisMoved(arg); + else + cursor.mouseRemainderReset(); + + } + else if (outMode == GyroOutMode.MouseJoystick) + { + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.GetGyroMouseStickTriggerTurns(deviceNum); + int i = 0; + string[] ss = Global.GetSAMouseStickTriggers(deviceNum).Split(','); + bool andCond = Global.GetSAMouseStickTriggerCond(deviceNum); + triggeractivated = andCond ? true : false; + if (!string.IsNullOrEmpty(ss[0])) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + triggeractivated = false; + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) + SixMouseStick(arg); + else if (!useReverseRatchet && !triggeractivated) + SixMouseStick(arg); + else + SixMouseReset(arg); } } + private const int SMOOTH_BUFFER_LEN = 3; + private int[] xSmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int[] ySmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + + private void SixMouseReset(SixAxisEventArgs args) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0; + ySmoothBuffer[iIndex] = 0; + smoothBufferTail = iIndex + 1; + } + + private void SixMouseStick(SixAxisEventArgs arg) + { + int deltaX = 0, deltaY = 0; + deltaX = Global.getGyroMouseStickHorizontalAxis(0) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //int inputX = deltaX, inputY = deltaY; + int maxDirX = deltaX >= 0 ? 127 : -128; + int maxDirY = deltaY >= 0 ? 127 : -128; + + GyroMouseStickInfo msinfo = Global.GetGyroMouseStickInfo(deviceNum); + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + int deadzoneX = (int)Math.Abs(normX * msinfo.deadZone); + int deadzoneY = (int)Math.Abs(normY * msinfo.deadZone); + + int maxValX = signX * msinfo.maxZone; + int maxValY = signY * msinfo.maxZone; + + double xratio = 0.0, yratio = 0.0; + double antiX = msinfo.antiDeadX * normX; + double antiY = msinfo.antiDeadY * normY; + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + deltaX = (deltaX < 0 && deltaX < maxValX) ? maxValX : + (deltaX > 0 && deltaX > maxValX) ? maxValX : deltaX; + //if (deltaX != maxValX) deltaX -= deltaX % (signX * GyroMouseFuzz); + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + //if (deltaY != maxValY) deltaY -= deltaY % (signY * GyroMouseFuzz); + } + else + { + deltaY = 0; + } + + if (msinfo.useSmoothing) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = deltaX; + ySmoothBuffer[iIndex] = deltaY; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= msinfo.smoothWeight; + } + + x_out /= finalWeight; + deltaX = (int)x_out; + y_out /= finalWeight; + deltaY = (int)y_out; + + maxValX = deltaX < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxValY = deltaY < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxDirX = deltaX >= 0 ? 127 : -128; + maxDirY = deltaY >= 0 ? 127 : -128; + } + + if (msinfo.vertScale != 100) + { + double verticalScale = msinfo.vertScale * 0.01; + deltaY = (int)(deltaY * verticalScale); + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + } + + if (deltaX != 0) xratio = deltaX / (double)maxValX; + if (deltaY != 0) yratio = deltaY / (double)maxValY; + + double xNorm = 0.0, yNorm = 0.0; + if (xratio != 0.0) + { + xNorm = (1.0 - antiX) * xratio + antiX; + } + + if (yratio != 0.0) + { + yNorm = (1.0 - antiY) * yratio + antiY; + } + + if (msinfo.inverted != 0) + { + if ((msinfo.inverted & 1) == 1) + { + // Invert max dir value + maxDirX = deltaX >= 0 ? -128 : 127; + } + + if ((msinfo.inverted & 2) == 2) + { + // Invert max dir value + maxDirY = deltaY >= 0 ? -128 : 127; + } + } + + byte axisXOut = (byte)(xNorm * maxDirX + 128.0); + byte axisYOut = (byte)(yNorm * maxDirY + 128.0); + Mapping.gyroStickX[deviceNum] = axisXOut; + Mapping.gyroStickY[deviceNum] = axisYOut; + } + private bool getDS4ControlsByName(int key) { switch (key) @@ -59,30 +333,68 @@ private bool getDS4ControlsByName(int key) case 2: return s.Square; case 3: return s.Triangle; case 4: return s.L1; - case 5: return s.L2 > 127; + case 5: return s.L2 > 128; case 6: return s.R1; - case 7: return s.R2 > 127; + case 7: return s.R2 > 128; case 8: return s.DpadUp; case 9: return s.DpadDown; case 10: return s.DpadLeft; case 11: return s.DpadRight; case 12: return s.L3; case 13: return s.R3; - case 14: return s.Touch1; - case 15: return s.Touch2; + case 14: return s.Touch1Finger; + case 15: return s.Touch2Fingers; case 16: return s.Options; case 17: return s.Share; case 18: return s.PS; + default: break; } + return false; } - public virtual void touchesMoved(object sender, TouchpadEventArgs arg) + private bool tempBool = false; + public virtual void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg) { - if (!Global.UseTPforControls[deviceNum]) + s = dev.getCurrentStateRef(); + + if (Global.getUseTPforControls(deviceNum) == false) { - cursor.touchesMoved(arg, dragging || dragging2); - wheel.touchesMoved(arg, dragging || dragging2); + if (Global.GetTouchActive(deviceNum)) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = (arg.touches[0].deltaX * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballYBuffer[iIndex] = (arg.touches[0].deltaY * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + + cursor.touchesMoved(arg, dragging || dragging2, tempBool); + wheel.touchesMoved(arg, dragging || dragging2); + } + else + { + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = 0; + trackballYBuffer[iIndex] = 0; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + } } else { @@ -93,54 +405,78 @@ public virtual void touchesMoved(object sender, TouchpadEventArgs arg) if (arg.touches[0].hwY - firstTouch.hwY > 300) swipeDown = true; if (arg.touches[0].hwY - firstTouch.hwY < -300) swipeUp = true; } + swipeUpB = (byte)Math.Min(255, Math.Max(0, (firstTouch.hwY - arg.touches[0].hwY) * 1.5f)); swipeDownB = (byte)Math.Min(255, Math.Max(0, (arg.touches[0].hwY - firstTouch.hwY) * 1.5f)); swipeLeftB = (byte)Math.Min(255, Math.Max(0, firstTouch.hwX - arg.touches[0].hwX)); swipeRightB = (byte)Math.Min(255, Math.Max(0, arg.touches[0].hwX - firstTouch.hwX)); } + if (Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 50 && arg.touches.Length == 2) + { if (arg.touches[0].hwX - firstTouch.hwX > 200 && !slideleft) slideright = true; else if (firstTouch.hwX - arg.touches[0].hwX > 200 && !slideright) slideleft = true; - dev.getCurrentState(s); + } + synthesizeMouseButtons(); } - public virtual void touchesBegan(object sender, TouchpadEventArgs arg) + + public virtual void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg) { if (!Global.UseTPforControls[deviceNum]) { + Array.Clear(trackballXBuffer, 0, TRACKBALL_BUFFER_LEN); + Array.Clear(trackballYBuffer, 0, TRACKBALL_BUFFER_LEN); + trackballXVel = 0.0; + trackballYVel = 0.0; + trackballActive = false; + trackballBufferTail = 0; + trackballBufferHead = 0; + trackballDXRemain = 0.0; + trackballDYRemain = 0.0; + cursor.touchesBegan(arg); wheel.touchesBegan(arg); } + pastTime = arg.timeStamp; - firstTouch = arg.touches[0]; - if (Global.DoubleTap[deviceNum]) + firstTouch.populate(arg.touches[0].hwX, arg.touches[0].hwY, arg.touches[0].touchID, + arg.touches[0].previousTouch); + + if (Global.getDoubleTap(deviceNum)) { DateTime test = arg.timeStamp; if (test <= (firstTap + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 1.5)) && !arg.touchButtonPressed) secondtouchbegin = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } - public virtual void touchesEnded(object sender, TouchpadEventArgs arg) + + public virtual void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg) { + s = dev.getCurrentStateRef(); slideright = slideleft = false; swipeUp = swipeDown = swipeLeft = swipeRight = false; swipeUpB = swipeDownB = swipeLeftB = swipeRightB = 0; - if (Global.TapSensitivity[deviceNum] != 0 && !Global.UseTPforControls[deviceNum]) + byte tapSensitivity = Global.getTapSensitivity(deviceNum); + if (tapSensitivity != 0 && !Global.getUseTPforControls(deviceNum)) { - if (secondtouchbegin) { tappedOnce = false; secondtouchbegin = false; } + DateTime test = arg.timeStamp; - if (test <= (pastTime + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 2)) && !arg.touchButtonPressed && !tappedOnce) + if (test <= (pastTime + TimeSpan.FromMilliseconds((double)tapSensitivity * 2)) && !arg.touchButtonPressed && !tappedOnce) + { if (Math.Abs(firstTouch.hwX - arg.touches[0].hwX) < 10 && Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 10) - if (Global.DoubleTap[deviceNum]) + { + if (Global.getDoubleTap(deviceNum)) { tappedOnce = true; firstTap = arg.timeStamp; @@ -148,8 +484,97 @@ public virtual void touchesEnded(object sender, TouchpadEventArgs arg) } else Mapping.MapClick(deviceNum, Mapping.Click.Left); //this way no delay if disabled + } + } + } + else + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + if (!trackballActive) + { + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = -1; + for (int i = 0; i < TRACKBALL_BUFFER_LEN && idx != trackballBufferHead; i++) + { + idx = (trackballBufferTail - i - 1 + TRACKBALL_BUFFER_LEN) % TRACKBALL_BUFFER_LEN; + x_out += trackballXBuffer[idx] * currentWeight; + y_out += trackballYBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= 1.0; + } + + x_out /= finalWeight; + trackballXVel = x_out; + y_out /= finalWeight; + trackballYVel = y_out; + + trackballActive = true; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } } - dev.getCurrentState(s); + synthesizeMouseButtons(); } @@ -163,30 +588,98 @@ private bool isRight(Touch t) return t.hwX >= 1920 * 2 / 5; } - public virtual void touchUnchanged(object sender, EventArgs unused) + public virtual void touchUnchanged(DS4Touchpad sender, EventArgs unused) { - dev.getCurrentState(s); - //if (s.Touch1 || s.Touch2 || s.TouchButton) - synthesizeMouseButtons(); + s = dev.getCurrentStateRef(); + + if (trackballActive) + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } + + if (s.Touch1Finger || s.TouchButton) + synthesizeMouseButtons(); } - private DS4State remapped = new DS4State(); public bool dragging, dragging2; + private void synthesizeMouseButtons() { - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft.ToString(), false) == null && leftDown) + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft, false) == null && leftDown) { Mapping.MapClick(deviceNum, Mapping.Click.Left); dragging2 = true; } else + { dragging2 = false; - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper.ToString(), false) == null && upperDown) + } + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper, false) == null && upperDown) Mapping.MapClick(deviceNum, Mapping.Click.Middle); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight.ToString(), false) == null && rightDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight, false) == null && rightDown) Mapping.MapClick(deviceNum, Mapping.Click.Left); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti.ToString(), false) == null && multiDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti, false) == null && multiDown) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (!Global.UseTPforControls[deviceNum]) { if (tappedOnce) @@ -205,23 +698,22 @@ private void synthesizeMouseButtons() dragging = true; } else + { dragging = false; + } } - s = remapped; - //remapped.CopyTo(s); } - public virtual void touchButtonUp(object sender, TouchpadEventArgs arg) + public virtual void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg) { pushed = DS4Controls.None; upperDown = leftDown = rightDown = multiDown = false; - dev.setRumble(0, 0); - dev.getCurrentState(s); + s = dev.getCurrentStateRef(); if (s.Touch1 || s.Touch2) synthesizeMouseButtons(); } - public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) + public virtual void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg) { if (arg.touches == null) upperDown = true; @@ -231,15 +723,30 @@ public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) { if ((Global.LowerRCOn[deviceNum] && arg.touches[0].hwX > (1920 * 3) / 4 && arg.touches[0].hwY > (960 * 3) / 4)) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (isLeft(arg.touches[0])) leftDown = true; else if (isRight(arg.touches[0])) rightDown = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } + public void populatePriorButtonStates() + { + priorUpperDown = upperDown; + priorLeftDown = leftDown; + priorRightDown = rightDown; + priorMultiDown = multiDown; + + priorSwipeLeft = swipeLeft; priorSwipeRight = swipeRight; + priorSwipeUp = swipeUp; priorSwipeDown = swipeDown; + priorSwipeLeftB = swipeLeftB; priorSwipeRightB = swipeRightB; priorSwipeUpB = swipeUpB; + priorSwipeDownB = swipeDownB; priorSwipedB = swipedB; + } + public DS4State getDS4State() { return s; diff --git a/DS4Windows/DS4Control/MouseCursor.cs b/DS4Windows/DS4Control/MouseCursor.cs index 0a5870d3b7..d11ea56865 100644 --- a/DS4Windows/DS4Control/MouseCursor.cs +++ b/DS4Windows/DS4Control/MouseCursor.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - +using System.Drawing; namespace DS4Windows { @@ -20,37 +17,187 @@ public MouseCursor(int deviceNum) /** Indicate x/y direction for doing jitter compensation, etc. */ public enum Direction { Negative, Neutral, Positive } // Track direction vector separately and very trivially for now. - private Direction horizontalDirection = Direction.Neutral, verticalDirection = Direction.Neutral; + private Direction horizontalDirection = Direction.Neutral, + verticalDirection = Direction.Neutral; private Direction hDirection = Direction.Neutral, vDirection = Direction.Neutral; + private const double GYRO_MOUSE_COEFFICIENT = 0.0095; + public const int GYRO_MOUSE_DEADZONE = 10; + private const double GYRO_MOUSE_OFFSET = 0.1463; + private const double GYRO_SMOOTH_MOUSE_OFFSET = 0.14698; + private const double TOUCHPAD_MOUSE_OFFSET = 0.015; + + private const int SMOOTH_BUFFER_LEN = 3; + private double[] xSmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private double[] ySmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + private int gyroCursorDeadZone = GYRO_MOUSE_DEADZONE; + public int GyroCursorDeadZone { get => gyroCursorDeadZone; set => gyroCursorDeadZone = value; } + + Point lastMouseMovementXY = new Point(0, 0); + + double coefficient = 0.0; + double verticalScale = 0.0; + bool gyroSmooth = false; + + int tempInt = 0; + double tempDouble = 0.0; + bool tempBool = false; + public virtual void sixaxisMoved(SixAxisEventArgs arg) { int deltaX = 0, deltaY = 0; - deltaX = -arg.sixAxis.accelX; - deltaY = -arg.sixAxis.accelY; - //Console.WriteLine(arg.sixAxis.deltaX); - - double coefficient = Global.GyroSensitivity[deviceNumber] / 100f; - //Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - xMotion += hRemainder; - int xAction = (int)xMotion; - hRemainder += xMotion - xAction; - hRemainder -= (int)hRemainder; - double yMotion = coefficient * deltaY; - yMotion += vRemainder; - int yAction = (int)yMotion; - vRemainder += yMotion - yAction; - vRemainder -= (int)vRemainder; - if (Global.GyroInvert[deviceNumber] == 2 || Global.GyroInvert[deviceNumber] == 3) + deltaX = Global.getGyroMouseHorizontalAxis(deviceNumber) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //tempDouble = arg.sixAxis.elapsed * 0.001 * 200.0; // Base default speed on 5 ms + tempDouble = arg.sixAxis.elapsed * 200.0; // Base default speed on 5 ms + + gyroSmooth = Global.getGyroSmoothing(deviceNumber); + double gyroSmoothWeight = 0.0; + + coefficient = (Global.getGyroSensitivity(deviceNumber) * 0.01) * GYRO_MOUSE_COEFFICIENT; + double offset = GYRO_MOUSE_OFFSET; + if (gyroSmooth) + { + gyroSmoothWeight = Global.getGyroSmoothingWeight(deviceNumber); + if (gyroSmoothWeight > 0.0) + { + offset = GYRO_SMOOTH_MOUSE_OFFSET; + } + } + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + if (deltaX == 0 || (hRemainder > 0 != deltaX > 0)) + { + hRemainder = 0.0; + } + + if (deltaY == 0 || (vRemainder > 0 != deltaY > 0)) + { + vRemainder = 0.0; + } + + int deadzoneX = (int)Math.Abs(normX * gyroCursorDeadZone); + int deadzoneY = (int)Math.Abs(normY * gyroCursorDeadZone); + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + } + else + { + deltaY = 0; + } + + double xMotion = deltaX != 0 ? coefficient * (deltaX * tempDouble) + + (normX * (offset * signX)) : 0; + + int xAction = 0; + if (xMotion != 0.0) + { + xMotion += hRemainder; + } + else + { + hRemainder = 0.0; + } + + verticalScale = Global.getGyroSensVerticalScale(deviceNumber) * 0.01; + double yMotion = deltaY != 0 ? (coefficient * verticalScale) * (deltaY * tempDouble) + + (normY * (offset * signY)) : 0; + + int yAction = 0; + if (yMotion != 0.0) + { + yMotion += vRemainder; + } + else + { + vRemainder = 0.0; + } + + if (gyroSmooth) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = xMotion; + ySmoothBuffer[iIndex] = yMotion; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= gyroSmoothWeight; + } + + x_out /= finalWeight; + xMotion = x_out; + y_out /= finalWeight; + yMotion = y_out; + } + + hRemainder = vRemainder = 0.0; + if (xMotion != 0.0) + { + xAction = (int)xMotion; + hRemainder = xMotion - xAction; + } + + if (yMotion != 0.0) + { + yAction = (int)yMotion; + vRemainder = yMotion - yAction; + } + + int gyroInvert = Global.getGyroInvert(deviceNumber); + if ((gyroInvert & 0x02) == 2) xAction *= -1; - if (Global.GyroInvert[deviceNumber] == 1 || Global.GyroInvert[deviceNumber] == 3) + + if ((gyroInvert & 0x01) == 1) yAction *= -1; - if (yAction != 0 || xAction != 0) - InputMethods.MoveCursorBy(xAction, yAction); + if (!DS4Lightgun.SET_R2_AS_LIGHTGUN) { + if (yAction != 0 || xAction != 0) + InputMethods.MoveCursorBy(xAction, yAction); + } hDirection = xMotion > 0.0 ? Direction.Positive : xMotion < 0.0 ? Direction.Negative : Direction.Neutral; vDirection = yMotion > 0.0 ? Direction.Positive : yMotion < 0.0 ? Direction.Negative : Direction.Neutral; + + lastMouseMovementXY = new Point(xAction, yAction); + } + + public Point GetLastMouseMovementXY() { + return lastMouseMovementXY; + } + + public void mouseRemainderReset() + { + hRemainder = vRemainder = 0.0; + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0.0; + ySmoothBuffer[iIndex] = 0.0; + smoothBufferTail = iIndex + 1; } public void touchesBegan(TouchpadEventArgs arg) @@ -59,15 +206,18 @@ public void touchesBegan(TouchpadEventArgs arg) { horizontalRemainder = verticalRemainder = 0.0; horizontalDirection = verticalDirection = Direction.Neutral; + } } private byte lastTouchID; - public void touchesMoved(TouchpadEventArgs arg, bool dragging) + public void touchesMoved(TouchpadEventArgs arg, bool dragging, bool disableInvert = false) { - if ((!dragging && arg.touches.Length != 1) || (dragging && arg.touches.Length < 1)) + int touchesLen = arg.touches.Length; + if ((!dragging && touchesLen != 1) || (dragging && touchesLen < 1)) return; - int deltaX, deltaY; + + int deltaX = 0, deltaY = 0; if (arg.touches[0].touchID != lastTouchID) { deltaX = deltaY = 0; @@ -75,11 +225,9 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) horizontalDirection = verticalDirection = Direction.Neutral; lastTouchID = arg.touches[0].touchID; } - else if (Global.TouchpadJitterCompensation[deviceNumber]) + else { - // Often the DS4's internal jitter compensation kicks in and starts hiding changes, ironically creating jitter... - - if (dragging && arg.touches.Length > 1) + if (dragging && touchesLen > 1) { deltaX = arg.touches[1].deltaX; deltaY = arg.touches[1].deltaY; @@ -89,86 +237,75 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) deltaX = arg.touches[0].deltaX; deltaY = arg.touches[0].deltaY; } - // allow only very fine, slow motions, when changing direction, even from neutral - // TODO maybe just consume it completely? - if (deltaX <= -1) - { - if (horizontalDirection != Direction.Negative) - { - deltaX = -1; - horizontalRemainder = 0.0; - } - } - else if (deltaX >= 1) - { - if (horizontalDirection != Direction.Positive) - { - deltaX = 1; - horizontalRemainder = 0.0; - } - } - - if (deltaY <= -1) - { - if (verticalDirection != Direction.Negative) - { - deltaY = -1; - verticalRemainder = 0.0; - } - } - else if (deltaY >= 1) - { - if (verticalDirection != Direction.Positive) - { - deltaY = 1; - verticalRemainder = 0.0; - } - } } - else + + TouchMoveCursor(deltaX, deltaY, disableInvert); + } + + public void TouchMoveCursor(int dx, int dy, bool disableInvert = false) + { + double tempAngle = Math.Atan2(-dy, dx); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(dx); + int signY = Math.Sign(dy); + double coefficient = Global.getTouchSensitivity(deviceNumber) * 0.01; + bool jitterCompenstation = Global.getTouchpadJitterCompensation(deviceNumber); + + double xMotion = dx != 0 ? + coefficient * dx + (normX * (TOUCHPAD_MOUSE_OFFSET * signX)) : 0.0; + + double yMotion = dy != 0 ? + coefficient * dy + (normY * (TOUCHPAD_MOUSE_OFFSET * signY)) : 0.0; + + if (jitterCompenstation) { - if (dragging && arg.touches.Length > 1) + double absX = Math.Abs(xMotion); + if (absX <= normX * 0.15) { - deltaX = arg.touches[1].deltaX; - deltaY = arg.touches[1].deltaY; + xMotion = signX * Math.Pow(absX / 0.15f, 1.408) * 0.15; } - else + + double absY = Math.Abs(yMotion); + if (absY <= normY * 0.15) { - deltaX = arg.touches[0].deltaX; - deltaY = arg.touches[0].deltaY; + yMotion = signY * Math.Pow(absY / 0.15f, 1.408) * 0.15; } } - double coefficient = Global.TouchSensitivity[deviceNumber] / 100.0; // Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - if (xMotion > 0.0) + if (xMotion > 0.0 && horizontalRemainder > 0.0) { - if (horizontalRemainder > 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } - else if (xMotion < 0.0) + else if (xMotion < 0.0 && horizontalRemainder < 0.0) { - if (horizontalRemainder < 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } int xAction = (int)xMotion; horizontalRemainder = xMotion - xAction; - double yMotion = coefficient * deltaY; - if (yMotion > 0.0) + if (yMotion > 0.0 && verticalRemainder > 0.0) { - if (verticalRemainder > 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } - else if (yMotion < 0.0) + else if (yMotion < 0.0 && verticalRemainder < 0.0) { - if (verticalRemainder < 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } int yAction = (int)yMotion; verticalRemainder = yMotion - yAction; + if (disableInvert == false) + { + int touchpadInvert = tempInt = Global.getTouchpadInvert(deviceNumber); + if ((touchpadInvert & 0x02) == 2) + xAction *= -1; + + if ((touchpadInvert & 0x01) == 1) + yAction *= -1; + } + if (yAction != 0 || xAction != 0) InputMethods.MoveCursorBy(xAction, yAction); diff --git a/DS4Windows/DS4Control/MouseWheel.cs b/DS4Windows/DS4Control/MouseWheel.cs index d237731bca..e17ba13aef 100644 --- a/DS4Windows/DS4Control/MouseWheel.cs +++ b/DS4Windows/DS4Control/MouseWheel.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace DS4Windows { @@ -27,6 +23,7 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) { if (arg.touches.Length != 2 || dragging) return; + Touch lastT0 = arg.touches[0].previousTouch; Touch lastT1 = arg.touches[1].previousTouch; Touch T0 = arg.touches[0]; diff --git a/DS4Windows/DS4Control/OutputDevice.cs b/DS4Windows/DS4Control/OutputDevice.cs new file mode 100644 index 0000000000..3e46f526f2 --- /dev/null +++ b/DS4Windows/DS4Control/OutputDevice.cs @@ -0,0 +1,12 @@ +using System; + +namespace DS4Windows +{ + public abstract class OutputDevice + { + public abstract void ConvertandSendReport(DS4State state, int device); + public abstract void Connect(); + public abstract void Disconnect(); + public abstract string GetDeviceType(); + } +} diff --git a/DS4Windows/DS4Control/OutputSlotManager.cs b/DS4Windows/DS4Control/OutputSlotManager.cs new file mode 100644 index 0000000000..429a143e88 --- /dev/null +++ b/DS4Windows/DS4Control/OutputSlotManager.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class OutputSlotManager + { + public const int DELAY_TIME = 500; // measured in ms + + private Dictionary deviceDict = new Dictionary(); + private Dictionary revDeviceDict = new Dictionary(); + private OutputDevice[] outputDevices = new OutputDevice[4]; + private Queue actions = new Queue(); + private object actionLock = new object(); + private bool runningQueue; + + public bool RunningQueue { get => runningQueue; } + + public OutputDevice AllocateController(OutContType contType, ViGEmClient client) + { + OutputDevice outputDevice = null; + switch(contType) + { + case OutContType.X360: + outputDevice = new Xbox360OutDevice(client); + break; + case OutContType.DS4: + outputDevice = new DS4OutDevice(client); + break; + case OutContType.None: + default: + break; + } + + return outputDevice; + } + + private int FindSlot() + { + int result = -1; + for (int i = 0; i < outputDevices.Length && result == -1; i++) + { + OutputDevice tempdev = outputDevices[i]; + if (tempdev == null) + { + result = i; + } + } + + return result; + } + + private void LaunchEvents() + { + bool hasItems = false; + Action act = null; + lock (actionLock) + { + hasItems = actions.Count > 0; + } + + while (hasItems) + { + lock (actionLock) + { + act = actions.Dequeue(); + } + + act.Invoke(); + + lock (actionLock) + { + hasItems = actions.Count > 0; + } + } + } + + private void PrepareEventTask() + { + if (!runningQueue) + { + runningQueue = true; + Task.Run(() => + { + LaunchEvents(); + runningQueue = false; + }); + } + } + + public void DeferredPlugin(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs) + { + Action tempAction = new Action(() => + { + int slot = FindSlot(); + if (slot != -1) + { + outputDevice.Connect(); + outputDevices[slot] = outputDevice; + deviceDict.Add(slot, outputDevice); + revDeviceDict.Add(outputDevice, slot); + Task.Delay(DELAY_TIME).Wait(); + outdevs[inIdx] = outputDevice; + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + + public void DeferredRemoval(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs, bool immediate = false) + { + Action tempAction = new Action(() => + { + if (revDeviceDict.ContainsKey(outputDevice)) + { + int slot = revDeviceDict[outputDevice]; + outputDevices[slot] = null; + deviceDict.Remove(slot); + revDeviceDict.Remove(outputDevice); + outputDevice.Disconnect(); + outdevs[inIdx] = null; + if (!immediate) + { + Task.Delay(DELAY_TIME).Wait(); + } + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + } +} diff --git a/DS4Windows/DS4Control/ProfilePropGroups.cs b/DS4Windows/DS4Control/ProfilePropGroups.cs new file mode 100644 index 0000000000..91eee96b09 --- /dev/null +++ b/DS4Windows/DS4Control/ProfilePropGroups.cs @@ -0,0 +1,98 @@ +using System; + +namespace DS4Windows +{ + public class SquareStickInfo + { + public bool lsMode; + public bool rsMode; + public double lsRoundness = 5.0; + public double rsRoundness = 5.0; + } + + public class StickDeadZoneInfo + { + public int deadZone; + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class TriggerDeadZoneZInfo + { + public byte deadZone; // Trigger deadzone is expressed in axis units + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class GyroMouseInfo + { + + } + + public class GyroMouseStickInfo + { + public int deadZone; + public int maxZone; + public double antiDeadX; + public double antiDeadY; + public int vertScale; + // Flags representing invert axis choices + public uint inverted; + public bool useSmoothing; + public double smoothWeight; + } + + public enum LightbarMode : uint + { + None, + DS4Win, + Passthru, + } + + public class LightbarDS4WinInfo + { + public bool useCustomLed; + public bool ledAsBattery; + public DS4Color m_CustomLed = new DS4Color(0, 0, 255); + public DS4Color m_Led; + public DS4Color m_LowLed; + public DS4Color m_ChargingLed; + public DS4Color m_FlashLed; + public double rainbow; + public double maxRainbowSat = 1.0; + public int flashAt; // Battery % when flashing occurs. <0 means disabled + public byte flashType; + public int chargingType; + } + + public class LightbarSettingInfo + { + public LightbarMode mode = LightbarMode.DS4Win; + public LightbarDS4WinInfo ds4winSettings = new LightbarDS4WinInfo(); + public LightbarMode Mode + { + get => mode; + set + { + if (mode == value) return; + mode = value; + ChangedMode?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ChangedMode; + + public LightbarSettingInfo() + { + /*ChangedMode += (sender, e) => + { + if (mode != LightbarMode.DS4Win) + { + ds4winSettings = null; + } + }; + */ + } + } +} \ No newline at end of file diff --git a/DS4Windows/DS4Control/Program.cs b/DS4Windows/DS4Control/Program.cs new file mode 100644 index 0000000000..c78e7615a8 --- /dev/null +++ b/DS4Windows/DS4Control/Program.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DS4Windows +{ + public class Program + { + public static ControlService rootHub; + } +} diff --git a/DS4Windows/DS4Control/ScpDevice.Designer.cs b/DS4Windows/DS4Control/ScpDevice.Designer.cs deleted file mode 100644 index f1f8f1ed01..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpDevice - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpDevice.cs b/DS4Windows/DS4Control/ScpDevice.cs deleted file mode 100644 index ae276170c7..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.cs +++ /dev/null @@ -1,707 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Windows.Forms; - -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -namespace DS4Windows -{ - public partial class ScpDevice : Component - { - public virtual Boolean IsActive - { - get { return m_IsActive; } - } - - public virtual String Path - { - get { return m_Path; } - } - - - public ScpDevice() - { - InitializeComponent(); - } - - public ScpDevice(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - public ScpDevice(String Class) - { - InitializeComponent(); - - this.m_Class = new Guid(Class); - } - - - public virtual Boolean Open(Int32 Instance = 0) - { - String DevicePath = String.Empty; - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (Find(m_Class, ref DevicePath, Instance)) - { - Open(DevicePath); - } - - return m_IsActive; - } - - public virtual Boolean Open(String DevicePath) - { - m_Path = DevicePath.ToUpper(); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (GetDeviceHandle(m_Path)) - { - if (WinUsb_Initialize(m_FileHandle, ref m_WinUsbHandle)) - { - if (InitializeDevice()) - { - m_IsActive = true; - } - else - { - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - } - else - { - m_FileHandle.Close(); - } - } - - return m_IsActive; - } - - public virtual Boolean Start() - { - return m_IsActive; - } - - public virtual Boolean Stop() - { - m_IsActive = false; - - if (!(m_WinUsbHandle == (IntPtr) INVALID_HANDLE_VALUE)) - { - WinUsb_AbortPipe(m_WinUsbHandle, m_IntIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkOut); - - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - - if (m_FileHandle != null && !m_FileHandle.IsInvalid && !m_FileHandle.IsClosed) - { - m_FileHandle.Close(); - m_FileHandle = null; - } - - return true; - } - - public virtual Boolean Close() - { - return Stop(); - } - - - public virtual Boolean ReadIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_IntIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean ReadBulkPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_BulkIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_IntOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteBulkPipe(Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_BulkOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - - public virtual Boolean SendTransfer(Byte RequestType, Byte Request, UInt16 Value, Byte[] Buffer, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - WINUSB_SETUP_PACKET Setup = new WINUSB_SETUP_PACKET(); - - Setup.RequestType = RequestType; - Setup.Request = Request; - Setup.Value = Value; - Setup.Index = 0; - Setup.Length = (UInt16) Buffer.Length; - - return WinUsb_ControlTransfer(m_WinUsbHandle, Setup, Buffer, Buffer.Length, ref Transfered, IntPtr.Zero); - } - - - #region Constant and Structure Definitions - public const Int32 SERVICE_CONTROL_STOP = 0x00000001; - public const Int32 SERVICE_CONTROL_SHUTDOWN = 0x00000005; - public const Int32 SERVICE_CONTROL_DEVICEEVENT = 0x0000000B; - public const Int32 SERVICE_CONTROL_POWEREVENT = 0x0000000D; - - public const Int32 DBT_DEVICEARRIVAL = 0x8000; - public const Int32 DBT_DEVICEQUERYREMOVE = 0x8001; - public const Int32 DBT_DEVICEREMOVECOMPLETE = 0x8004; - public const Int32 DBT_DEVTYP_DEVICEINTERFACE = 0x0005; - public const Int32 DBT_DEVTYP_HANDLE = 0x0006; - - public const Int32 PBT_APMRESUMEAUTOMATIC = 0x0012; - public const Int32 PBT_APMSUSPEND = 0x0004; - - public const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0x0000; - public const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 0x0001; - public const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x0004; - - public const Int32 WM_DEVICECHANGE = 0x0219; - - public const Int32 DIGCF_PRESENT = 0x0002; - public const Int32 DIGCF_DEVICEINTERFACE = 0x0010; - - public delegate Int32 ServiceControlHandlerEx(Int32 Control, Int32 Type, IntPtr Data, IntPtr Context); - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_DEVICEINTERFACE - { - internal Int32 dbcc_size; - internal Int32 dbcc_devicetype; - internal Int32 dbcc_reserved; - internal Guid dbcc_classguid; - internal Int16 dbcc_name; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public class DEV_BROADCAST_DEVICEINTERFACE_M - { - public Int32 dbcc_size; - public Int32 dbcc_devicetype; - public Int32 dbcc_reserved; - - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] - public Byte[] dbcc_classguid; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] - public Char[] dbcc_name; - } - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_HDR - { - public Int32 dbch_size; - public Int32 dbch_devicetype; - public Int32 dbch_reserved; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_DEVICE_INTERFACE_DATA - { - internal Int32 cbSize; - internal Guid InterfaceClassGuid; - internal Int32 Flags; - internal IntPtr Reserved; - } - - protected const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80; - protected const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; - protected const UInt32 FILE_SHARE_READ = 1; - protected const UInt32 FILE_SHARE_WRITE = 2; - protected const UInt32 GENERIC_READ = 0x80000000; - protected const UInt32 GENERIC_WRITE = 0x40000000; - protected const Int32 INVALID_HANDLE_VALUE = -1; - protected const UInt32 OPEN_EXISTING = 3; - protected const UInt32 DEVICE_SPEED = 1; - protected const Byte USB_ENDPOINT_DIRECTION_MASK = 0x80; - - protected enum POLICY_TYPE - { - SHORT_PACKET_TERMINATE = 1, - AUTO_CLEAR_STALL = 2, - PIPE_TRANSFER_TIMEOUT = 3, - IGNORE_SHORT_PACKETS = 4, - ALLOW_PARTIAL_READS = 5, - AUTO_FLUSH = 6, - RAW_IO = 7, - } - - protected enum USBD_PIPE_TYPE - { - UsbdPipeTypeControl = 0, - UsbdPipeTypeIsochronous = 1, - UsbdPipeTypeBulk = 2, - UsbdPipeTypeInterrupt = 3, - } - - protected enum USB_DEVICE_SPEED - { - UsbLowSpeed = 1, - UsbFullSpeed = 2, - UsbHighSpeed = 3, - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_CONFIGURATION_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal UInt16 wTotalLength; - internal Byte bNumInterfaces; - internal Byte bConfigurationValue; - internal Byte iConfiguration; - internal Byte bmAttributes; - internal Byte MaxPower; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_INTERFACE_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal Byte bInterfaceNumber; - internal Byte bAlternateSetting; - internal Byte bNumEndpoints; - internal Byte bInterfaceClass; - internal Byte bInterfaceSubClass; - internal Byte bInterfaceProtocol; - internal Byte iInterface; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct WINUSB_PIPE_INFORMATION - { - internal USBD_PIPE_TYPE PipeType; - internal Byte PipeId; - internal UInt16 MaximumPacketSize; - internal Byte Interval; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - protected struct WINUSB_SETUP_PACKET - { - internal Byte RequestType; - internal Byte Request; - internal UInt16 Value; - internal UInt16 Index; - internal UInt16 Length; - } - - protected const Int32 DIF_PROPERTYCHANGE = 0x12; - protected const Int32 DICS_ENABLE = 1; - protected const Int32 DICS_DISABLE = 2; - protected const Int32 DICS_PROPCHANGE = 3; - protected const Int32 DICS_FLAG_GLOBAL = 1; - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_CLASSINSTALL_HEADER - { - internal Int32 cbSize; - internal Int32 InstallFunction; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_PROPCHANGE_PARAMS - { - internal SP_CLASSINSTALL_HEADER ClassInstallHeader; - internal Int32 StateChange; - internal Int32 Scope; - internal Int32 HwProfile; - } - #endregion - - #region Protected Data Members - protected Guid m_Class = Guid.Empty; - protected String m_Path = String.Empty; - - protected SafeFileHandle m_FileHandle = null; - protected IntPtr m_WinUsbHandle = IntPtr.Zero; - - protected Byte m_IntIn = 0xFF; - protected Byte m_IntOut = 0xFF; - protected Byte m_BulkIn = 0xFF; - protected Byte m_BulkOut = 0xFF; - - protected Boolean m_IsActive = false; - #endregion - - #region Static Helper Methods - public enum Notified { Ignore = 0x0000, Arrival = 0x8000, QueryRemove = 0x8001, Removal = 0x8004 }; - - public static Boolean RegisterNotify(IntPtr Form, Guid Class, ref IntPtr Handle, Boolean Window = true) - { - IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; - - try - { - DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); - Int32 Size = Marshal.SizeOf(devBroadcastDeviceInterface); - - devBroadcastDeviceInterface.dbcc_size = Size; - devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - devBroadcastDeviceInterface.dbcc_reserved = 0; - devBroadcastDeviceInterface.dbcc_classguid = Class; - - devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(Size); - Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); - - Handle = RegisterDeviceNotification(Form, devBroadcastDeviceInterfaceBuffer, Window ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE); - - Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); - - return Handle != IntPtr.Zero; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) - { - Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); - } - } - } - - public static Boolean UnregisterNotify(IntPtr Handle) - { - try - { - return UnregisterDeviceNotification(Handle); - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - #endregion - - #region Protected Methods - protected virtual Boolean Find(Guid Target, ref String Path, Int32 Instance = 0) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref Target, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref Target, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4: detailDataBuffer.ToInt64() + 4); - - Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (memberIndex == Instance) return true; - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceInstance(ref String Instance) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref m_Class, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4 : detailDataBuffer.ToInt64() + 4); - - String Current = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (Current == Path) - { - Int32 nBytes = 256; - IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); - - CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0); - Instance = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper(); - - Marshal.FreeHGlobal(ptrInstanceBuf); - return true; - } - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceHandle(String Path) - { - m_FileHandle = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); - - return !m_FileHandle.IsInvalid; - } - - protected virtual Boolean UsbEndpointDirectionIn(Int32 addr) - { - return (addr & 0x80) == 0x80; - } - - protected virtual Boolean UsbEndpointDirectionOut(Int32 addr) - { - return (addr & 0x80) == 0x00; - } - - protected virtual Boolean InitializeDevice() - { - try - { - USB_INTERFACE_DESCRIPTOR ifaceDescriptor = new USB_INTERFACE_DESCRIPTOR(); - WINUSB_PIPE_INFORMATION pipeInfo = new WINUSB_PIPE_INFORMATION(); - - if (WinUsb_QueryInterfaceSettings(m_WinUsbHandle, 0, ref ifaceDescriptor)) - { - for (Int32 i = 0; i < ifaceDescriptor.bNumEndpoints; i++) - { - WinUsb_QueryPipe(m_WinUsbHandle, 0, System.Convert.ToByte(i), ref pipeInfo); - - if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionIn(pipeInfo.PipeId))) - { - m_BulkIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkIn); - } - else if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionOut(pipeInfo.PipeId))) - { - m_BulkOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkOut); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionIn(pipeInfo.PipeId)) - { - m_IntIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntIn); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionOut(pipeInfo.PipeId)) - { - m_IntOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntOut); - } - } - - return true; - } - - return false; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - - protected virtual Boolean RestartDevice(String InstanceId) - { - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); - - deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if (SetupDiOpenDeviceInfo(deviceInfoSet, InstanceId, IntPtr.Zero, 0, ref deviceInterfaceData)) - { - SP_PROPCHANGE_PARAMS props = new SP_PROPCHANGE_PARAMS(); - - props.ClassInstallHeader = new SP_CLASSINSTALL_HEADER(); - props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader); - props.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; - - props.Scope = DICS_FLAG_GLOBAL; - props.StateChange = DICS_PROPCHANGE; - props.HwProfile = 0x00; - - if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, Marshal.SizeOf(props))) - { - return SetupDiChangeState(deviceInfoSet, ref deviceInterfaceData); - } - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - #endregion - - #region Interop Definitions - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - protected static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); - - [DllImport("user32.dll", SetLastError = true)] - protected static extern Boolean UnregisterDeviceNotification(IntPtr Handle); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UInt32 hTemplateFile); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, ref USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, ref WINUSB_PIPE_INFORMATION PipeInformation); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_AbortPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_FlushPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ReadPipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_WritePipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Free(IntPtr InterfaceHandle); - - [DllImport("advapi32.dll", SetLastError = true)] - public static extern IntPtr RegisterServiceCtrlHandlerEx(String ServiceName, ServiceControlHandlerEx Callback, IntPtr Context); - - [DllImport("kernel32.dll", SetLastError = true)] - protected static extern Boolean DeviceIoControl(SafeFileHandle DeviceHandle, Int32 IoControlCode, Byte[] InBuffer, Int32 InBufferSize, Byte[] OutBuffer, Int32 OutBufferSize, ref Int32 BytesReturned, IntPtr Overlapped); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Int32 CM_Get_Device_ID(Int32 dnDevInst, IntPtr Buffer, Int32 BufferLen, Int32 ulFlags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, String DeviceInstanceId, IntPtr hwndParent, Int32 Flags, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiChangeState(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_PROPCHANGE_PARAMS ClassInstallParams, Int32 ClassInstallParamsSize); - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.Designer.cs b/DS4Windows/DS4Control/ScpHub.Designer.cs deleted file mode 100644 index ed08d0c1c0..0000000000 --- a/DS4Windows/DS4Control/ScpHub.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpHub - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.cs b/DS4Windows/DS4Control/ScpHub.cs deleted file mode 100644 index fd7031a495..0000000000 --- a/DS4Windows/DS4Control/ScpHub.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.ComponentModel; - -namespace DS4Windows -{ - public partial class ScpHub : Component - { - protected IntPtr m_Reference = IntPtr.Zero; - protected volatile Boolean m_Started = false; - - public event EventHandler Debug = null; - - public event EventHandler Report = null; - - protected virtual Boolean LogDebug(String Data, bool warning) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - - On_Debug(this, args); - - return true; - } - - public Boolean Active - { - get { return m_Started; } - } - - - public ScpHub() - { - InitializeComponent(); - } - - public ScpHub(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - - public virtual Boolean Open() - { - return true; - } - - public virtual Boolean Start() - { - return m_Started; - } - - public virtual Boolean Stop() - { - return !m_Started; - } - - public virtual Boolean Close() - { - if (m_Reference != IntPtr.Zero) ScpDevice.UnregisterNotify(m_Reference); - - return !m_Started; - } - - - public virtual Boolean Suspend() - { - return true; - } - - public virtual Boolean Resume() - { - return true; - } - - protected virtual void On_Debug(object sender, DebugEventArgs e) - { - if (Debug != null) Debug(sender, e); - } - - - protected virtual void On_Report(object sender, ReportEventArgs e) - { - if (Report != null) Report(sender, e); - } - } -} diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs index 2b23fcb603..f755a0e422 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.IO; using System.Reflection; @@ -8,18 +8,34 @@ using System.Drawing; using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; +using System.Diagnostics; + namespace DS4Windows { [Flags] - public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; //Increment by exponents of 2*, starting at 2^0 + public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; // Increment by exponents of 2*, starting at 2^0 public enum Ds3PadId : byte { None = 0xFF, One = 0x00, Two = 0x01, Three = 0x02, Four = 0x03, All = 0x04 }; public enum DS4Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, L1, L2, L3, R1, R2, R3, Square, Triangle, Circle, Cross, DpadUp, DpadRight, DpadDown, DpadLeft, PS, TouchLeft, TouchUpper, TouchMulti, TouchRight, Share, Options, GyroXPos, GyroXNeg, GyroZPos, GyroZNeg, SwipeLeft, SwipeRight, SwipeUp, SwipeDown }; public enum X360Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, LB, LT, LS, RB, RT, RS, X, Y, B, A, DpadUp, DpadRight, DpadDown, DpadLeft, Guide, Back, Start, LeftMouse, RightMouse, MiddleMouse, FourthMouse, FifthMouse, WUP, WDOWN, MouseUp, MouseDown, MouseLeft, MouseRight, Unbound }; + public enum SASteeringWheelEmulationAxisType: byte { None = 0, LX, LY, RX, RY, L2R2, VJoy1X, VJoy1Y, VJoy1Z, VJoy2X, VJoy2Y, VJoy2Z }; + public enum OutContType : uint { None = 0, X360, DS4 } + + public enum GyroOutMode : uint + { + None, + Controls, + Mouse, + MouseJoystick, + } + public class DS4ControlSettings { public DS4Controls control; - public string extras = "0,0,0,0,0,0,0,0"; + public string extras = null; public DS4KeyType keyType = DS4KeyType.None; public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType actionType = ActionType.Default; @@ -27,7 +43,7 @@ public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType shiftActionType = ActionType.Default; public object shiftAction = null; public int shiftTrigger = 0; - public string shiftExtras = "0,0,0,0,0,0,0,0"; + public string shiftExtras = null; public DS4KeyType shiftKeyType = DS4KeyType.None; public DS4ControlSettings(DS4Controls ctrl) @@ -37,17 +53,17 @@ public DS4ControlSettings(DS4Controls ctrl) public void Reset() { - extras = "0,0,0,0,0,0,0,0"; + extras = null; keyType = DS4KeyType.None; actionType = ActionType.Default; action = null; shiftActionType = ActionType.Default; shiftAction = null; shiftTrigger = 0; - shiftExtras = "0,0,0,0,0,0,0,0"; + shiftExtras = null; shiftKeyType = DS4KeyType.None; } - + internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, int trigger = 0) { if (!shift) @@ -60,6 +76,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, actionType = ActionType.Macro; else actionType = ActionType.Default; + action = act; extras = exts; keyType = kt; @@ -74,6 +91,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, shiftActionType = ActionType.Macro; else shiftActionType = ActionType.Default; + shiftAction = act; shiftExtras = exts; shiftKeyType = kt; @@ -85,16 +103,16 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, public class DebugEventArgs : EventArgs { protected DateTime m_Time = DateTime.Now; - protected String m_Data = String.Empty; + protected string m_Data = string.Empty; protected bool warning = false; - public DebugEventArgs(String Data, bool warn) + public DebugEventArgs(string Data, bool warn) { m_Data = Data; warning = warn; } public DateTime Time => m_Time; - public String Data => m_Data; + public string Data => m_Data; public bool Warning => warning; } @@ -113,7 +131,7 @@ public MappingDoneEventArgs(int DeviceID) public class ReportEventArgs : EventArgs { protected Ds3PadId m_Pad = Ds3PadId.None; - protected Byte[] m_Report = new Byte[64]; + protected byte[] m_Report = new byte[64]; public ReportEventArgs() { @@ -136,20 +154,337 @@ public Byte[] Report } } + public class BatteryReportArgs : EventArgs + { + private int index; + private int level; + private bool charging; + + public BatteryReportArgs(int index, int level, bool charging) + { + this.index = index; + this.level = level; + this.charging = charging; + } + + public int getIndex() + { + return index; + } + + public int getLevel() + { + return level; + } + + public bool isCharging() + { + return charging; + } + } + + public class ControllerRemovedArgs : EventArgs + { + private int index; + + public ControllerRemovedArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return this.index; + } + } + + public class DeviceStatusChangeEventArgs : EventArgs + { + private int index; + + public DeviceStatusChangeEventArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return index; + } + } + + public class SerialChangeArgs : EventArgs + { + private int index; + private string serial; + + public SerialChangeArgs(int index, string serial) + { + this.index = index; + this.serial = serial; + } + + public int getIndex() + { + return index; + } + + public string getSerial() + { + return serial; + } + } + public class Global { protected static BackingStore m_Config = new BackingStore(); protected static Int32 m_IdleTimeout = 600000; - static string exepath = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName; + public static string exelocation = Assembly.GetExecutingAssembly().Location; + public static string exedirpath = Directory.GetParent(exelocation).FullName; + public static FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(exelocation); + public static string exeversion = fileVersion.ProductVersion; public static string appdatapath; + public static bool firstRun = false; + public static bool multisavespots = false; + public static string appDataPpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Windows"; + public static bool runHotPlug = false; public static string[] tempprofilename = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public static bool[] useTempProfile = new bool[5] { false, false, false, false, false }; + public static bool[] tempprofileDistance = new bool[5] { false, false, false, false, false }; + public static bool[] useDInputOnly = new bool[5] { true, true, true, true, true }; + public static bool[] linkedProfileCheck = new bool[4] { false, false, false, false }; + public static bool[] touchpadActive = new bool[5] { true, true, true, true, true }; + // Used to hold device type desired from Profile Editor + public static OutContType[] outDevTypeTemp = new OutContType[5] { DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360 }; + // Used to hold the currently active controller output type in use for a slot + public static OutContType[] activeOutDevType = new OutContType[5] { DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None }; + public static bool vigemInstalled = IsViGEmBusInstalled(); + public static bool hidguardInstalled = IsHidGuardianInstalled(); + public static string vigembusVersion = ViGEmBusVersion(); + + public static X360Controls[] defaultButtonMapping = { X360Controls.None, X360Controls.LXNeg, X360Controls.LXPos, + X360Controls.LYNeg, X360Controls.LYPos, X360Controls.RXNeg, X360Controls.RXPos, X360Controls.RYNeg, X360Controls.RYPos, + X360Controls.LB, X360Controls.LT, X360Controls.LS, X360Controls.RB, X360Controls.RT, X360Controls.RS, X360Controls.X, + X360Controls.Y, X360Controls.B, X360Controls.A, X360Controls.DpadUp, X360Controls.DpadRight, X360Controls.DpadDown, + X360Controls.DpadLeft, X360Controls.Guide, X360Controls.LeftMouse, X360Controls.MiddleMouse, X360Controls.RightMouse, X360Controls.LeftMouse, + X360Controls.Back, X360Controls.Start, X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None, + X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None + }; + + // Create mapping array at runtime + public static DS4Controls[] reverseX360ButtonMapping = new Func(() => + { + DS4Controls[] temp = new DS4Controls[defaultButtonMapping.Length]; + for (int i = 0, arlen = defaultButtonMapping.Length; i < arlen; i++) + { + X360Controls mapping = defaultButtonMapping[i]; + if (mapping != X360Controls.None) + { + temp[(int)mapping] = (DS4Controls)i; + } + } + + return temp; + })(); + + public static Dictionary xboxDefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "Left Bumper", + [X360Controls.LT] = "Left Trigger", + [X360Controls.LS] = "Left Stick", + [X360Controls.RB] = "Right Bumper", + [X360Controls.RT] = "Right Trigger", + [X360Controls.RS] = "Right Stick", + [X360Controls.X] = "X Button", + [X360Controls.Y] = "Y Button", + [X360Controls.B] = "B Button", + [X360Controls.A] = "A Button", + [X360Controls.DpadUp] = "Up Button", + [X360Controls.DpadRight] = "Right Button", + [X360Controls.DpadDown] = "Down Button", + [X360Controls.DpadLeft] = "Left Button", + [X360Controls.Guide] = "Guide", + [X360Controls.Back] = "Back", + [X360Controls.Start] = "Start", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static Dictionary ds4DefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "L1", + [X360Controls.LT] = "L2", + [X360Controls.LS] = "L3", + [X360Controls.RB] = "R1", + [X360Controls.RT] = "R2", + [X360Controls.RS] = "R3", + [X360Controls.X] = "Square", + [X360Controls.Y] = "Triangle", + [X360Controls.B] = "Circle", + [X360Controls.A] = "Cross", + [X360Controls.DpadUp] = "Dpad Up", + [X360Controls.DpadRight] = "Dpad Right", + [X360Controls.DpadDown] = "Dpad Down", + [X360Controls.DpadLeft] = "Dpad Left", + [X360Controls.Guide] = "PS", + [X360Controls.Back] = "Share", + [X360Controls.Start] = "Options", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static string getX360ControlString(X360Controls key, OutContType conType) + { + string result = string.Empty; + if (conType == DS4Windows.OutContType.X360) + { + xboxDefaultNames.TryGetValue(key, out result); + } + else if (conType == DS4Windows.OutContType.DS4) + { + ds4DefaultNames.TryGetValue(key, out result); + } + + return result; + } + + public static Dictionary ds4inputNames = new Dictionary() + { + [DS4Controls.LXNeg] = "Left X-Axis-", + [DS4Controls.LXPos] = "Left X-Axis+", + [DS4Controls.LYNeg] = "Left Y-Axis-", + [DS4Controls.LYPos] = "Left Y-Axis+", + [DS4Controls.RXNeg] = "Right X-Axis-", + [DS4Controls.RXPos] = "Right X-Axis+", + [DS4Controls.RYNeg] = "Right Y-Axis-", + [DS4Controls.RYPos] = "Right Y-Axis+", + [DS4Controls.L1] = "L1", + [DS4Controls.L2] = "L2", + [DS4Controls.L3] = "L3", + [DS4Controls.R1] = "R1", + [DS4Controls.R2] = "R2", + [DS4Controls.R3] = "R3", + [DS4Controls.Square] = "Square", + [DS4Controls.Triangle] = "Triangle", + [DS4Controls.Circle] = "Circle", + [DS4Controls.Cross] = "Cross", + [DS4Controls.DpadUp] = "Dpad Up", + [DS4Controls.DpadRight] = "Dpad Right", + [DS4Controls.DpadDown] = "Dpad Down", + [DS4Controls.DpadLeft] = "Dpad Left", + [DS4Controls.PS] = "PS", + [DS4Controls.Share] = "Share", + [DS4Controls.Options] = "Options", + [DS4Controls.TouchLeft] = "Left Touch", + [DS4Controls.TouchUpper] = "Upper Touch", + [DS4Controls.TouchMulti] = "Multitouch", + [DS4Controls.TouchRight] = "Right Touch", + [DS4Controls.GyroXPos] = "Gyro X+", + [DS4Controls.GyroXNeg] = "Gyro X-", + [DS4Controls.GyroZPos] = "Gyro Z+", + [DS4Controls.GyroZNeg] = "Gyro Z-", + [DS4Controls.SwipeLeft] = "Swipe Left", + [DS4Controls.SwipeRight] = "Swipe Right", + [DS4Controls.SwipeUp] = "Swipe Up", + [DS4Controls.SwipeDown] = "Swipe Down", + }; + + public static Dictionary macroDS4Values = new Dictionary() + { + [DS4Controls.Cross] = 261, [DS4Controls.Circle] = 262, + [DS4Controls.Square] = 263, [DS4Controls.Triangle] = 264, + [DS4Controls.Options] = 265, [DS4Controls.Share] = 266, + [DS4Controls.DpadUp] = 267, [DS4Controls.DpadDown] = 268, + [DS4Controls.DpadLeft] = 269, [DS4Controls.DpadRight] = 270, + [DS4Controls.PS] = 271, [DS4Controls.L1] = 272, + [DS4Controls.R1] = 273, [DS4Controls.L2] = 274, + [DS4Controls.R2] = 275, [DS4Controls.L3] = 276, + [DS4Controls.R3] = 277, [DS4Controls.LXPos] = 278, + [DS4Controls.LXNeg] = 279, [DS4Controls.LYPos] = 280, + [DS4Controls.LYNeg] = 281, [DS4Controls.RXPos] = 282, + [DS4Controls.RXNeg] = 283, [DS4Controls.RYPos] = 284, + [DS4Controls.RYNeg] = 285, + }; public static void SaveWhere(string path) { appdatapath = path; m_Config.m_Profile = appdatapath + "\\Profiles.xml"; m_Config.m_Actions = appdatapath + "\\Actions.xml"; + m_Config.m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + m_Config.m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; + } + + public static bool SaveDefault(string path) + { + Boolean Saved = true; + XmlDocument m_Xdoc = new XmlDocument(); + try + { + XmlNode Node; + + m_Xdoc.RemoveAll(); + + Node = m_Xdoc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateComment(string.Format(" Profile Configuration Data. {0} ", DateTime.Now)); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateWhitespace("\r\n"); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateNode(XmlNodeType.Element, "Profile", null); + + m_Xdoc.AppendChild(Node); + + m_Xdoc.Save(path); + } + catch { Saved = false; } + + return Saved; } + /// /// Check if Admin Rights are needed to write in Appliplation Directory /// @@ -158,8 +493,8 @@ public static bool AdminNeeded() { try { - File.WriteAllText(exepath + "\\test.txt", "test"); - File.Delete(exepath + "\\test.txt"); + File.WriteAllText(exedirpath + "\\test.txt", "test"); + File.Delete(exedirpath + "\\test.txt"); return false; } catch (UnauthorizedAccessException) @@ -175,6 +510,230 @@ public static bool IsAdministrator() return principal.IsInRole(WindowsBuiltInRole.Administrator); } + public static bool CheckForDevice(string guid) + { + bool result = false; + Guid deviceGuid = Guid.Parse(guid); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + result = NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static bool CheckForSysDevice(string searchHardwareId) + { + bool result = false; + Guid sysGuid = Guid.Parse("{4d36e97d-e325-11ce-bfc1-08002be10318}"); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref sysGuid, null, 0, 0); + for (int i = 0; !result && NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++) + { + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, + ref NativeMethods.DEVPKEY_Device_HardwareIds, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + string hardwareId = dataBuffer.ToUTF16String(); + //if (hardwareIds.Contains("Virtual Gamepad Emulation Bus")) + // result = true; + if (hardwareId.Equals(searchHardwareId)) + result = true; + } + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + internal static string GetDeviceProperty(string deviceInstanceId, + NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + NativeMethods.SP_DEVINFO_DATA deviceInfoData = new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + Guid hidGuid = new Guid(); + NativeMethods.HidD_GetHidGuid(ref hidGuid); + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static string GetViGEmDriverProperty(NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + Guid deviceGuid = Guid.Parse(VIGEMBUS_GUID); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + public static bool IsHidGuardianInstalled() + { + return CheckForSysDevice(@"Root\HidGuardian"); + } + + const string VIGEMBUS_GUID = "{96E42B22-F5E9-42F8-B043-ED0F932F014F}"; + public static bool IsViGEmBusInstalled() + { + return CheckForDevice(VIGEMBUS_GUID); + } + + public static string ViGEmBusVersion() + { + return GetViGEmDriverProperty(NativeMethods.DEVPKEY_Device_DriverVersion); + } + + public static void FindConfigLocation() + { + if (File.Exists(exedirpath + "\\Auto Profiles.xml") + && File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = true; + } + else if (File.Exists(exedirpath + "\\Auto Profiles.xml")) + SaveWhere(exedirpath); + else if (File.Exists(appDataPpath + "\\Auto Profiles.xml")) + SaveWhere(appDataPpath); + else if (!File.Exists(exedirpath + "\\Auto Profiles.xml") + && !File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = false; + } + } + + public static void SetCulture(string culture) + { + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo(culture); + } + catch { /* Skip setting culture that we cannot set */ } + } + + public static void CreateStdActions() + { + XmlDocument xDoc = new XmlDocument(); + try + { + string[] profiles = Directory.GetFiles(appdatapath + @"\Profiles\"); + string s = string.Empty; + //foreach (string s in profiles) + for (int i = 0, proflen = profiles.Length; i < proflen; i++) + { + s = profiles[i]; + if (Path.GetExtension(s) == ".xml") + { + xDoc.Load(s); + XmlNode el = xDoc.SelectSingleNode("DS4Windows/ProfileActions"); + if (el != null) + { + if (string.IsNullOrEmpty(el.InnerText)) + el.InnerText = "Disconnect Controller"; + else + el.InnerText += "/Disconnect Controller"; + } + else + { + XmlNode Node = xDoc.SelectSingleNode("DS4Windows"); + el = xDoc.CreateElement("ProfileActions"); + el.InnerText = "Disconnect Controller"; + Node.AppendChild(el); + } + + xDoc.Save(s); + LoadActions(); + } + } + } + catch { } + } + + public static bool CreateAutoProfiles(string m_Profile) + { + bool Saved = true; + + try + { + XmlNode Node; + XmlDocument doc = new XmlDocument(); + + Node = doc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + doc.AppendChild(Node); + + Node = doc.CreateComment(string.Format(" Auto-Profile Configuration Data. {0} ", DateTime.Now)); + doc.AppendChild(Node); + + Node = doc.CreateWhitespace("\r\n"); + doc.AppendChild(Node); + + Node = doc.CreateNode(XmlNodeType.Element, "Programs", ""); + doc.AppendChild(Node); + doc.Save(m_Profile); + } + catch { Saved = false; } + + return Saved; + } + public static event EventHandler ControllerStatusChange; // called when a controller is added/removed/battery or touchpad mode changes/etc. public static void ControllerStatusChanged(object sender) { @@ -182,13 +741,58 @@ public static void ControllerStatusChanged(object sender) ControllerStatusChange(sender, EventArgs.Empty); } - //general values + public static event EventHandler BatteryStatusChange; + public static void OnBatteryStatusChange(object sender, int index, int level, bool charging) + { + if (BatteryStatusChange != null) + { + BatteryReportArgs args = new BatteryReportArgs(index, level, charging); + BatteryStatusChange(sender, args); + } + } + + public static event EventHandler ControllerRemoved; + public static void OnControllerRemoved(object sender, int index) + { + if (ControllerRemoved != null) + { + ControllerRemovedArgs args = new ControllerRemovedArgs(index); + ControllerRemoved(sender, args); + } + } + + public static event EventHandler DeviceStatusChange; + public static void OnDeviceStatusChanged(object sender, int index) + { + if (DeviceStatusChange != null) + { + DeviceStatusChangeEventArgs args = new DeviceStatusChangeEventArgs(index); + DeviceStatusChange(sender, args); + } + } + + public static event EventHandler DeviceSerialChange; + public static void OnDeviceSerialChange(object sender, int index, string serial) + { + if (DeviceSerialChange != null) + { + SerialChangeArgs args = new SerialChangeArgs(index, serial); + DeviceSerialChange(sender, args); + } + } + + // general values public static bool UseExclusiveMode { set { m_Config.useExclusiveMode = value; } get { return m_Config.useExclusiveMode; } } + public static bool getUseExclusiveMode() + { + return m_Config.useExclusiveMode; + } + public static DateTime LastChecked { set { m_Config.lastChecked = value; } @@ -200,146 +804,826 @@ public static int CheckWhen set { m_Config.CheckWhen = value; } get { return m_Config.CheckWhen; } } + public static int Notifications { set { m_Config.notifications = value; } get { return m_Config.notifications; } } + public static bool DCBTatStop { set { m_Config.disconnectBTAtStop = value; } get { return m_Config.disconnectBTAtStop; } } + public static bool SwipeProfiles { set { m_Config.swipeProfiles = value; } get { return m_Config.swipeProfiles; } } + public static bool DS4Mapping { set { m_Config.ds4Mapping = value; } get { return m_Config.ds4Mapping; } } + public static bool QuickCharge { set { m_Config.quickCharge = value; } get { return m_Config.quickCharge; } } - public static int FirstXinputPort + + public static bool getQuickCharge() { - set { m_Config.firstXinputPort = value; } - get { return m_Config.firstXinputPort; } + return m_Config.quickCharge; } + public static bool CloseMini { set { m_Config.closeMini = value; } get { return m_Config.closeMini; } } + public static bool StartMinimized { set { m_Config.startMinimized = value; } get { return m_Config.startMinimized; } } + + public static bool MinToTaskbar + { + set { m_Config.minToTaskbar = value; } + get { return m_Config.minToTaskbar; } + } + + public static bool GetMinToTaskbar() + { + return m_Config.minToTaskbar; + } + public static int FormWidth { set { m_Config.formWidth = value; } - get { return m_Config.formWidth;} + get { return m_Config.formWidth; } } + public static int FormHeight { set { m_Config.formHeight = value; } get { return m_Config.formHeight; } } + + public static int FormLocationX + { + set { m_Config.formLocationX = value; } + get { return m_Config.formLocationX; } + } + + public static int FormLocationY + { + set { m_Config.formLocationY = value; } + get { return m_Config.formLocationY; } + } + + public static string UseLang + { + set { m_Config.useLang = value; } + get { return m_Config.useLang; } + } + public static bool DownloadLang { set { m_Config.downloadLang = value; } get { return m_Config.downloadLang; } } + public static bool FlashWhenLate { set { m_Config.flashWhenLate = value; } get { return m_Config.flashWhenLate; } } + + public static bool getFlashWhenLate() + { + return m_Config.flashWhenLate; + } + public static int FlashWhenLateAt { set { m_Config.flashWhenLateAt = value; } get { return m_Config.flashWhenLateAt; } } - public static bool UseWhiteIcon + + public static int getFlashWhenLateAt() + { + return m_Config.flashWhenLateAt; + } + + public static bool isUsingUDPServer() + { + return m_Config.useUDPServ; + } + public static void setUsingUDPServer(bool state) + { + m_Config.useUDPServ = state; + } + + public static int getUDPServerPortNum() + { + return m_Config.udpServPort; + } + public static void setUDPServerPort(int value) + { + m_Config.udpServPort = value; + } + + public static string getUDPServerListenAddress() + { + return m_Config.udpServListenAddress; + } + public static void setUDPServerListenAddress(string value) + { + m_Config.udpServListenAddress = value.Trim(); + } + + public static bool UseWhiteIcon { set { m_Config.useWhiteIcon = value; } get { return m_Config.useWhiteIcon; } } - //controller/profile specfic values - public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; - public static byte[] RumbleBoost => m_Config.rumble; - public static double[] Rainbow => m_Config.rainbow; - public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; - public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; - public static byte[] TouchSensitivity => m_Config.touchSensitivity; - public static byte[] FlashType => m_Config.flashType; - public static int[] FlashAt => m_Config.flashAt; - public static bool[] LedAsBatteryIndicator => m_Config.ledAsBattery; - public static int[] ChargingType => m_Config.chargingType; - public static bool[] DinputOnly => m_Config.dinputOnly; - public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; - public static bool[] UseTPforControls => m_Config.useTPforControls; - public static bool[] UseSAforMouse => m_Config.useSAforMouse; - public static string[] SATriggers => m_Config.sATriggers; - public static int[] GyroSensitivity => m_Config.gyroSensitivity; - public static int[] GyroInvert => m_Config.gyroInvert; - public static DS4Color[] MainColor => m_Config.m_Leds; - public static DS4Color[] LowColor => m_Config.m_LowLeds; - public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; - public static DS4Color[] CustomColor => m_Config.m_CustomLeds; - public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool UseCustomSteamFolder + { + set { m_Config.useCustomSteamFolder = value; } + get { return m_Config.useCustomSteamFolder; } + } + + public static string CustomSteamFolder + { + set { m_Config.customSteamFolder = value; } + get { return m_Config.customSteamFolder; } + } + + public static bool AutoProfileRevertDefaultProfile + { + set { m_Config.autoProfileRevertDefaultProfile = value; } + get { return m_Config.autoProfileRevertDefaultProfile; } + } + + // controller/profile specfic values + public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; + + public static byte[] RumbleBoost => m_Config.rumble; + public static byte getRumbleBoost(int index) + { + return m_Config.rumble[index]; + } + + public static void setRumbleAutostopTime(int index, int value) + { + m_Config.rumbleAutostopTime[index] = value; + + DS4Device tempDev = Program.rootHub.DS4Controllers[index]; + if (tempDev != null && tempDev.isSynced()) + tempDev.RumbleAutostopTime = value; + } + + public static int getRumbleAutostopTime(int index) + { + return m_Config.rumbleAutostopTime[index]; + } + + public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; + public static bool getFlushHIDQueue(int index) + { + return m_Config.flushHIDQueue[index]; + } + + public static bool[] EnableTouchToggle => m_Config.enableTouchToggle; + public static bool getEnableTouchToggle(int index) + { + return m_Config.enableTouchToggle[index]; + } + + public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; + public static int getIdleDisconnectTimeout(int index) + { + return m_Config.idleDisconnectTimeout[index]; + } + + public static byte[] TouchSensitivity => m_Config.touchSensitivity; + public static byte[] getTouchSensitivity() + { + return m_Config.touchSensitivity; + } + + public static byte getTouchSensitivity(int index) + { + return m_Config.touchSensitivity[index]; + } + + public static bool[] TouchActive => touchpadActive; + public static bool GetTouchActive(int index) + { + return touchpadActive[index]; + } + + public static LightbarSettingInfo[] LightbarSettingsInfo => m_Config.lightbarSettingInfo; + public static LightbarSettingInfo getLightbarSettingsInfo(int index) + { + return m_Config.lightbarSettingInfo[index]; + } + + public static bool[] DinputOnly => m_Config.dinputOnly; + public static bool getDInputOnly(int index) + { + return m_Config.dinputOnly[index]; + } + + public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; + + public static bool[] UseTPforControls => m_Config.useTPforControls; + public static bool getUseTPforControls(int index) + { + return m_Config.useTPforControls[index]; + } + + public static bool[] UseSAforMouse => m_Config.useSAforMouse; + public static bool isUsingSAforMouse(int index) + { + return m_Config.gyroOutMode[index] == DS4Windows.GyroOutMode.Mouse; + } + + public static string[] SATriggers => m_Config.sATriggers; + public static string getSATriggers(int index) + { + return m_Config.sATriggers[index]; + } + + public static bool[] SATriggerCond => m_Config.sATriggerCond; + public static bool getSATriggerCond(int index) + { + return m_Config.sATriggerCond[index]; + } + public static void SetSaTriggerCond(int index, string text) + { + m_Config.SetSaTriggerCond(index, text); + } + + + public static GyroOutMode[] GyroOutputMode => m_Config.gyroOutMode; + public static GyroOutMode GetGyroOutMode(int device) + { + return m_Config.gyroOutMode[device]; + } + + public static string[] SAMousestickTriggers => m_Config.sAMouseStickTriggers; + public static string GetSAMouseStickTriggers(int device) + { + return m_Config.sAMouseStickTriggers[device]; + } + + public static bool[] SAMouseStickTriggerCond => m_Config.sAMouseStickTriggerCond; + public static bool GetSAMouseStickTriggerCond(int device) + { + return m_Config.sAMouseStickTriggerCond[device]; + } + public static void SetSaMouseStickTriggerCond(int index, string text) + { + m_Config.SetSaMouseStickTriggerCond(index, text); + } + + public static bool[] GyroMouseStickTriggerTurns = m_Config.gyroMouseStickTriggerTurns; + public static bool GetGyroMouseStickTriggerTurns(int device) + { + return m_Config.gyroMouseStickTriggerTurns[device]; + } + + public static int[] GyroMouseStickHorizontalAxis => + m_Config.gyroMouseStickHorizontalAxis; + public static int getGyroMouseStickHorizontalAxis(int index) + { + return m_Config.gyroMouseStickHorizontalAxis[index]; + } + + public static GyroMouseStickInfo[] GyroMouseStickInf => m_Config.gyroMStickInfo; + public static GyroMouseStickInfo GetGyroMouseStickInfo(int device) + { + return m_Config.gyroMStickInfo[device]; + } + + public static bool[] GyroMouseStickToggle => m_Config.gyroMouseStickToggle; + public static void SetGyroMouseStickToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseStickToggle(index, value, control); + + public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis; + public static SASteeringWheelEmulationAxisType GetSASteeringWheelEmulationAxis(int index) + { + return m_Config.sASteeringWheelEmulationAxis[index]; + } + + public static int[] SASteeringWheelEmulationRange => m_Config.sASteeringWheelEmulationRange; + public static int GetSASteeringWheelEmulationRange(int index) + { + return m_Config.sASteeringWheelEmulationRange[index]; + } + + public static int[][] TouchDisInvertTriggers => m_Config.touchDisInvertTriggers; + public static int[] getTouchDisInvertTriggers(int index) + { + return m_Config.touchDisInvertTriggers[index]; + } + + public static int[] GyroSensitivity => m_Config.gyroSensitivity; + public static int getGyroSensitivity(int index) + { + return m_Config.gyroSensitivity[index]; + } + + public static int[] GyroSensVerticalScale => m_Config.gyroSensVerticalScale; + public static int getGyroSensVerticalScale(int index) + { + return m_Config.gyroSensVerticalScale[index]; + } + + public static int[] GyroInvert => m_Config.gyroInvert; + public static int getGyroInvert(int index) + { + return m_Config.gyroInvert[index]; + } + + public static bool[] GyroTriggerTurns => m_Config.gyroTriggerTurns; + public static bool getGyroTriggerTurns(int index) + { + return m_Config.gyroTriggerTurns[index]; + } + + public static bool[] GyroSmoothing => m_Config.gyroSmoothing; + public static bool getGyroSmoothing(int index) + { + return m_Config.gyroSmoothing[index]; + } + + public static double[] GyroSmoothingWeight => m_Config.gyroSmoothWeight; + public static double getGyroSmoothingWeight(int index) + { + return m_Config.gyroSmoothWeight[index]; + } + + public static int[] GyroMouseHorizontalAxis => m_Config.gyroMouseHorizontalAxis; + public static int getGyroMouseHorizontalAxis(int index) + { + return m_Config.gyroMouseHorizontalAxis[index]; + } + + public static int[] GyroMouseDeadZone => m_Config.gyroMouseDZ; + public static int GetGyroMouseDeadZone(int index) + { + return m_Config.gyroMouseDZ[index]; + } + + public static void SetGyroMouseDeadZone(int index, int value, ControlService control) + { + m_Config.SetGyroMouseDZ(index, value, control); + } + + public static bool[] GyroMouseToggle => m_Config.gyroMouseToggle; + public static void SetGyroMouseToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseToggle(index, value, control); + + //public static DS4Color[] MainColor => m_Config.m_Leds; + public static ref DS4Color getMainColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_Led; + //return ref m_Config.m_Leds[index]; + } + + //public static DS4Color[] LowColor => m_Config.m_LowLeds; + public static ref DS4Color getLowColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_LowLed; + //return ref m_Config.m_LowLeds[index]; + } + + //public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; + public static ref DS4Color getChargingColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_ChargingLed; + //return ref m_Config.m_ChargingLeds[index]; + } + + //public static DS4Color[] CustomColor => m_Config.m_CustomLeds; + public static ref DS4Color getCustomColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_CustomLed; + //return ref m_Config.m_CustomLeds[index]; + } + + //public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool getUseCustomLed(int index) + { + return m_Config.lightbarSettingInfo[index].ds4winSettings.useCustomLed; + //return m_Config.useCustomLeds[index]; + } + + //public static DS4Color[] FlashColor => m_Config.m_FlashLeds; + public static ref DS4Color getFlashColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_FlashLed; + //return ref m_Config.m_FlashLeds[index]; + } + + public static byte[] TapSensitivity => m_Config.tapSensitivity; + public static byte getTapSensitivity(int index) + { + return m_Config.tapSensitivity[index]; + } + + public static bool[] DoubleTap => m_Config.doubleTap; + public static bool getDoubleTap(int index) + { + return m_Config.doubleTap[index]; + } + + public static int[] ScrollSensitivity => m_Config.scrollSensitivity; + public static int[] getScrollSensitivity() + { + return m_Config.scrollSensitivity; + } + public static int getScrollSensitivity(int index) + { + return m_Config.scrollSensitivity[index]; + } + + public static bool[] LowerRCOn => m_Config.lowerRCOn; + public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static bool getTouchpadJitterCompensation(int index) + { + return m_Config.touchpadJitterCompensation[index]; + } + + public static int[] TouchpadInvert => m_Config.touchpadInvert; + public static int getTouchpadInvert(int index) + { + return m_Config.touchpadInvert[index]; + } + + public static TriggerDeadZoneZInfo[] L2ModInfo => m_Config.l2ModInfo; + public static TriggerDeadZoneZInfo GetL2ModInfo(int index) + { + return m_Config.l2ModInfo[index]; + } + + //public static byte[] L2Deadzone => m_Config.l2Deadzone; + public static byte getL2Deadzone(int index) + { + return m_Config.l2ModInfo[index].deadZone; + //return m_Config.l2Deadzone[index]; + } + + public static TriggerDeadZoneZInfo[] R2ModInfo => m_Config.r2ModInfo; + public static TriggerDeadZoneZInfo GetR2ModInfo(int index) + { + return m_Config.r2ModInfo[index]; + } + + //public static byte[] R2Deadzone => m_Config.r2Deadzone; + public static byte getR2Deadzone(int index) + { + return m_Config.r2ModInfo[index].deadZone; + //return m_Config.r2Deadzone[index]; + } + + public static double[] SXDeadzone => m_Config.SXDeadzone; + public static double getSXDeadzone(int index) + { + return m_Config.SXDeadzone[index]; + } + + public static double[] SZDeadzone => m_Config.SZDeadzone; + public static double getSZDeadzone(int index) + { + return m_Config.SZDeadzone[index]; + } + + //public static int[] LSDeadzone => m_Config.LSDeadzone; + public static int getLSDeadzone(int index) + { + return m_Config.lsModInfo[index].deadZone; + //return m_Config.LSDeadzone[index]; + } + + //public static int[] RSDeadzone => m_Config.RSDeadzone; + public static int getRSDeadzone(int index) + { + return m_Config.rsModInfo[index].deadZone; + //return m_Config.RSDeadzone[index]; + } + + //public static int[] LSAntiDeadzone => m_Config.LSAntiDeadzone; + public static int getLSAntiDeadzone(int index) + { + return m_Config.lsModInfo[index].antiDeadZone; + //return m_Config.LSAntiDeadzone[index]; + } + + //public static int[] RSAntiDeadzone => m_Config.RSAntiDeadzone; + public static int getRSAntiDeadzone(int index) + { + return m_Config.rsModInfo[index].antiDeadZone; + //return m_Config.RSAntiDeadzone[index]; + } + + public static StickDeadZoneInfo[] LSModInfo => m_Config.lsModInfo; + public static StickDeadZoneInfo GetLSDeadInfo(int index) + { + return m_Config.lsModInfo[index]; + } + + public static StickDeadZoneInfo[] RSModInfo => m_Config.rsModInfo; + public static StickDeadZoneInfo GetRSDeadInfo(int index) + { + return m_Config.rsModInfo[index]; + } + + public static double[] SXAntiDeadzone => m_Config.SXAntiDeadzone; + public static double getSXAntiDeadzone(int index) + { + return m_Config.SXAntiDeadzone[index]; + } + + public static double[] SZAntiDeadzone => m_Config.SZAntiDeadzone; + public static double getSZAntiDeadzone(int index) + { + return m_Config.SZAntiDeadzone[index]; + } + + //public static int[] LSMaxzone => m_Config.LSMaxzone; + public static int getLSMaxzone(int index) + { + return m_Config.lsModInfo[index].maxZone; + //return m_Config.LSMaxzone[index]; + } + + //public static int[] RSMaxzone => m_Config.RSMaxzone; + public static int getRSMaxzone(int index) + { + return m_Config.rsModInfo[index].maxZone; + //return m_Config.RSMaxzone[index]; + } + + public static double[] SXMaxzone => m_Config.SXMaxzone; + public static double getSXMaxzone(int index) + { + return m_Config.SXMaxzone[index]; + } + + public static double[] SZMaxzone => m_Config.SZMaxzone; + public static double getSZMaxzone(int index) + { + return m_Config.SZMaxzone[index]; + } + + //public static int[] L2AntiDeadzone => m_Config.l2AntiDeadzone; + public static int getL2AntiDeadzone(int index) + { + return m_Config.l2ModInfo[index].antiDeadZone; + //return m_Config.l2AntiDeadzone[index]; + } + + //public static int[] R2AntiDeadzone => m_Config.r2AntiDeadzone; + public static int getR2AntiDeadzone(int index) + { + return m_Config.r2ModInfo[index].antiDeadZone; + //return m_Config.r2AntiDeadzone[index]; + } + + //public static int[] L2Maxzone => m_Config.l2Maxzone; + public static int getL2Maxzone(int index) + { + return m_Config.l2ModInfo[index].maxZone; + //return m_Config.l2Maxzone[index]; + } + + //public static int[] R2Maxzone => m_Config.r2Maxzone; + public static int getR2Maxzone(int index) + { + return m_Config.r2ModInfo[index].maxZone; + //return m_Config.r2Maxzone[index]; + } + + public static int[] LSCurve => m_Config.lsCurve; + public static int getLSCurve(int index) + { + return m_Config.lsCurve[index]; + } + + public static int[] RSCurve => m_Config.rsCurve; + public static int getRSCurve(int index) + { + return m_Config.rsCurve[index]; + } + + public static double[] LSRotation => m_Config.LSRotation; + public static double getLSRotation(int index) + { + return m_Config.LSRotation[index]; + } + + public static double[] RSRotation => m_Config.RSRotation; + public static double getRSRotation(int index) + { + return m_Config.RSRotation[index]; + } + + public static double[] L2Sens => m_Config.l2Sens; + public static double getL2Sens(int index) + { + return m_Config.l2Sens[index]; + } + + public static double[] R2Sens => m_Config.r2Sens; + public static double getR2Sens(int index) + { + return m_Config.r2Sens[index]; + } + + public static double[] SXSens => m_Config.SXSens; + public static double getSXSens(int index) + { + return m_Config.SXSens[index]; + } + + public static double[] SZSens => m_Config.SZSens; + public static double getSZSens(int index) + { + return m_Config.SZSens[index]; + } + + public static double[] LSSens => m_Config.LSSens; + public static double getLSSens(int index) + { + return m_Config.LSSens[index]; + } + + public static double[] RSSens => m_Config.RSSens; + public static double getRSSens(int index) + { + return m_Config.RSSens[index]; + } + + public static bool[] MouseAccel => m_Config.mouseAccel; + public static bool getMouseAccel(int device) + { + return m_Config.mouseAccel[device]; + } + + public static int[] BTPollRate => m_Config.btPollRate; + public static int getBTPollRate(int index) + { + return m_Config.btPollRate[index]; + } + + public static SquareStickInfo[] SquStickInfo = m_Config.squStickInfo; + public static SquareStickInfo GetSquareStickInfo(int device) + { + return m_Config.squStickInfo[device]; + } + + public static void setLsOutCurveMode(int index, int value) + { + m_Config.setLsOutCurveMode(index, value); + } + public static int getLsOutCurveMode(int index) + { + return m_Config.getLsOutCurveMode(index); + } + public static BezierCurve[] lsOutBezierCurveObj => m_Config.lsOutBezierCurveObj; + + public static void setRsOutCurveMode(int index, int value) + { + m_Config.setRsOutCurveMode(index, value); + } + public static int getRsOutCurveMode(int index) + { + return m_Config.getRsOutCurveMode(index); + } + public static BezierCurve[] rsOutBezierCurveObj => m_Config.rsOutBezierCurveObj; - public static DS4Color[] FlashColor => m_Config.m_FlashLeds; - public static byte[] TapSensitivity => m_Config.tapSensitivity; - public static bool[] DoubleTap => m_Config.doubleTap; - public static int[] ScrollSensitivity => m_Config.scrollSensitivity; - public static bool[] LowerRCOn => m_Config.lowerRCOn; - public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static void setL2OutCurveMode(int index, int value) + { + m_Config.setL2OutCurveMode(index, value); + } + public static int getL2OutCurveMode(int index) + { + return m_Config.getL2OutCurveMode(index); + } + public static BezierCurve[] l2OutBezierCurveObj => m_Config.l2OutBezierCurveObj; - public static byte[] L2Deadzone => m_Config.l2Deadzone; - public static byte[] R2Deadzone => m_Config.r2Deadzone; - public static double[] SXDeadzone => m_Config.SXDeadzone; - public static double[] SZDeadzone => m_Config.SZDeadzone; - public static int[] LSDeadzone => m_Config.LSDeadzone; - public static int[] RSDeadzone => m_Config.RSDeadzone; - public static int[] LSCurve => m_Config.lsCurve; - public static int[] RSCurve => m_Config.rsCurve; - public static double[] L2Sens => m_Config.l2Sens; - public static double[] R2Sens => m_Config.r2Sens; - public static double[] SXSens => m_Config.SXSens; - public static double[] SZSens => m_Config.SZSens; - public static double[] LSSens => m_Config.LSSens; - public static double[] RSSens => m_Config.RSSens; - public static bool[] MouseAccel => m_Config.mouseAccel; + public static void setR2OutCurveMode(int index, int value) + { + m_Config.setR2OutCurveMode(index, value); + } + public static int getR2OutCurveMode(int index) + { + return m_Config.getR2OutCurveMode(index); + } + public static BezierCurve[] r2OutBezierCurveObj => m_Config.r2OutBezierCurveObj; + + public static void setSXOutCurveMode(int index, int value) + { + m_Config.setSXOutCurveMode(index, value); + } + public static int getSXOutCurveMode(int index) + { + return m_Config.getSXOutCurveMode(index); + } + public static BezierCurve[] sxOutBezierCurveObj => m_Config.sxOutBezierCurveObj; + + public static void setSZOutCurveMode(int index, int value) + { + m_Config.setSZOutCurveMode(index, value); + } + public static int getSZOutCurveMode(int index) + { + return m_Config.getSZOutCurveMode(index); + } + public static BezierCurve[] szOutBezierCurveObj => m_Config.szOutBezierCurveObj; + + public static bool[] TrackballMode => m_Config.trackballMode; + public static bool getTrackballMode(int index) + { + return m_Config.trackballMode[index]; + } + + public static double[] TrackballFriction => m_Config.trackballFriction; + public static double getTrackballFriction(int index) + { + return m_Config.trackballFriction[index]; + } + + public static OutContType[] OutContType => m_Config.outputDevType; public static string[] LaunchProgram => m_Config.launchProgram; public static string[] ProfilePath => m_Config.profilePath; + public static string[] OlderProfilePath => m_Config.olderProfilePath; + public static bool[] DistanceProfiles = m_Config.distanceProfiles; + public static List[] ProfileActions => m_Config.profileActions; + public static int getProfileActionCount(int index) + { + return m_Config.profileActionCount[index]; + } + + public static void calculateProfileActionCount(int index) + { + m_Config.profileActionCount[index] = m_Config.profileActions[index].Count; + } + + public static List getProfileActions(int index) + { + return m_Config.profileActions[index]; + } public static void UpdateDS4CSetting (int deviceNum, string buttonName, bool shift, object action, string exts, DS4KeyType kt, int trigger = 0) { m_Config.UpdateDS4CSetting(deviceNum, buttonName, shift, action, exts, kt, trigger); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } + public static void UpdateDS4Extra(int deviceNum, string buttonName, bool shift, string exts) { m_Config.UpdateDS4CExtra(deviceNum, buttonName, shift, exts); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } - public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, DS4Controls control, bool shift) => m_Config.GetDS4Action(deviceNum, control, shift); public static DS4KeyType GetDS4KeyType(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4KeyType(deviceNum, buttonName, shift); public static string GetDS4Extra(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Extra(deviceNum, buttonName, shift); public static int GetDS4STrigger(int deviceNum, string buttonName) => m_Config.GetDS4STrigger(deviceNum, buttonName); + public static int GetDS4STrigger(int deviceNum, DS4Controls control) => m_Config.GetDS4STrigger(deviceNum, control); public static List getDS4CSettings(int device) => m_Config.ds4settings[device]; public static DS4ControlSettings getDS4CSetting(int deviceNum, string control) => m_Config.getDS4CSetting(deviceNum, control); - public static bool HasCustomAction(int deviceNum) => m_Config.HasCustomActions(deviceNum); + public static DS4ControlSettings getDS4CSetting(int deviceNum, DS4Controls control) => m_Config.getDS4CSetting(deviceNum, control); + public static bool HasCustomActions(int deviceNum) => m_Config.HasCustomActions(deviceNum); public static bool HasCustomExtras(int deviceNum) => m_Config.HasCustomExtras(deviceNum); - public static void SaveAction(string name, string controls, int mode, string details, bool edit, string extras = "") + public static bool containsCustomAction(int deviceNum) + { + return m_Config.containsCustomAction[deviceNum]; + } + + public static bool containsCustomExtras(int deviceNum) + { + return m_Config.containsCustomExtras[deviceNum]; + } + + public static void SaveAction(string name, string controls, int mode, + string details, bool edit, string extras = "") { m_Config.SaveAction(name, controls, mode, details, edit, extras); Mapping.actionDone.Add(new Mapping.ActionState()); @@ -356,66 +1640,155 @@ public static void RemoveAction(string name) public static int GetActionIndexOf(string name) { - for (int i = 0; i < m_Config.actions.Count; i++) + for (int i = 0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { if (m_Config.actions[i].name == name) return i; + } + return -1; } + public static int GetProfileActionIndexOf(int device, string name) + { + int index = -1; + m_Config.profileActionIndexDict[device].TryGetValue(name, out index); + return index; + } + public static SpecialAction GetAction(string name) { - foreach (SpecialAction sA in m_Config.actions) + //foreach (SpecialAction sA in m_Config.actions) + for (int i=0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { + SpecialAction sA = m_Config.actions[i]; if (sA.name == name) return sA; + } + return new SpecialAction("null", "null", "null", "null"); } + public static SpecialAction GetProfileAction(int device, string name) + { + SpecialAction sA = null; + m_Config.profileActionDict[device].TryGetValue(name, out sA); + return sA; + } + + public static void calculateProfileActionDicts(int device) + { + m_Config.profileActionDict[device].Clear(); + m_Config.profileActionIndexDict[device].Clear(); + + foreach (string actionname in m_Config.profileActions[device]) + { + m_Config.profileActionDict[device][actionname] = GetAction(actionname); + m_Config.profileActionIndexDict[device][actionname] = GetActionIndexOf(actionname); + } + } + + public static void cacheProfileCustomsFlags(int device) + { + bool customAct = false; + m_Config.containsCustomAction[device] = customAct = HasCustomActions(device); + m_Config.containsCustomExtras[device] = HasCustomExtras(device); + + if (!customAct) + { + customAct = m_Config.gyroOutMode[device] == GyroOutMode.MouseJoystick; + customAct = customAct || m_Config.sASteeringWheelEmulationAxis[device] >= SASteeringWheelEmulationAxisType.VJoy1X; + m_Config.containsCustomAction[device] = customAct; + } + } + + public static void CacheExtraProfileInfo(int device) + { + calculateProfileActionCount(device); + calculateProfileActionDicts(device); + cacheProfileCustomsFlags(device); + } + + public static X360Controls getX360ControlsByName(string key) + { + return m_Config.getX360ControlsByName(key); + } + + public static string getX360ControlString(X360Controls key) + { + return m_Config.getX360ControlString(key); + } + + public static DS4Controls getDS4ControlsByName(string key) + { + return m_Config.getDS4ControlsByName(key); + } + + public static X360Controls getDefaultX360ControlBinding(DS4Controls dc) + { + return defaultButtonMapping[(int)dc]; + } + + public static bool containsLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + return m_Config.linkedProfiles.ContainsKey(tempSerial); + } + + public static string getLinkedProfile(string serial) + { + string temp = string.Empty; + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + temp = m_Config.linkedProfiles[tempSerial]; + } + + return temp; + } + + public static void changeLinkedProfile(string serial, string profile) + { + string tempSerial = serial.Replace(":", string.Empty); + m_Config.linkedProfiles[tempSerial] = profile; + } + + public static void removeLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + m_Config.linkedProfiles.Remove(tempSerial); + } + } - /*public static X360Controls getCustomButton(int device, DS4Controls controlName) => m_Config.GetCustomButton(device, controlName); - - public static ushort getCustomKey(int device, DS4Controls controlName) => m_Config.GetCustomKey(device, controlName); - - public static string getCustomMacro(int device, DS4Controls controlName) => m_Config.GetCustomMacro(device, controlName); - - public static string getCustomExtras(int device, DS4Controls controlName) => m_Config.GetCustomExtras(device, controlName); - - public static DS4KeyType getCustomKeyType(int device, DS4Controls controlName) => m_Config.GetCustomKeyType(device, controlName); - - public static bool getHasCustomKeysorButtons(int device) => m_Config.customMapButtons[device].Count > 0 - || m_Config.customMapKeys[device].Count > 0; - - public static bool getHasCustomExtras(int device) => m_Config.customMapExtras[device].Count > 0; - public static Dictionary getCustomButtons(int device) => m_Config.customMapButtons[device]; - public static Dictionary getCustomKeys(int device) => m_Config.customMapKeys[device]; - public static Dictionary getCustomMacros(int device) => m_Config.customMapMacros[device]; - public static Dictionary getCustomExtras(int device) => m_Config.customMapExtras[device]; - public static Dictionary getCustomKeyTypes(int device) => m_Config.customMapKeyTypes[device]; - - public static X360Controls getShiftCustomButton(int device, DS4Controls controlName) => m_Config.GetShiftCustomButton(device, controlName); - public static ushort getShiftCustomKey(int device, DS4Controls controlName) => m_Config.GetShiftCustomKey(device, controlName); - public static string getShiftCustomMacro(int device, DS4Controls controlName) => m_Config.GetShiftCustomMacro(device, controlName); - public static string getShiftCustomExtras(int device, DS4Controls controlName) => m_Config.GetShiftCustomExtras(device, controlName); - public static DS4KeyType getShiftCustomKeyType(int device, DS4Controls controlName) => m_Config.GetShiftCustomKeyType(device, controlName); - public static bool getHasShiftCustomKeysorButtons(int device) => m_Config.shiftCustomMapButtons[device].Count > 0 - || m_Config.shiftCustomMapKeys[device].Count > 0; - public static bool getHasShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device].Count > 0; - public static Dictionary getShiftCustomButtons(int device) => m_Config.shiftCustomMapButtons[device]; - public static Dictionary getShiftCustomKeys(int device) => m_Config.shiftCustomMapKeys[device]; - public static Dictionary getShiftCustomMacros(int device) => m_Config.shiftCustomMapMacros[device]; - public static Dictionary getShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device]; - public static Dictionary getShiftCustomKeyTypes(int device) => m_Config.shiftCustomMapKeyTypes[device]; */ public static bool Load() => m_Config.Load(); - public static void LoadProfile(int device, bool launchprogram, ControlService control) + public static void LoadProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) { - m_Config.LoadProfile(device, launchprogram, control); + m_Config.LoadProfile(device, launchprogram, control, "", xinputChange, postLoad); tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } - public static void LoadTempProfile(int device, string name, bool launchprogram, ControlService control) + public static void LoadTempProfile(int device, string name, bool launchprogram, + ControlService control, bool xinputChange = true) { m_Config.LoadProfile(device, launchprogram, control, appdatapath + @"\Profiles\" + name + ".xml"); tempprofilename[device] = name; + useTempProfile[device] = true; + tempprofileDistance[device] = name.ToLower().Contains("distance"); + } + + public static void LoadBlankDevProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) + { + m_Config.LoadBlankProfile(device, launchprogram, control, "", xinputChange, postLoad); + tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } public static bool Save() @@ -428,22 +1801,66 @@ public static void SaveProfile(int device, string propath) m_Config.SaveProfile(device, propath); } + public static void SaveAsNewProfile(int device, string propath) + { + m_Config.SaveAsNewProfile(device, propath); + } + + public static bool SaveLinkedProfiles() + { + return m_Config.SaveLinkedProfiles(); + } + + public static bool LoadLinkedProfiles() + { + return m_Config.LoadLinkedProfiles(); + } + + public static bool SaveControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.SaveControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.SaveControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + + public static bool LoadControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.LoadControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.LoadControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + private static byte applyRatio(byte b1, byte b2, double r) { - if (r > 100) - r = 100; - else if (r < 0) - r = 0; - r /= 100f; - return (byte)Math.Round((b1 * (1 - r) + b2 *r),0); + if (r > 100.0) + r = 100.0; + else if (r < 0.0) + r = 0.0; + + r *= 0.01; + return (byte)Math.Round((b1 * (1 - r)) + b2 * r, 0); } - public static DS4Color getTransitionedColor(DS4Color c1, DS4Color c2, double ratio) - {//; + + public static DS4Color getTransitionedColor(ref DS4Color c1, ref DS4Color c2, double ratio) + { //Color cs = Color.FromArgb(c1.red, c1.green, c1.blue); - c1.red = applyRatio(c1.red, c2.red, ratio); - c1.green = applyRatio(c1.green, c2.green, ratio); - c1.blue = applyRatio(c1.blue, c2.blue, ratio); - return c1; + DS4Color cs = new DS4Color + { + red = applyRatio(c1.red, c2.red, ratio), + green = applyRatio(c1.green, c2.green, ratio), + blue = applyRatio(c1.blue, c2.blue, ratio) + }; + return cs; } private static Color applyRatio(Color c1, Color c2, uint r) @@ -463,6 +1880,7 @@ private static Color applyRatio(Color c1, Color c2, uint r) csR = HuetoRGB(hue2,sat2,bri2 - br*ratio); else csR = HuetoRGB(hue2 - hr * ratio, sat2 - sr * ratio, bri2 - br * ratio); + return csR; } @@ -473,234 +1891,546 @@ public static Color HuetoRGB(float hue, float sat, float bri) float m = bri - C / 2; float R, G, B; if (0 <= hue && hue < 60) - { R = C; G = X; B = 0;} + { + R = C; G = X; B = 0; + } else if (60 <= hue && hue < 120) - {R = X; G = C; B = 0; } + { + R = X; G = C; B = 0; + } else if (120 <= hue && hue < 180) - { R = 0; G = C; B = X; } + { + R = 0; G = C; B = X; + } else if (180 <= hue && hue < 240) - { R = 0; G = X; B = C; } + { + R = 0; G = X; B = C; + } else if (240 <= hue && hue < 300) - { R = X; G = 0; B = C; } + { + R = X; G = 0; B = C; + } else if (300 <= hue && hue < 360) - { R = C; G = 0; B = X; } + { + R = C; G = 0; B = X; + } else - { R = 255; G = 0; B = 0; } + { + R = 255; G = 0; B = 0; + } + R += m; G += m; B += m; - R *= 255; G *= 255; B *= 255; + R *= 255.0f; G *= 255.0f; B *= 255.0f; return Color.FromArgb((int)R, (int)G, (int)B); } - } - + public static double Clamp(double min, double value, double max) + { + return (value < min) ? min : (value > max) ? max : value; + } + private static int ClampInt(int min, int value, int max) + { + return (value < min) ? min : (value > max) ? max : value; + } + } public class BackingStore { //public String m_Profile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Tool" + "\\Profiles.xml"; public String m_Profile = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName + "\\Profiles.xml"; public String m_Actions = Global.appdatapath + "\\Actions.xml"; + public string m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + public string m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; protected XmlDocument m_Xdoc = new XmlDocument(); - //fifth value used to for options, not fifth controller - public int[] buttonMouseSensitivity = { 25, 25, 25, 25, 25 }; - - public bool[] flushHIDQueue = { true, true, true, true, true }; - public int[] idleDisconnectTimeout = { 0, 0, 0, 0, 0 }; - public Boolean[] touchpadJitterCompensation = { true, true, true, true, true }; - public Boolean[] lowerRCOn = { false, false, false, false, false }; - public Boolean[] ledAsBattery = { false, false, false, false, false }; - public Byte[] flashType = { 0, 0, 0, 0, 0 }; - public String[] profilePath = { String.Empty, String.Empty, String.Empty, String.Empty, String.Empty }; - public Byte[] rumble = { 100, 100, 100, 100, 100 }; - public Byte[] touchSensitivity = { 100, 100, 100, 100, 100 }; - public Byte[] l2Deadzone = { 0, 0, 0, 0, 0 }, r2Deadzone = { 0, 0, 0, 0, 0 }; - public int[] LSDeadzone = { 0, 0, 0, 0, 0 }, RSDeadzone = { 0, 0, 0, 0, 0 }; - public double[] SXDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }; - public double[] l2Sens = { 1, 1, 1, 1, 1 }, r2Sens = { 1, 1, 1, 1, 1 }; - public double[] LSSens = { 1, 1, 1, 1, 1 }, RSSens = { 1, 1, 1, 1, 1 }; - public double[] SXSens = { 1, 1, 1, 1, 1 }, SZSens = { 1, 1, 1, 1, 1 }; - public Byte[] tapSensitivity = { 0, 0, 0, 0, 0 }; - public bool[] doubleTap = { false, false, false, false, false }; - public int[] scrollSensitivity = { 0, 0, 0, 0, 0 }; - public double[] rainbow = { 0, 0, 0, 0, 0 }; - public int[] flashAt = { 0, 0, 0, 0, 0 }; - public bool[] mouseAccel = { true, true, true, true, true }; - public DS4Color[] m_LowLeds = new DS4Color[] - { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + // fifth value used for options, not fifth controller + public int[] buttonMouseSensitivity = new int[5] { 25, 25, 25, 25, 25 }; + + public bool[] flushHIDQueue = new bool[5] { false, false, false, false, false }; + public bool[] enableTouchToggle = new bool[5] { true, true, true, true, true }; + public int[] idleDisconnectTimeout = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] touchpadJitterCompensation = new bool[5] { true, true, true, true, true }; + public bool[] lowerRCOn = new bool[5] { false, false, false, false, false }; + public string[] profilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public string[] olderProfilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public Dictionary linkedProfiles = new Dictionary(); + // Cache properties instead of performing a string comparison every frame + public bool[] distanceProfiles = new bool[5] { false, false, false, false, false }; + public Byte[] rumble = new Byte[5] { 100, 100, 100, 100, 100 }; + public int[] rumbleAutostopTime = new int[5] { 0, 0, 0, 0, 0 }; // Value in milliseconds (0=autustop timer disabled) + public Byte[] touchSensitivity = new Byte[5] { 100, 100, 100, 100, 100 }; + public StickDeadZoneInfo[] lsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public StickDeadZoneInfo[] rsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public TriggerDeadZoneZInfo[] l2ModInfo = new TriggerDeadZoneZInfo[5] + { + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_Leds = new DS4Color[] + public TriggerDeadZoneZInfo[] r2ModInfo = new TriggerDeadZoneZInfo[5] { - new DS4Color(Color.Blue), - new DS4Color(Color.Red), - new DS4Color(Color.Green), - new DS4Color(Color.Pink), - new DS4Color(Color.White) + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_ChargingLeds = new DS4Color[] + + public double[] LSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, RSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] SXDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }; + public double[] SXMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, + SZMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, + SZAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] l2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, r2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] LSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, RSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, SZSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public Byte[] tapSensitivity = new Byte[5] { 0, 0, 0, 0, 0 }; + public bool[] doubleTap = new bool[5] { false, false, false, false, false }; + public int[] scrollSensitivity = new int[5] { 0, 0, 0, 0, 0 }; + public int[] touchpadInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] mouseAccel = new bool[5] { false, false, false, false, false }; + public int[] btPollRate = new int[5] { 4, 4, 4, 4, 4 }; + public int[] gyroMouseDZ = new int[5] { MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE }; + public bool[] gyroMouseToggle = new bool[5] { false, false, false, + false, false }; + + public SquareStickInfo[] squStickInfo = new SquareStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), }; - public DS4Color[] m_FlashLeds = new DS4Color[] + + private void setOutBezierCurveObjArrayItem(BezierCurve[] bezierCurveArray, int device, int curveOptionValue, BezierCurve.AxisType axisType) + { + // Set bezier curve obj of axis. 0=Linear (no curve mapping), 1-5=Pre-defined curves, 6=User supplied custom curve string value of a profile (comma separated list of 4 decimal numbers) + switch (curveOptionValue) + { + // Commented out case 1..5 because Mapping.cs:SetCurveAndDeadzone function has the original IF-THEN-ELSE code logic for those original 1..5 output curve mappings (ie. no need to initialize the lookup result table). + // Only the new bezier custom curve option 6 uses the lookup result table (initialized in BezierCurve class based on an input curve definition). + //case 1: bezierCurveArray[device].InitBezierCurve(99.0, 91.0, 0.00, 0.00, axisType); break; // Enhanced Precision (hard-coded curve) (almost the same curve as bezier 0.70, 0.28, 1.00, 1.00) + //case 2: bezierCurveArray[device].InitBezierCurve(99.0, 92.0, 0.00, 0.00, axisType); break; // Quadric + //case 3: bezierCurveArray[device].InitBezierCurve(99.0, 93.0, 0.00, 0.00, axisType); break; // Cubic + //case 4: bezierCurveArray[device].InitBezierCurve(99.0, 94.0, 0.00, 0.00, axisType); break; // Easeout Quad + //case 5: bezierCurveArray[device].InitBezierCurve(99.0, 95.0, 0.00, 0.00, axisType); break; // Easeout Cubic + case 6: bezierCurveArray[device].InitBezierCurve(bezierCurveArray[device].CustomDefinition, axisType); break; // Custom output curve + } + } + + public BezierCurve[] lsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] rsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] l2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] r2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] sxOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] szOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + + private int[] _lsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getLsOutCurveMode(int index) { return _lsOutCurveMode[index]; } + public void setLsOutCurveMode(int index, int value) { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + if (value >= 1) setOutBezierCurveObjArrayItem(lsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _lsOutCurveMode[index] = value; + } + + private int[] _rsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getRsOutCurveMode(int index) { return _rsOutCurveMode[index]; } + public void setRsOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(rsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _rsOutCurveMode[index] = value; + } + + private int[] _l2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getL2OutCurveMode(int index) { return _l2OutCurveMode[index]; } + public void setL2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(l2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _l2OutCurveMode[index] = value; + } + + private int[] _r2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getR2OutCurveMode(int index) { return _r2OutCurveMode[index]; } + public void setR2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(r2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _r2OutCurveMode[index] = value; + } + + private int[] _sxOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSXOutCurveMode(int index) { return _sxOutCurveMode[index]; } + public void setSXOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(sxOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _sxOutCurveMode[index] = value; + } + + private int[] _szOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSZOutCurveMode(int index) { return _szOutCurveMode[index]; } + public void setSZOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(szOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _szOutCurveMode[index] = value; + } + + public LightbarSettingInfo[] lightbarSettingInfo = new LightbarSettingInfo[5] + { + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo() }; - public bool[] useCustomLeds = new bool[] { false, false, false, false }; - public DS4Color[] m_CustomLeds = new DS4Color[] + + public string[] launchProgram = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public bool[] dinputOnly = new bool[5] { false, false, false, false, false }; + public bool[] startTouchpadOff = new bool[5] { false, false, false, false, false }; + public bool[] useTPforControls = new bool[5] { false, false, false, false, false }; + public bool[] useSAforMouse = new bool[5] { false, false, false, false, false }; + public GyroOutMode[] gyroOutMode = new GyroOutMode[5] { GyroOutMode.Controls, GyroOutMode.Controls, + GyroOutMode.Controls, GyroOutMode.Controls, GyroOutMode.Controls }; + public string[] sATriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public string[] sAMouseStickTriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public bool[] sATriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] sAMouseStickTriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] gyroMouseStickTriggerTurns = new bool[5] { true, true, true, true, true }; + public GyroMouseStickInfo[] gyroMStickInfo = new GyroMouseStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), new GyroMouseStickInfo(), + new GyroMouseStickInfo() }; - public int[] chargingType = { 0, 0, 0, 0, 0 }; - public string[] launchProgram = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; - public bool[] dinputOnly = { false, false, false, false, false }; - public bool[] startTouchpadOff = { false, false, false, false, false }; - public bool[] useTPforControls = { false, false, false, false, false }; - public bool[] useSAforMouse = { false, false, false, false, false }; - public string[] sATriggers = { "", "", "", "", "" }; - public int[] lsCurve = { 0, 0, 0, 0, 0 }; - public int[] rsCurve = { 0, 0, 0, 0, 0 }; + + public bool[] gyroMouseStickToggle = new bool[5] { false, false, false, + false, false }; + + public SASteeringWheelEmulationAxisType[] sASteeringWheelEmulationAxis = new SASteeringWheelEmulationAxisType[5] { SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None }; + public int[] sASteeringWheelEmulationRange = new int[5] { 360, 360, 360, 360, 360 }; + public int[][] touchDisInvertTriggers = new int[5][] { new int[1] { -1 }, new int[1] { -1 }, new int[1] { -1 }, + new int[1] { -1 }, new int[1] { -1 } }; + public int[] lsCurve = new int[5] { 0, 0, 0, 0, 0 }; + public int[] rsCurve = new int[5] { 0, 0, 0, 0, 0 }; public Boolean useExclusiveMode = false; public Int32 formWidth = 782; public Int32 formHeight = 550; + public int formLocationX = 0; + public int formLocationY = 0; public Boolean startMinimized = false; + public Boolean minToTaskbar = false; public DateTime lastChecked; public int CheckWhen = 1; public int notifications = 2; public bool disconnectBTAtStop = false; public bool swipeProfiles = true; - public bool ds4Mapping = true; + public bool ds4Mapping = false; public bool quickCharge = false; - public int firstXinputPort = 1; public bool closeMini = false; public List actions = new List(); - public List[] ds4settings = { new List(), new List(), new List(), new List(), new List() }; - /*public Dictionary[] customMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] customMapKeys = { null, null, null, null, null }; - public Dictionary[] customMapMacros = { null, null, null, null, null }; - public Dictionary[] customMapButtons = { null, null, null, null, null }; - public Dictionary[] customMapExtras = { null, null, null, null, null }; - - public Dictionary[] shiftCustomMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapKeys = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapMacros = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapButtons = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapExtras = { null, null, null, null, null };*/ - public List[] profileActions = { null, null, null, null, null }; + public List[] ds4settings = new List[5] + { new List(), new List(), new List(), + new List(), new List() }; + + public List[] profileActions = new List[5] { null, null, null, null, null }; + public int[] profileActionCount = new int[5] { 0, 0, 0, 0, 0 }; + public Dictionary[] profileActionDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public Dictionary[] profileActionIndexDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public string useLang = ""; public bool downloadLang = true; public bool useWhiteIcon; public bool flashWhenLate = true; - public int flashWhenLateAt = 10; - public int[] gyroSensitivity = { 100, 100, 100, 100, 100 }; - public int[] gyroInvert = { 0, 0, 0, 0, 0 }; + public int flashWhenLateAt = 20; + public bool useUDPServ = false; + public int udpServPort = 26760; + public string udpServListenAddress = "127.0.0.1"; // 127.0.0.1=IPAddress.Loopback (default), 0.0.0.0=IPAddress.Any as all interfaces, x.x.x.x = Specific ipv4 interface address or hostname + public bool useCustomSteamFolder; + public string customSteamFolder; + // Cache whether profile has custom action + public bool[] containsCustomAction = new bool[5] { false, false, false, false, false }; + + // Cache whether profile has custom extras + public bool[] containsCustomExtras = new bool[5] { false, false, false, false, false }; + + public int[] gyroSensitivity = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroSensVerticalScale = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] gyroTriggerTurns = new bool[5] { true, true, true, true, true }; + public bool[] gyroSmoothing = new bool[5] { false, false, false, false, false }; + public double[] gyroSmoothWeight = new double[5] { 0.5, 0.5, 0.5, 0.5, 0.5 }; + public int[] gyroMouseHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public int[] gyroMouseStickHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public bool[] trackballMode = new bool[5] { false, false, false, false, false }; + public double[] trackballFriction = new double[5] { 10.0, 10.0, 10.0, 10.0, 10.0 }; + // Used to hold the controller type desired in a profile + public OutContType[] outputDevType = new OutContType[5] { OutContType.X360, + OutContType.X360, OutContType.X360, + OutContType.X360, OutContType.X360 }; + + // TRUE=AutoProfile reverts to default profile if current foreground process is unknown, FALSE=Leave existing profile active when a foreground proces is unknown (ie. no matching auto-profile rule) + public bool autoProfileRevertDefaultProfile = true; + + bool tempBool = false; public BackingStore() { for (int i = 0; i < 5; i++) { foreach (DS4Controls dc in Enum.GetValues(typeof(DS4Controls))) + { if (dc != DS4Controls.None) ds4settings[i].Add(new DS4ControlSettings(dc)); - /*customMapKeyTypes[i] = new Dictionary(); - customMapKeys[i] = new Dictionary(); - customMapMacros[i] = new Dictionary(); - customMapButtons[i] = new Dictionary(); - customMapExtras[i] = new Dictionary(); - - shiftCustomMapKeyTypes[i] = new Dictionary(); - shiftCustomMapKeys[i] = new Dictionary(); - shiftCustomMapMacros[i] = new Dictionary(); - shiftCustomMapButtons[i] = new Dictionary(); - shiftCustomMapExtras[i] = new Dictionary();*/ + } + profileActions[i] = new List(); profileActions[i].Add("Disconnect Controller"); + profileActionCount[i] = profileActions[i].Count; + } + + SetupDefaultColors(); + } + + private void SetupDefaultColors() + { + lightbarSettingInfo[0].ds4winSettings.m_Led = new DS4Color(Color.Blue); + lightbarSettingInfo[1].ds4winSettings.m_Led = new DS4Color(Color.Red); + lightbarSettingInfo[2].ds4winSettings.m_Led = new DS4Color(Color.Green); + lightbarSettingInfo[3].ds4winSettings.m_Led = new DS4Color(Color.Pink); + lightbarSettingInfo[4].ds4winSettings.m_Led = new DS4Color(Color.White); + } + + private string stickOutputCurveString(int id) + { + string result = "linear"; + switch (id) + { + case 0: break; + case 1: result = "enhanced-precision"; break; + case 2: result = "quadratic"; break; + case 3: result = "cubic"; break; + case 4: result = "easeout-quad"; break; + case 5: result = "easeout-cubic"; break; + case 6: result = "custom"; break; + default: break; + } + + return result; + } + + private int stickOutputCurveId(string name) + { + int id = 0; + switch (name) + { + case "linear": id = 0; break; + case "enhanced-precision": id = 1; break; + case "quadratic": id = 2; break; + case "cubic": id = 3; break; + case "easeout-quad": id = 4; break; + case "easeout-cubic": id = 5; break; + case "custom": id = 6; break; + default: break; + } + + return id; + } + + private string axisOutputCurveString(int id) + { + return stickOutputCurveString(id); + } + + private int axisOutputCurveId(string name) + { + return stickOutputCurveId(name); + } + + private bool SaTriggerCondValue(string text) + { + bool result = true; + switch (text) + { + case "and": result = true; break; + case "or": result = false; break; + default: result = true; break; } + + return result; + } + + private string SaTriggerCondString(bool value) + { + string result = value ? "and" : "or"; + return result; + } + + public void SetSaTriggerCond(int index, string text) + { + sATriggerCond[index] = SaTriggerCondValue(text); } - /*public X360Controls GetCustomButton(int device, DS4Controls controlName) + public void SetSaMouseStickTriggerCond(int index, string text) { - if (customMapButtons[device].ContainsKey(controlName)) - return customMapButtons[device][controlName]; - else return X360Controls.None; + sAMouseStickTriggerCond[index] = SaTriggerCondValue(text); } - public UInt16 GetCustomKey(int device, DS4Controls controlName) + + public void SetGyroMouseDZ(int index, int value, ControlService control) + { + gyroMouseDZ[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].CursorGyroDead = value; + } + + public void SetGyroMouseToggle(int index, bool value, ControlService control) { - if (customMapKeys[device].ContainsKey(controlName)) - return customMapKeys[device][controlName]; - else return 0; + gyroMouseToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomMacro(int device, DS4Controls controlName) + + public void SetGyroMouseStickToggle(int index, bool value, ControlService control) { - if (customMapMacros[device].ContainsKey(controlName)) - return customMapMacros[device][controlName]; - else return "0"; + gyroMouseStickToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomExtras(int device, DS4Controls controlName) + + private string OutContDeviceString(OutContType id) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "X360"; + switch (id) + { + case OutContType.None: + case OutContType.X360: result = "X360"; break; + case OutContType.DS4: result = "DS4"; break; + default: break; + } + + return result; } - public DS4KeyType GetCustomKeyType(int device, DS4Controls controlName) + + private OutContType OutContDeviceId(string name) { - try + OutContType id = OutContType.X360; + switch (name) { - if (customMapKeyTypes[device].ContainsKey(controlName)) - return customMapKeyTypes[device][controlName]; - else return 0; + case "None": + case "X360": id = OutContType.X360; break; + case "DS4": id = OutContType.DS4; break; + default: break; } - catch { return 0; } + + return id; } - public X360Controls GetShiftCustomButton(int device, DS4Controls controlName) + private void PortOldGyroSettings(int device) { - if (shiftCustomMapButtons[device].ContainsKey(controlName)) - return shiftCustomMapButtons[device][controlName]; - else return X360Controls.None; + if (gyroOutMode[device] == GyroOutMode.None) + { + gyroOutMode[device] = GyroOutMode.Controls; + } } - public UInt16 GetShiftCustomKey(int device, DS4Controls controlName) + + private string GetGyroOutModeString(GyroOutMode mode) { - if (shiftCustomMapKeys[device].ContainsKey(controlName)) - return shiftCustomMapKeys[device][controlName]; - else return 0; + string result = "None"; + switch(mode) + { + case GyroOutMode.Controls: + result = "Controls"; + break; + case GyroOutMode.Mouse: + result = "Mouse"; + break; + case GyroOutMode.MouseJoystick: + result = "MouseJoystick"; + break; + default: + break; + } + + return result; } - public string GetShiftCustomMacro(int device, DS4Controls controlName) + + private GyroOutMode GetGyroOutModeType(string modeString) { - if (shiftCustomMapMacros[device].ContainsKey(controlName)) - return shiftCustomMapMacros[device][controlName]; - else return "0"; + GyroOutMode result = GyroOutMode.None; + switch(modeString) + { + case "Controls": + result = GyroOutMode.Controls; + break; + case "Mouse": + result = GyroOutMode.Mouse; + break; + case "MouseJoystick": + result = GyroOutMode.MouseJoystick; + break; + default: + break; + } + + return result; } - public string GetShiftCustomExtras(int device, DS4Controls controlName) + + private string GetLightbarModeString(LightbarMode mode) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "DS4Win"; + switch (mode) + { + case LightbarMode.DS4Win: + result = "DS4Win"; + break; + case LightbarMode.Passthru: + result = "Passthru"; + break; + default: + break; + } + return result; } - public DS4KeyType GetShiftCustomKeyType(int device, DS4Controls controlName) + + private LightbarMode GetLightbarModeType(string modeString) { - try + LightbarMode result = LightbarMode.DS4Win; + switch (modeString) { - if (shiftCustomMapKeyTypes[device].ContainsKey(controlName)) - return shiftCustomMapKeyTypes[device][controlName]; - else return 0; + case "DS4Win": + result = LightbarMode.DS4Win; + break; + case "Passthru": + result = LightbarMode.Passthru; + break; + default: + break; } - catch { return 0; } - }*/ + + return result; + } + + public bool SaveAsNewProfile(int device, string propath) + { + bool Saved = true; + ResetProfile(device); + Saved = SaveProfile(device, propath); + return Saved; + } public bool SaveProfile(int device, string propath) { @@ -719,49 +2449,81 @@ public bool SaveProfile(int device, string propath) Node = m_Xdoc.CreateComment(string.Format(" DS4Windows Configuration Data. {0} ", DateTime.Now)); m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateComment(string.Format(" Made with DS4Windows version {0} ", Global.exeversion)); + m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateWhitespace("\r\n"); m_Xdoc.AppendChild(Node); Node = m_Xdoc.CreateNode(XmlNodeType.Element, "DS4Windows", null); + LightbarSettingInfo lightbarSettings = lightbarSettingInfo[device]; + LightbarDS4WinInfo lightInfo = lightbarSettings.ds4winSettings; + XmlNode xmlFlushHIDQueue = m_Xdoc.CreateNode(XmlNodeType.Element, "flushHIDQueue", null); xmlFlushHIDQueue.InnerText = flushHIDQueue[device].ToString(); Node.AppendChild(xmlFlushHIDQueue); + XmlNode xmlTouchToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "touchToggle", null); xmlTouchToggle.InnerText = enableTouchToggle[device].ToString(); Node.AppendChild(xmlTouchToggle); XmlNode xmlIdleDisconnectTimeout = m_Xdoc.CreateNode(XmlNodeType.Element, "idleDisconnectTimeout", null); xmlIdleDisconnectTimeout.InnerText = idleDisconnectTimeout[device].ToString(); Node.AppendChild(xmlIdleDisconnectTimeout); XmlNode xmlColor = m_Xdoc.CreateNode(XmlNodeType.Element, "Color", null); - xmlColor.InnerText = m_Leds[device].red.ToString() + "," + m_Leds[device].green.ToString() + "," + m_Leds[device].blue.ToString(); + xmlColor.InnerText = lightInfo.m_Led.red.ToString() + "," + lightInfo.m_Led.green.ToString() + "," + lightInfo.m_Led.blue.ToString(); Node.AppendChild(xmlColor); XmlNode xmlRumbleBoost = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleBoost", null); xmlRumbleBoost.InnerText = rumble[device].ToString(); Node.AppendChild(xmlRumbleBoost); - XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = ledAsBattery[device].ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); - XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = flashType[device].ToString(); Node.AppendChild(xmlLowBatteryFlash); - XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = flashAt[device].ToString(); Node.AppendChild(xmlFlashBatterAt); + XmlNode xmlRumbleAutostopTime = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleAutostopTime", null); xmlRumbleAutostopTime.InnerText = rumbleAutostopTime[device].ToString(); Node.AppendChild(xmlRumbleAutostopTime); + XmlNode xmlLightbarMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LightbarMode", null); xmlLightbarMode.InnerText = GetLightbarModeString(lightbarSettings.mode); Node.AppendChild(xmlLightbarMode); + XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = lightInfo.ledAsBattery.ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); + XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = lightInfo.flashType.ToString(); Node.AppendChild(xmlLowBatteryFlash); + XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = lightInfo.flashAt.ToString(); Node.AppendChild(xmlFlashBatterAt); XmlNode xmlTouchSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "touchSensitivity", null); xmlTouchSensitivity.InnerText = touchSensitivity[device].ToString(); Node.AppendChild(xmlTouchSensitivity); XmlNode xmlLowColor = m_Xdoc.CreateNode(XmlNodeType.Element, "LowColor", null); - xmlLowColor.InnerText = m_LowLeds[device].red.ToString() + "," + m_LowLeds[device].green.ToString() + "," + m_LowLeds[device].blue.ToString(); + xmlLowColor.InnerText = lightInfo.m_LowLed.red.ToString() + "," + lightInfo.m_LowLed.green.ToString() + "," + lightInfo.m_LowLed.blue.ToString(); Node.AppendChild(xmlLowColor); XmlNode xmlChargingColor = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingColor", null); - xmlChargingColor.InnerText = m_ChargingLeds[device].red.ToString() + "," + m_ChargingLeds[device].green.ToString() + "," + m_ChargingLeds[device].blue.ToString(); + xmlChargingColor.InnerText = lightInfo.m_ChargingLed.red.ToString() + "," + lightInfo.m_ChargingLed.green.ToString() + "," + lightInfo.m_ChargingLed.blue.ToString(); Node.AppendChild(xmlChargingColor); XmlNode xmlFlashColor = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashColor", null); - xmlFlashColor.InnerText = m_FlashLeds[device].red.ToString() + "," + m_FlashLeds[device].green.ToString() + "," + m_FlashLeds[device].blue.ToString(); + xmlFlashColor.InnerText = lightInfo.m_FlashLed.red.ToString() + "," + lightInfo.m_FlashLed.green.ToString() + "," + lightInfo.m_FlashLed.blue.ToString(); Node.AppendChild(xmlFlashColor); XmlNode xmlTouchpadJitterCompensation = m_Xdoc.CreateNode(XmlNodeType.Element, "touchpadJitterCompensation", null); xmlTouchpadJitterCompensation.InnerText = touchpadJitterCompensation[device].ToString(); Node.AppendChild(xmlTouchpadJitterCompensation); XmlNode xmlLowerRCOn = m_Xdoc.CreateNode(XmlNodeType.Element, "lowerRCOn", null); xmlLowerRCOn.InnerText = lowerRCOn[device].ToString(); Node.AppendChild(xmlLowerRCOn); XmlNode xmlTapSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "tapSensitivity", null); xmlTapSensitivity.InnerText = tapSensitivity[device].ToString(); Node.AppendChild(xmlTapSensitivity); XmlNode xmlDouble = m_Xdoc.CreateNode(XmlNodeType.Element, "doubleTap", null); xmlDouble.InnerText = doubleTap[device].ToString(); Node.AppendChild(xmlDouble); XmlNode xmlScrollSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "scrollSensitivity", null); xmlScrollSensitivity.InnerText = scrollSensitivity[device].ToString(); Node.AppendChild(xmlScrollSensitivity); - XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2Deadzone[device].ToString(); Node.AppendChild(xmlLeftTriggerMiddle); - XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2Deadzone[device].ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLeftTriggerMiddle); + XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlTouchpadInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchpadInvert", null); xmlTouchpadInvert.InnerText = touchpadInvert[device].ToString(); Node.AppendChild(xmlTouchpadInvert); + XmlNode xmlL2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "L2AntiDeadZone", null); xmlL2AD.InnerText = l2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlL2AD); + XmlNode xmlR2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "R2AntiDeadZone", null); xmlR2AD.InnerText = r2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlR2AD); + XmlNode xmlL2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxZone", null); xmlL2Maxzone.InnerText = l2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlL2Maxzone); + XmlNode xmlR2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxZone", null); xmlR2Maxzone.InnerText = r2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlR2Maxzone); + XmlNode xmlL2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxOutput", null); xmlL2MaxOutput.InnerText = l2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlL2MaxOutput); + XmlNode xmlR2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxOutput", null); xmlR2MaxOutput.InnerText = r2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlR2MaxOutput); XmlNode xmlButtonMouseSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "ButtonMouseSensitivity", null); xmlButtonMouseSensitivity.InnerText = buttonMouseSensitivity[device].ToString(); Node.AppendChild(xmlButtonMouseSensitivity); - XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = rainbow[device].ToString(); Node.AppendChild(xmlRainbow); - XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = LSDeadzone[device].ToString(); Node.AppendChild(xmlLSD); - XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = RSDeadzone[device].ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = lightInfo.rainbow.ToString(); Node.AppendChild(xmlRainbow); + XmlNode xmlMaxSatRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "MaxSatRainbow", null); xmlMaxSatRainbow.InnerText = Convert.ToInt32(lightInfo.maxRainbowSat * 100.0).ToString(); Node.AppendChild(xmlMaxSatRainbow); + XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = lsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLSD); + XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = rsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlLSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSAntiDeadZone", null); xmlLSAD.InnerText = lsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlLSAD); + XmlNode xmlRSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSAntiDeadZone", null); xmlRSAD.InnerText = rsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlRSAD); + XmlNode xmlLSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxZone", null); xmlLSMaxZone.InnerText = lsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlLSMaxZone); + XmlNode xmlRSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxZone", null); xmlRSMaxZone.InnerText = rsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlRSMaxZone); + XmlNode xmlLSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxOutput", null); xmlLSMaxOutput.InnerText = lsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlLSMaxOutput); + XmlNode xmlRSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxOutput", null); xmlRSMaxOutput.InnerText = rsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlRSMaxOutput); + XmlNode xmlLSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "LSRotation", null); xmlLSRotation.InnerText = Convert.ToInt32(LSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlLSRotation); + XmlNode xmlRSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "RSRotation", null); xmlRSRotation.InnerText = Convert.ToInt32(RSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlRSRotation); + XmlNode xmlSXD = m_Xdoc.CreateNode(XmlNodeType.Element, "SXDeadZone", null); xmlSXD.InnerText = SXDeadzone[device].ToString(); Node.AppendChild(xmlSXD); XmlNode xmlSZD = m_Xdoc.CreateNode(XmlNodeType.Element, "SZDeadZone", null); xmlSZD.InnerText = SZDeadzone[device].ToString(); Node.AppendChild(xmlSZD); + XmlNode xmlSXMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXMaxZone", null); xmlSXMaxzone.InnerText = Convert.ToInt32(SXMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXMaxzone); + XmlNode xmlSZMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZMaxZone", null); xmlSZMaxzone.InnerText = Convert.ToInt32(SZMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZMaxzone); + + XmlNode xmlSXAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXAntiDeadZone", null); xmlSXAntiDeadzone.InnerText = Convert.ToInt32(SXAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXAntiDeadzone); + XmlNode xmlSZAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZAntiDeadZone", null); xmlSZAntiDeadzone.InnerText = Convert.ToInt32(SZAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZAntiDeadzone); + XmlNode xmlSens = m_Xdoc.CreateNode(XmlNodeType.Element, "Sensitivity", null); xmlSens.InnerText = $"{LSSens[device]}|{RSSens[device]}|{l2Sens[device]}|{r2Sens[device]}|{SXSens[device]}|{SZSens[device]}"; Node.AppendChild(xmlSens); - XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = chargingType[device].ToString(); Node.AppendChild(xmlChargingType); + XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = lightInfo.chargingType.ToString(); Node.AppendChild(xmlChargingType); XmlNode xmlMouseAccel = m_Xdoc.CreateNode(XmlNodeType.Element, "MouseAcceleration", null); xmlMouseAccel.InnerText = mouseAccel[device].ToString(); Node.AppendChild(xmlMouseAccel); //XmlNode xmlShiftMod = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftModifier", null); xmlShiftMod.InnerText = shiftModifier[device].ToString(); Node.AppendChild(xmlShiftMod); XmlNode xmlLaunchProgram = m_Xdoc.CreateNode(XmlNodeType.Element, "LaunchProgram", null); xmlLaunchProgram.InnerText = launchProgram[device].ToString(); Node.AppendChild(xmlLaunchProgram); @@ -770,11 +2532,75 @@ public bool SaveProfile(int device, string propath) XmlNode xmlUseTPforControls = m_Xdoc.CreateNode(XmlNodeType.Element, "UseTPforControls", null); xmlUseTPforControls.InnerText = useTPforControls[device].ToString(); Node.AppendChild(xmlUseTPforControls); XmlNode xmlUseSAforMouse = m_Xdoc.CreateNode(XmlNodeType.Element, "UseSAforMouse", null); xmlUseSAforMouse.InnerText = useSAforMouse[device].ToString(); Node.AppendChild(xmlUseSAforMouse); XmlNode xmlSATriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggers", null); xmlSATriggers.InnerText = sATriggers[device].ToString(); Node.AppendChild(xmlSATriggers); + XmlNode xmlSATriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggerCond", null); xmlSATriggerCond.InnerText = SaTriggerCondString(sATriggerCond[device]); Node.AppendChild(xmlSATriggerCond); + XmlNode xmlSASteeringWheelEmulationAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationAxis", null); xmlSASteeringWheelEmulationAxis.InnerText = sASteeringWheelEmulationAxis[device].ToString("G"); Node.AppendChild(xmlSASteeringWheelEmulationAxis); + XmlNode xmlSASteeringWheelEmulationRange = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationRange", null); xmlSASteeringWheelEmulationRange.InnerText = sASteeringWheelEmulationRange[device].ToString(); Node.AppendChild(xmlSASteeringWheelEmulationRange); + + + + XmlNode xmlTouchDisInvTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchDisInvTriggers", null); + string tempTouchDisInv = string.Join(",", touchDisInvertTriggers[device]); + xmlTouchDisInvTriggers.InnerText = tempTouchDisInv; + Node.AppendChild(xmlTouchDisInvTriggers); + XmlNode xmlGyroSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensitivity", null); xmlGyroSensitivity.InnerText = gyroSensitivity[device].ToString(); Node.AppendChild(xmlGyroSensitivity); + XmlNode xmlGyroSensVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensVerticalScale", null); xmlGyroSensVerticalScale.InnerText = gyroSensVerticalScale[device].ToString(); Node.AppendChild(xmlGyroSensVerticalScale); XmlNode xmlGyroInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroInvert", null); xmlGyroInvert.InnerText = gyroInvert[device].ToString(); Node.AppendChild(xmlGyroInvert); + XmlNode xmlGyroTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroTriggerTurns", null); xmlGyroTriggerTurns.InnerText = gyroTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroTriggerTurns); + XmlNode xmlGyroSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothingWeight", null); xmlGyroSmoothWeight.InnerText = Convert.ToInt32(gyroSmoothWeight[device] * 100).ToString(); Node.AppendChild(xmlGyroSmoothWeight); + XmlNode xmlGyroSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothing", null); xmlGyroSmoothing.InnerText = gyroSmoothing[device].ToString(); Node.AppendChild(xmlGyroSmoothing); + XmlNode xmlGyroMouseHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseHAxis", null); xmlGyroMouseHAxis.InnerText = gyroMouseHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMouseHAxis); + XmlNode xmlGyroMouseDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseDeadZone", null); xmlGyroMouseDZ.InnerText = gyroMouseDZ[device].ToString(); Node.AppendChild(xmlGyroMouseDZ); + XmlNode xmlGyroMouseToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseToggle", null); xmlGyroMouseToggle.InnerText = gyroMouseToggle[device].ToString(); Node.AppendChild(xmlGyroMouseToggle); + + XmlNode xmlGyroOutMode = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroOutputMode", null); xmlGyroOutMode.InnerText = GetGyroOutModeString(gyroOutMode[device]); Node.AppendChild(xmlGyroOutMode); + XmlNode xmlGyroMStickTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggers", null); xmlGyroMStickTriggers.InnerText = sAMouseStickTriggers[device].ToString(); Node.AppendChild(xmlGyroMStickTriggers); + XmlNode xmlGyroMStickTriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerCond", null); xmlGyroMStickTriggerCond.InnerText = SaTriggerCondString(sAMouseStickTriggerCond[device]); Node.AppendChild(xmlGyroMStickTriggerCond); + XmlNode xmlGyroMStickTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerTurns", null); xmlGyroMStickTriggerTurns.InnerText = gyroMouseStickTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroMStickTriggerTurns); + XmlNode xmlGyroMStickHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickHAxis", null); xmlGyroMStickHAxis.InnerText = gyroMouseStickHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMStickHAxis); + XmlNode xmlGyroMStickDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickDeadZone", null); xmlGyroMStickDZ.InnerText = gyroMStickInfo[device].deadZone.ToString(); Node.AppendChild(xmlGyroMStickDZ); + XmlNode xmlGyroMStickMaxZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickMaxZone", null); xmlGyroMStickMaxZ.InnerText = gyroMStickInfo[device].maxZone.ToString(); Node.AppendChild(xmlGyroMStickMaxZ); + XmlNode xmlGyroMStickAntiDX = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadX", null); xmlGyroMStickAntiDX.InnerText = gyroMStickInfo[device].antiDeadX.ToString(); Node.AppendChild(xmlGyroMStickAntiDX); + XmlNode xmlGyroMStickAntiDY = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadY", null); xmlGyroMStickAntiDY.InnerText = gyroMStickInfo[device].antiDeadY.ToString(); Node.AppendChild(xmlGyroMStickAntiDY); + XmlNode xmlGyroMStickInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickInvert", null); xmlGyroMStickInvert.InnerText = gyroMStickInfo[device].inverted.ToString(); Node.AppendChild(xmlGyroMStickInvert); + XmlNode xmlGyroMStickToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickToggle", null); xmlGyroMStickToggle.InnerText = gyroMouseStickToggle[device].ToString(); Node.AppendChild(xmlGyroMStickToggle); + XmlNode xmlGyroMStickVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickVerticalScale", null); xmlGyroMStickVerticalScale.InnerText = gyroMStickInfo[device].vertScale.ToString(); Node.AppendChild(xmlGyroMStickVerticalScale); + XmlNode xmlGyroMStickSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothing", null); xmlGyroMStickSmoothing.InnerText = gyroMStickInfo[device].useSmoothing.ToString(); Node.AppendChild(xmlGyroMStickSmoothing); + XmlNode xmlGyroMStickSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothingWeight", null); xmlGyroMStickSmoothWeight.InnerText = Convert.ToInt32(gyroMStickInfo[device].smoothWeight * 100).ToString(); Node.AppendChild(xmlGyroMStickSmoothWeight); + XmlNode xmlLSC = m_Xdoc.CreateNode(XmlNodeType.Element, "LSCurve", null); xmlLSC.InnerText = lsCurve[device].ToString(); Node.AppendChild(xmlLSC); XmlNode xmlRSC = m_Xdoc.CreateNode(XmlNodeType.Element, "RSCurve", null); xmlRSC.InnerText = rsCurve[device].ToString(); Node.AppendChild(xmlRSC); XmlNode xmlProfileActions = m_Xdoc.CreateNode(XmlNodeType.Element, "ProfileActions", null); xmlProfileActions.InnerText = string.Join("/", profileActions[device]); Node.AppendChild(xmlProfileActions); + XmlNode xmlBTPollRate = m_Xdoc.CreateNode(XmlNodeType.Element, "BTPollRate", null); xmlBTPollRate.InnerText = btPollRate[device].ToString(); Node.AppendChild(xmlBTPollRate); + + XmlNode xmlLsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveMode", null); xmlLsOutputCurveMode.InnerText = stickOutputCurveString(getLsOutCurveMode(device)); Node.AppendChild(xmlLsOutputCurveMode); + XmlNode xmlLsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveCustom", null); xmlLsOutputCurveCustom.InnerText = lsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlLsOutputCurveCustom); + + XmlNode xmlRsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveMode", null); xmlRsOutputCurveMode.InnerText = stickOutputCurveString(getRsOutCurveMode(device)); Node.AppendChild(xmlRsOutputCurveMode); + XmlNode xmlRsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveCustom", null); xmlRsOutputCurveCustom.InnerText = rsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlRsOutputCurveCustom); + + XmlNode xmlLsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSSquareStick", null); xmlLsSquareStickMode.InnerText = squStickInfo[device].lsMode.ToString(); Node.AppendChild(xmlLsSquareStickMode); + XmlNode xmlRsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSSquareStick", null); xmlRsSquareStickMode.InnerText = squStickInfo[device].rsMode.ToString(); Node.AppendChild(xmlRsSquareStickMode); + + XmlNode xmlSquareStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareStickRoundness", null); xmlSquareStickRoundness.InnerText = squStickInfo[device].lsRoundness.ToString(); Node.AppendChild(xmlSquareStickRoundness); + XmlNode xmlSquareRStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareRStickRoundness", null); xmlSquareRStickRoundness.InnerText = squStickInfo[device].rsRoundness.ToString(); Node.AppendChild(xmlSquareRStickRoundness); + + XmlNode xmlL2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveMode", null); xmlL2OutputCurveMode.InnerText = axisOutputCurveString(getL2OutCurveMode(device)); Node.AppendChild(xmlL2OutputCurveMode); + XmlNode xmlL2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveCustom", null); xmlL2OutputCurveCustom.InnerText = l2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlL2OutputCurveCustom); + + XmlNode xmlR2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveMode", null); xmlR2OutputCurveMode.InnerText = axisOutputCurveString(getR2OutCurveMode(device)); Node.AppendChild(xmlR2OutputCurveMode); + XmlNode xmlR2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveCustom", null); xmlR2OutputCurveCustom.InnerText = r2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlR2OutputCurveCustom); + + XmlNode xmlSXOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveMode", null); xmlSXOutputCurveMode.InnerText = axisOutputCurveString(getSXOutCurveMode(device)); Node.AppendChild(xmlSXOutputCurveMode); + XmlNode xmlSXOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveCustom", null); xmlSXOutputCurveCustom.InnerText = sxOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSXOutputCurveCustom); + + XmlNode xmlSZOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveMode", null); xmlSZOutputCurveMode.InnerText = axisOutputCurveString(getSZOutCurveMode(device)); Node.AppendChild(xmlSZOutputCurveMode); + XmlNode xmlSZOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveCustom", null); xmlSZOutputCurveCustom.InnerText = szOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSZOutputCurveCustom); + + XmlNode xmlTrackBallMode = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballMode", null); xmlTrackBallMode.InnerText = trackballMode[device].ToString(); Node.AppendChild(xmlTrackBallMode); + XmlNode xmlTrackBallFriction = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballFriction", null); xmlTrackBallFriction.InnerText = trackballFriction[device].ToString(); Node.AppendChild(xmlTrackBallFriction); + + XmlNode xmlOutContDevice = m_Xdoc.CreateNode(XmlNodeType.Element, "OutputContDevice", null); xmlOutContDevice.InnerText = OutContDeviceString(outputDevType[device]); Node.AppendChild(xmlOutContDevice); XmlNode NodeControl = m_Xdoc.CreateNode(XmlNodeType.Element, "Control", null); XmlNode Key = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); @@ -799,16 +2625,21 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.action is string) + { if (dcs.action.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.keyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; - if (dcs.keyType.HasFlag(DS4KeyType.Macro)) + else if (dcs.keyType.HasFlag(DS4KeyType.Macro)) keyType += DS4KeyType.Macro; + if (dcs.keyType.HasFlag(DS4KeyType.Toggle)) keyType += DS4KeyType.Toggle; if (dcs.keyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -828,21 +2659,31 @@ public bool SaveProfile(int device, string propath) buttonNode.InnerText = dcs.action.ToString(); Key.AppendChild(buttonNode); } - else if (dcs.action is string || dcs.action is X360Controls) + else if (dcs.action is string) { buttonNode.InnerText = dcs.action.ToString(); Button.AppendChild(buttonNode); } + else if (dcs.action is X360Controls) + { + buttonNode.InnerText = getX360ControlString((X360Controls)dcs.action); + Button.AppendChild(buttonNode); + } } bool hasvalue = false; if (!string.IsNullOrEmpty(dcs.extras)) + { foreach (string s in dcs.extras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -856,8 +2697,11 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.shiftAction is string) + { if (dcs.shiftAction.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.shiftKeyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; if (dcs.shiftKeyType.HasFlag(DS4KeyType.Macro)) @@ -866,6 +2710,7 @@ public bool SaveProfile(int device, string propath) keyType += DS4KeyType.Toggle; if (dcs.shiftKeyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateElement(dcs.control.ToString()); @@ -892,14 +2737,20 @@ public bool SaveProfile(int device, string propath) ShiftButton.AppendChild(buttonNode); } } + hasvalue = false; if (!string.IsNullOrEmpty(dcs.shiftExtras)) + { foreach (string s in dcs.shiftExtras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -907,6 +2758,7 @@ public bool SaveProfile(int device, string propath) ShiftExtras.AppendChild(extraNode); } } + Node.AppendChild(NodeControl); if (Button.HasChildNodes) NodeControl.AppendChild(Button); @@ -932,104 +2784,7 @@ public bool SaveProfile(int device, string propath) NodeShiftControl.AppendChild(ShiftKeyType); if (ShiftExtras.HasChildNodes) NodeShiftControl.AppendChild(ShiftExtras); - /*else if (xmlControls != null) - { - Node.AppendChild(xmlControls); - }*/ - /*if (shiftModifier[device] > 0) - { - XmlNode NodeShiftControl = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftControl", null); - - XmlNode ShiftKey = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); - XmlNode ShiftMacro = m_Xdoc.CreateNode(XmlNodeType.Element, "Macro", null); - XmlNode ShiftKeyType = m_Xdoc.CreateNode(XmlNodeType.Element, "KeyType", null); - XmlNode ShiftButton = m_Xdoc.CreateNode(XmlNodeType.Element, "Button", null); - XmlNode ShiftExtras = m_Xdoc.CreateNode(XmlNodeType.Element, "Extras", null); - if (shiftbuttons != null) - { - foreach (var button in shiftbuttons) - { - // Save even if string (for xbox controller buttons) - if (button.Tag != null) - { - XmlNode buttonNode; - string keyType = String.Empty; - if (button.Tag is KeyValuePair) - if (((KeyValuePair)button.Tag).Key == "Unbound") - keyType += DS4KeyType.Unbound; - - if (button.Font.Strikeout) - keyType += DS4KeyType.HoldMacro; - if (button.Font.Underline) - keyType += DS4KeyType.Macro; - if (button.Font.Italic) - keyType += DS4KeyType.Toggle; - if (button.Font.Bold) - keyType += DS4KeyType.ScanCode; - if (keyType != String.Empty) - { - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - buttonNode.InnerText = keyType; - ShiftKeyType.AppendChild(buttonNode); - } - - string[] extras; - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - if (button.Tag is KeyValuePair, string> || button.Tag is KeyValuePair || button.Tag is KeyValuePair) - { - KeyValuePair
, or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID `%s`.", d, h) : s(f), c = c || {}, c[h] = c[h] || [], c[h][d] = f, l = l || [], l.push(f) + } var v = o.dangerouslyRenderMarkup(t); + if (l) + for (var m = 0; m < l.length; m++) l[m].parentNode.removeChild(l[m]); + for (var y = 0; y < e.length; y++) switch (u = e[y], u.type) { + case a.INSERT_MARKUP: + r(u.parentNode, v[u.markupIndex], u.toIndex); + break; + case a.MOVE_EXISTING: + r(u.parentNode, c[u.parentID][u.fromIndex], u.toIndex); + break; + case a.TEXT_CONTENT: + i(u.parentNode, u.textContent); + break; + case a.REMOVE_NODE: + } + } + }; + t.exports = u + }).call(this, e("_process")) + }, { + "./Danger": 19, + "./ReactMultiChildUpdateTypes": 85, + "./invariant": 157, + "./setTextContent": 172, + _process: 2 + }], + 17: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + return (e & t) === t + } + var o = e("./invariant"), + a = { + MUST_USE_ATTRIBUTE: 1, + MUST_USE_PROPERTY: 2, + HAS_SIDE_EFFECTS: 4, + HAS_BOOLEAN_VALUE: 8, + HAS_NUMERIC_VALUE: 16, + HAS_POSITIVE_NUMERIC_VALUE: 48, + HAS_OVERLOADED_BOOLEAN_VALUE: 64, + injectDOMPropertyConfig: function(e) { + var t = e.Properties || {}, + i = e.DOMAttributeNames || {}, + u = e.DOMPropertyNames || {}, + c = e.DOMMutationMethods || {}; + e.isCustomAttribute && s._isCustomAttributeFunctions.push(e.isCustomAttribute); + for (var l in t) { + "production" !== n.env.NODE_ENV ? o(!s.isStandardName.hasOwnProperty(l), "injectDOMPropertyConfig(...): You're trying to inject DOM property '%s' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.", l) : o(!s.isStandardName.hasOwnProperty(l)), s.isStandardName[l] = !0; + var p = l.toLowerCase(); + if (s.getPossibleStandardName[p] = l, i.hasOwnProperty(l)) { + var d = i[l]; + s.getPossibleStandardName[d] = l, s.getAttributeName[l] = d + } else s.getAttributeName[l] = p; + s.getPropertyName[l] = u.hasOwnProperty(l) ? u[l] : l, s.getMutationMethod[l] = c.hasOwnProperty(l) ? c[l] : null; + var f = t[l]; + s.mustUseAttribute[l] = r(f, a.MUST_USE_ATTRIBUTE), s.mustUseProperty[l] = r(f, a.MUST_USE_PROPERTY), s.hasSideEffects[l] = r(f, a.HAS_SIDE_EFFECTS), s.hasBooleanValue[l] = r(f, a.HAS_BOOLEAN_VALUE), s.hasNumericValue[l] = r(f, a.HAS_NUMERIC_VALUE), s.hasPositiveNumericValue[l] = r(f, a.HAS_POSITIVE_NUMERIC_VALUE), s.hasOverloadedBooleanValue[l] = r(f, a.HAS_OVERLOADED_BOOLEAN_VALUE), "production" !== n.env.NODE_ENV ? o(!s.mustUseAttribute[l] || !s.mustUseProperty[l], "DOMProperty: Cannot require using both attribute and property: %s", l) : o(!s.mustUseAttribute[l] || !s.mustUseProperty[l]), "production" !== n.env.NODE_ENV ? o(s.mustUseProperty[l] || !s.hasSideEffects[l], "DOMProperty: Properties that have side effects must use property: %s", l) : o(s.mustUseProperty[l] || !s.hasSideEffects[l]), "production" !== n.env.NODE_ENV ? o(!!s.hasBooleanValue[l] + !!s.hasNumericValue[l] + !!s.hasOverloadedBooleanValue[l] <= 1, "DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s", l) : o(!!s.hasBooleanValue[l] + !!s.hasNumericValue[l] + !!s.hasOverloadedBooleanValue[l] <= 1) + } + } + }, + i = {}, + s = { + ID_ATTRIBUTE_NAME: "data-reactid", + isStandardName: {}, + getPossibleStandardName: {}, + getAttributeName: {}, + getPropertyName: {}, + getMutationMethod: {}, + mustUseAttribute: {}, + mustUseProperty: {}, + hasSideEffects: {}, + hasBooleanValue: {}, + hasNumericValue: {}, + hasPositiveNumericValue: {}, + hasOverloadedBooleanValue: {}, + _isCustomAttributeFunctions: [], + isCustomAttribute: function(e) { + for (var t = 0; t < s._isCustomAttributeFunctions.length; t++) { + var n = s._isCustomAttributeFunctions[t]; + if (n(e)) return !0 + } + return !1 + }, + getDefaultValueForProperty: function(e, t) { + var n, r = i[e]; + return r || (i[e] = r = {}), t in r || (n = document.createElement(e), r[t] = n[t]), r[t] + }, + injection: a + }; + t.exports = s + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 18: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + return null == t || o.hasBooleanValue[e] && !t || o.hasNumericValue[e] && isNaN(t) || o.hasPositiveNumericValue[e] && 1 > t || o.hasOverloadedBooleanValue[e] && t === !1 + } + var o = e("./DOMProperty"), + a = e("./quoteAttributeValueForBrowser"), + i = e("./warning"); + if ("production" !== n.env.NODE_ENV) var s = { + children: !0, + dangerouslySetInnerHTML: !0, + key: !0, + ref: !0 + }, + u = {}, + c = function(e) { + if (!(s.hasOwnProperty(e) && s[e] || u.hasOwnProperty(e) && u[e])) { + u[e] = !0; + var t = e.toLowerCase(), + r = o.isCustomAttribute(t) ? t : o.getPossibleStandardName.hasOwnProperty(t) ? o.getPossibleStandardName[t] : null; + "production" !== n.env.NODE_ENV ? i(null == r, "Unknown DOM property %s. Did you mean %s?", e, r) : null + } + }; + var l = { + createMarkupForID: function(e) { + return o.ID_ATTRIBUTE_NAME + "=" + a(e) + }, + createMarkupForProperty: function(e, t) { + if (o.isStandardName.hasOwnProperty(e) && o.isStandardName[e]) { + if (r(e, t)) return ""; + var i = o.getAttributeName[e]; + return o.hasBooleanValue[e] || o.hasOverloadedBooleanValue[e] && t === !0 ? i : i + "=" + a(t) + } + return o.isCustomAttribute(e) ? null == t ? "" : e + "=" + a(t) : ("production" !== n.env.NODE_ENV && c(e), null) + }, + setValueForProperty: function(e, t, a) { + if (o.isStandardName.hasOwnProperty(t) && o.isStandardName[t]) { + var i = o.getMutationMethod[t]; + if (i) i(e, a); + else if (r(t, a)) this.deleteValueForProperty(e, t); + else if (o.mustUseAttribute[t]) e.setAttribute(o.getAttributeName[t], "" + a); + else { + var s = o.getPropertyName[t]; + o.hasSideEffects[t] && "" + e[s] == "" + a || (e[s] = a) + } + } else o.isCustomAttribute(t) ? null == a ? e.removeAttribute(t) : e.setAttribute(t, "" + a) : "production" !== n.env.NODE_ENV && c(t) + }, + deleteValueForProperty: function(e, t) { + if (o.isStandardName.hasOwnProperty(t) && o.isStandardName[t]) { + var r = o.getMutationMethod[t]; + if (r) r(e, void 0); + else if (o.mustUseAttribute[t]) e.removeAttribute(o.getAttributeName[t]); + else { + var a = o.getPropertyName[t], + i = o.getDefaultValueForProperty(e.nodeName, a); + o.hasSideEffects[t] && "" + e[a] === i || (e[a] = i) + } + } else o.isCustomAttribute(t) ? e.removeAttribute(t) : "production" !== n.env.NODE_ENV && c(t) + } + }; + t.exports = l + }).call(this, e("_process")) + }, { + "./DOMProperty": 17, + "./quoteAttributeValueForBrowser": 170, + "./warning": 178, + _process: 2 + }], + 19: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return e.substring(1, e.indexOf(" ")) + } + var o = e("./ExecutionEnvironment"), + a = e("./createNodesFromMarkup"), + i = e("./emptyFunction"), + s = e("./getMarkupWrap"), + u = e("./invariant"), + c = /^(<[^ \/>]+)/, + l = "data-danger-index", + p = { + dangerouslyRenderMarkup: function(e) { + "production" !== n.env.NODE_ENV ? u(o.canUseDOM, "dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.") : u(o.canUseDOM); + for (var t, p = {}, d = 0; d < e.length; d++) "production" !== n.env.NODE_ENV ? u(e[d], "dangerouslyRenderMarkup(...): Missing markup.") : u(e[d]), t = r(e[d]), t = s(t) ? t : "*", p[t] = p[t] || [], p[t][d] = e[d]; + var f = [], + h = 0; + for (t in p) + if (p.hasOwnProperty(t)) { + var v, m = p[t]; + for (v in m) + if (m.hasOwnProperty(v)) { + var y = m[v]; + m[v] = y.replace(c, "$1 " + l + '="' + v + '" ') + } for (var g = a(m.join(""), i), E = 0; E < g.length; ++E) { + var C = g[E]; + C.hasAttribute && C.hasAttribute(l) ? (v = +C.getAttribute(l), C.removeAttribute(l), "production" !== n.env.NODE_ENV ? u(!f.hasOwnProperty(v), "Danger: Assigning to an already-occupied result index.") : u(!f.hasOwnProperty(v)), f[v] = C, h += 1) : "production" !== n.env.NODE_ENV && console.error("Danger: Discarding unexpected node:", C) + } + } return "production" !== n.env.NODE_ENV ? u(h === f.length, "Danger: Did not assign to every index of resultList.") : u(h === f.length), "production" !== n.env.NODE_ENV ? u(f.length === e.length, "Danger: Expected markup to render %s nodes, but rendered %s.", e.length, f.length) : u(f.length === e.length), f + }, + dangerouslyReplaceNodeWithMarkup: function(e, t) { + "production" !== n.env.NODE_ENV ? u(o.canUseDOM, "dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.") : u(o.canUseDOM), "production" !== n.env.NODE_ENV ? u(t, "dangerouslyReplaceNodeWithMarkup(...): Missing markup.") : u(t), "production" !== n.env.NODE_ENV ? u("html" !== e.tagName.toLowerCase(), "dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the node. This is because browser quirks make this unreliable and/or slow. If you want to render to the root you must use server rendering. See React.renderToString().") : u("html" !== e.tagName.toLowerCase()); + var r = a(t, i)[0]; + e.parentNode.replaceChild(r, e) + } + }; + t.exports = p + }).call(this, e("_process")) + }, { + "./ExecutionEnvironment": 28, + "./createNodesFromMarkup": 133, + "./emptyFunction": 136, + "./getMarkupWrap": 149, + "./invariant": 157, + _process: 2 + }], + 20: [function(e, t) { + "use strict"; + var n = e("./keyOf"), + r = [n({ + ResponderEventPlugin: null + }), n({ + SimpleEventPlugin: null + }), n({ + TapEventPlugin: null + }), n({ + EnterLeaveEventPlugin: null + }), n({ + ChangeEventPlugin: null + }), n({ + SelectEventPlugin: null + }), n({ + BeforeInputEventPlugin: null + }), n({ + AnalyticsEventPlugin: null + }), n({ + MobileSafariClickEventPlugin: null + })]; + t.exports = r + }, { + "./keyOf": 164 + }], + 21: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./EventPropagators"), + o = e("./SyntheticMouseEvent"), + a = e("./ReactMount"), + i = e("./keyOf"), + s = n.topLevelTypes, + u = a.getFirstReactDOM, + c = { + mouseEnter: { + registrationName: i({ + onMouseEnter: null + }), + dependencies: [s.topMouseOut, s.topMouseOver] + }, + mouseLeave: { + registrationName: i({ + onMouseLeave: null + }), + dependencies: [s.topMouseOut, s.topMouseOver] + } + }, + l = [null, null], + p = { + eventTypes: c, + extractEvents: function(e, t, n, i) { + if (e === s.topMouseOver && (i.relatedTarget || i.fromElement)) return null; + if (e !== s.topMouseOut && e !== s.topMouseOver) return null; + var p; + if (t.window === t) p = t; + else { + var d = t.ownerDocument; + p = d ? d.defaultView || d.parentWindow : window + } + var f, h; + if (e === s.topMouseOut ? (f = t, h = u(i.relatedTarget || i.toElement) || p) : (f = p, h = t), f === h) return null; + var v = f ? a.getID(f) : "", + m = h ? a.getID(h) : "", + y = o.getPooled(c.mouseLeave, v, i); + y.type = "mouseleave", y.target = f, y.relatedTarget = h; + var g = o.getPooled(c.mouseEnter, m, i); + return g.type = "mouseenter", g.target = h, g.relatedTarget = f, r.accumulateEnterLeaveDispatches(y, g, v, m), l[0] = y, l[1] = g, l + } + }; + t.exports = p + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ReactMount": 83, + "./SyntheticMouseEvent": 119, + "./keyOf": 164 + }], + 22: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + bubbled: null, + captured: null + }), + o = n({ + topBlur: null, + topChange: null, + topClick: null, + topCompositionEnd: null, + topCompositionStart: null, + topCompositionUpdate: null, + topContextMenu: null, + topCopy: null, + topCut: null, + topDoubleClick: null, + topDrag: null, + topDragEnd: null, + topDragEnter: null, + topDragExit: null, + topDragLeave: null, + topDragOver: null, + topDragStart: null, + topDrop: null, + topError: null, + topFocus: null, + topInput: null, + topKeyDown: null, + topKeyPress: null, + topKeyUp: null, + topLoad: null, + topMouseDown: null, + topMouseMove: null, + topMouseOut: null, + topMouseOver: null, + topMouseUp: null, + topPaste: null, + topReset: null, + topScroll: null, + topSelectionChange: null, + topSubmit: null, + topTextInput: null, + topTouchCancel: null, + topTouchEnd: null, + topTouchMove: null, + topTouchStart: null, + topWheel: null + }), + a = { + topLevelTypes: o, + PropagationPhases: r + }; + t.exports = a + }, { + "./keyMirror": 163 + }], + 23: [function(e, t) { + (function(n) { + var r = e("./emptyFunction"), + o = { + listen: function(e, t, n) { + return e.addEventListener ? (e.addEventListener(t, n, !1), { + remove: function() { + e.removeEventListener(t, n, !1) + } + }) : e.attachEvent ? (e.attachEvent("on" + t, n), { + remove: function() { + e.detachEvent("on" + t, n) + } + }) : void 0 + }, + capture: function(e, t, o) { + return e.addEventListener ? (e.addEventListener(t, o, !0), { + remove: function() { + e.removeEventListener(t, o, !0) + } + }) : ("production" !== n.env.NODE_ENV && console.error("Attempted to listen to events during the capture phase on a browser that does not support the capture phase. Your application will not receive some events."), { + remove: r + }) + }, + registerDefault: function() {} + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./emptyFunction": 136, + _process: 2 + }], + 24: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + var e = d && d.traverseTwoPhase && d.traverseEnterLeave; + "production" !== n.env.NODE_ENV ? u(e, "InstanceHandle not injected before use!") : u(e) + } + var o = e("./EventPluginRegistry"), + a = e("./EventPluginUtils"), + i = e("./accumulateInto"), + s = e("./forEachAccumulated"), + u = e("./invariant"), + c = {}, + l = null, + p = function(e) { + if (e) { + var t = a.executeDispatch, + n = o.getPluginModuleForEvent(e); + n && n.executeDispatch && (t = n.executeDispatch), a.executeDispatchesInOrder(e, t), e.isPersistent() || e.constructor.release(e) + } + }, + d = null, + f = { + injection: { + injectMount: a.injection.injectMount, + injectInstanceHandle: function(e) { + d = e, "production" !== n.env.NODE_ENV && r() + }, + getInstanceHandle: function() { + return "production" !== n.env.NODE_ENV && r(), d + }, + injectEventPluginOrder: o.injectEventPluginOrder, + injectEventPluginsByName: o.injectEventPluginsByName + }, + eventNameDispatchConfigs: o.eventNameDispatchConfigs, + registrationNameModules: o.registrationNameModules, + putListener: function(e, t, r) { + "production" !== n.env.NODE_ENV ? u(!r || "function" == typeof r, "Expected %s listener to be a function, instead got type %s", t, typeof r) : u(!r || "function" == typeof r); + var o = c[t] || (c[t] = {}); + o[e] = r + }, + getListener: function(e, t) { + var n = c[t]; + return n && n[e] + }, + deleteListener: function(e, t) { + var n = c[t]; + n && delete n[e] + }, + deleteAllListeners: function(e) { + for (var t in c) delete c[t][e] + }, + extractEvents: function(e, t, n, r) { + for (var a, s = o.plugins, u = 0, c = s.length; c > u; u++) { + var l = s[u]; + if (l) { + var p = l.extractEvents(e, t, n, r); + p && (a = i(a, p)) + } + } + return a + }, + enqueueEvents: function(e) { + e && (l = i(l, e)) + }, + processEventQueue: function() { + var e = l; + l = null, s(e, p), "production" !== n.env.NODE_ENV ? u(!l, "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented.") : u(!l) + }, + __purge: function() { + c = {} + }, + __getListenerBank: function() { + return c + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./EventPluginRegistry": 25, + "./EventPluginUtils": 26, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + "./invariant": 157, + _process: 2 + }], + 25: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + if (s) + for (var e in u) { + var t = u[e], + r = s.indexOf(e); + if ("production" !== n.env.NODE_ENV ? i(r > -1, "EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.", e) : i(r > -1), !c.plugins[r]) { + "production" !== n.env.NODE_ENV ? i(t.extractEvents, "EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.", e) : i(t.extractEvents), c.plugins[r] = t; + var a = t.eventTypes; + for (var l in a) "production" !== n.env.NODE_ENV ? i(o(a[l], t, l), "EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.", l, e) : i(o(a[l], t, l)) + } + } + } + + function o(e, t, r) { + "production" !== n.env.NODE_ENV ? i(!c.eventNameDispatchConfigs.hasOwnProperty(r), "EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.", r) : i(!c.eventNameDispatchConfigs.hasOwnProperty(r)), c.eventNameDispatchConfigs[r] = e; + var o = e.phasedRegistrationNames; + if (o) { + for (var s in o) + if (o.hasOwnProperty(s)) { + var u = o[s]; + a(u, t, r) + } return !0 + } + return e.registrationName ? (a(e.registrationName, t, r), !0) : !1 + } + + function a(e, t, r) { + "production" !== n.env.NODE_ENV ? i(!c.registrationNameModules[e], "EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.", e) : i(!c.registrationNameModules[e]), c.registrationNameModules[e] = t, c.registrationNameDependencies[e] = t.eventTypes[r].dependencies + } + var i = e("./invariant"), + s = null, + u = {}, + c = { + plugins: [], + eventNameDispatchConfigs: {}, + registrationNameModules: {}, + registrationNameDependencies: {}, + injectEventPluginOrder: function(e) { + "production" !== n.env.NODE_ENV ? i(!s, "EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React.") : i(!s), s = Array.prototype.slice.call(e), r() + }, + injectEventPluginsByName: function(e) { + var t = !1; + for (var o in e) + if (e.hasOwnProperty(o)) { + var a = e[o]; + u.hasOwnProperty(o) && u[o] === a || ("production" !== n.env.NODE_ENV ? i(!u[o], "EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.", o) : i(!u[o]), u[o] = a, t = !0) + } t && r() + }, + getPluginModuleForEvent: function(e) { + var t = e.dispatchConfig; + if (t.registrationName) return c.registrationNameModules[t.registrationName] || null; + for (var n in t.phasedRegistrationNames) + if (t.phasedRegistrationNames.hasOwnProperty(n)) { + var r = c.registrationNameModules[t.phasedRegistrationNames[n]]; + if (r) return r + } return null + }, + _resetEventPlugins: function() { + s = null; + for (var e in u) u.hasOwnProperty(e) && delete u[e]; + c.plugins.length = 0; + var t = c.eventNameDispatchConfigs; + for (var n in t) t.hasOwnProperty(n) && delete t[n]; + var r = c.registrationNameModules; + for (var o in r) r.hasOwnProperty(o) && delete r[o] + } + }; + t.exports = c + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 26: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return e === y.topMouseUp || e === y.topTouchEnd || e === y.topTouchCancel + } + + function o(e) { + return e === y.topMouseMove || e === y.topTouchMove + } + + function a(e) { + return e === y.topMouseDown || e === y.topTouchStart + } + + function i(e, t) { + var r = e._dispatchListeners, + o = e._dispatchIDs; + if ("production" !== n.env.NODE_ENV && f(e), Array.isArray(r)) + for (var a = 0; a < r.length && !e.isPropagationStopped(); a++) t(e, r[a], o[a]); + else r && t(e, r, o) + } + + function s(e, t, n) { + e.currentTarget = m.Mount.getNode(n); + var r = t(e, n); + return e.currentTarget = null, r + } + + function u(e, t) { + i(e, t), e._dispatchListeners = null, e._dispatchIDs = null + } + + function c(e) { + var t = e._dispatchListeners, + r = e._dispatchIDs; + if ("production" !== n.env.NODE_ENV && f(e), Array.isArray(t)) { + for (var o = 0; o < t.length && !e.isPropagationStopped(); o++) + if (t[o](e, r[o])) return r[o] + } else if (t && t(e, r)) return r; + return null + } + + function l(e) { + var t = c(e); + return e._dispatchIDs = null, e._dispatchListeners = null, t + } + + function p(e) { + "production" !== n.env.NODE_ENV && f(e); + var t = e._dispatchListeners, + r = e._dispatchIDs; + "production" !== n.env.NODE_ENV ? v(!Array.isArray(t), "executeDirectDispatch(...): Invalid `event`.") : v(!Array.isArray(t)); + var o = t ? t(e, r) : null; + return e._dispatchListeners = null, e._dispatchIDs = null, o + } + + function d(e) { + return !!e._dispatchListeners + } + var f, h = e("./EventConstants"), + v = e("./invariant"), + m = { + Mount: null, + injectMount: function(e) { + m.Mount = e, "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? v(e && e.getNode, "EventPluginUtils.injection.injectMount(...): Injected Mount module is missing getNode.") : v(e && e.getNode)) + } + }, + y = h.topLevelTypes; + "production" !== n.env.NODE_ENV && (f = function(e) { + var t = e._dispatchListeners, + r = e._dispatchIDs, + o = Array.isArray(t), + a = Array.isArray(r), + i = a ? r.length : r ? 1 : 0, + s = o ? t.length : t ? 1 : 0; + "production" !== n.env.NODE_ENV ? v(a === o && i === s, "EventPluginUtils: Invalid `event`.") : v(a === o && i === s) + }); + var g = { + isEndish: r, + isMoveish: o, + isStartish: a, + executeDirectDispatch: p, + executeDispatch: s, + executeDispatchesInOrder: u, + executeDispatchesInOrderStopAtTrue: l, + hasDispatches: d, + injection: m, + useTouchEvents: !1 + }; + t.exports = g + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./invariant": 157, + _process: 2 + }], + 27: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t, n) { + var r = t.dispatchConfig.phasedRegistrationNames[n]; + return m(e, r) + } + + function o(e, t, o) { + if ("production" !== n.env.NODE_ENV && !e) throw new Error("Dispatching id must not be null"); + var a = t ? v.bubbled : v.captured, + i = r(e, o, a); + i && (o._dispatchListeners = f(o._dispatchListeners, i), o._dispatchIDs = f(o._dispatchIDs, e)) + } + + function a(e) { + e && e.dispatchConfig.phasedRegistrationNames && d.injection.getInstanceHandle().traverseTwoPhase(e.dispatchMarker, o, e) + } + + function i(e, t, n) { + if (n && n.dispatchConfig.registrationName) { + var r = n.dispatchConfig.registrationName, + o = m(e, r); + o && (n._dispatchListeners = f(n._dispatchListeners, o), n._dispatchIDs = f(n._dispatchIDs, e)) + } + } + + function s(e) { + e && e.dispatchConfig.registrationName && i(e.dispatchMarker, null, e) + } + + function u(e) { + h(e, a) + } + + function c(e, t, n, r) { + d.injection.getInstanceHandle().traverseEnterLeave(n, r, i, e, t) + } + + function l(e) { + h(e, s) + } + var p = e("./EventConstants"), + d = e("./EventPluginHub"), + f = e("./accumulateInto"), + h = e("./forEachAccumulated"), + v = p.PropagationPhases, + m = d.getListener, + y = { + accumulateTwoPhaseDispatches: u, + accumulateDirectDispatches: l, + accumulateEnterLeaveDispatches: c + }; + t.exports = y + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + _process: 2 + }], + 28: [function(e, t) { + "use strict"; + var n = !("undefined" == typeof window || !window.document || !window.document.createElement), + r = { + canUseDOM: n, + canUseWorkers: "undefined" != typeof Worker, + canUseEventListeners: n && !(!window.addEventListener && !window.attachEvent), + canUseViewport: n && !!window.screen, + isInWorker: !n + }; + t.exports = r + }, {}], + 29: [function(e, t) { + "use strict"; + + function n(e) { + this._root = e, this._startText = this.getText(), this._fallbackText = null + } + var r = e("./PooledClass"), + o = e("./Object.assign"), + a = e("./getTextContentAccessor"); + o(n.prototype, { + getText: function() { + return "value" in this._root ? this._root.value : this._root[a()] + }, + getData: function() { + if (this._fallbackText) return this._fallbackText; + var e, t, n = this._startText, + r = n.length, + o = this.getText(), + a = o.length; + for (e = 0; r > e && n[e] === o[e]; e++); + var i = r - e; + for (t = 1; i >= t && n[r - t] === o[a - t]; t++); + var s = t > 1 ? 1 - t : void 0; + return this._fallbackText = o.slice(e, s), this._fallbackText + } + }), r.addPoolingTo(n), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./getTextContentAccessor": 152 + }], + 30: [function(e, t) { + "use strict"; + var n, r = e("./DOMProperty"), + o = e("./ExecutionEnvironment"), + a = r.injection.MUST_USE_ATTRIBUTE, + i = r.injection.MUST_USE_PROPERTY, + s = r.injection.HAS_BOOLEAN_VALUE, + u = r.injection.HAS_SIDE_EFFECTS, + c = r.injection.HAS_NUMERIC_VALUE, + l = r.injection.HAS_POSITIVE_NUMERIC_VALUE, + p = r.injection.HAS_OVERLOADED_BOOLEAN_VALUE; + if (o.canUseDOM) { + var d = document.implementation; + n = d && d.hasFeature && d.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") + } + var f = { + isCustomAttribute: RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/), + Properties: { + accept: null, + acceptCharset: null, + accessKey: null, + action: null, + allowFullScreen: a | s, + allowTransparency: a, + alt: null, + async: s, + autoComplete: null, + autoPlay: s, + cellPadding: null, + cellSpacing: null, + charSet: a, + checked: i | s, + classID: a, + className: n ? a : i, + cols: a | l, + colSpan: null, + content: null, + contentEditable: null, + contextMenu: a, + controls: i | s, + coords: null, + crossOrigin: null, + data: null, + dateTime: a, + defer: s, + dir: null, + disabled: a | s, + download: p, + draggable: null, + encType: null, + form: a, + formAction: a, + formEncType: a, + formMethod: a, + formNoValidate: s, + formTarget: a, + frameBorder: a, + headers: null, + height: a, + hidden: a | s, + href: null, + hrefLang: null, + htmlFor: null, + httpEquiv: null, + icon: null, + id: i, + label: null, + lang: null, + list: a, + loop: i | s, + manifest: a, + marginHeight: null, + marginWidth: null, + max: null, + maxLength: a, + media: a, + mediaGroup: null, + method: null, + min: null, + multiple: i | s, + muted: i | s, + name: null, + noValidate: s, + open: s, + pattern: null, + placeholder: null, + poster: null, + preload: null, + radioGroup: null, + readOnly: i | s, + rel: null, + required: s, + role: a, + rows: a | l, + rowSpan: null, + sandbox: null, + scope: null, + scrolling: null, + seamless: a | s, + selected: i | s, + shape: null, + size: a | l, + sizes: a, + span: l, + spellCheck: null, + src: null, + srcDoc: i, + srcSet: a, + start: c, + step: null, + style: null, + tabIndex: null, + target: null, + title: null, + type: null, + useMap: null, + value: i | u, + width: a, + wmode: a, + autoCapitalize: null, + autoCorrect: null, + itemProp: a, + itemScope: a | s, + itemType: a, + itemID: a, + itemRef: a, + property: null + }, + DOMAttributeNames: { + acceptCharset: "accept-charset", + className: "class", + htmlFor: "for", + httpEquiv: "http-equiv" + }, + DOMPropertyNames: { + autoCapitalize: "autocapitalize", + autoComplete: "autocomplete", + autoCorrect: "autocorrect", + autoFocus: "autofocus", + autoPlay: "autoplay", + encType: "encoding", + hrefLang: "hreflang", + radioGroup: "radiogroup", + spellCheck: "spellcheck", + srcDoc: "srcdoc", + srcSet: "srcset" + } + }; + t.exports = f + }, { + "./DOMProperty": 17, + "./ExecutionEnvironment": 28 + }], + 31: [function(e, t) { + "use strict"; + var n = e("./ReactLink"), + r = e("./ReactStateSetters"), + o = { + linkState: function(e) { + return new n(this.state[e], r.createStateKeySetter(this, e)) + } + }; + t.exports = o + }, { + "./ReactLink": 81, + "./ReactStateSetters": 100 + }], + 32: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + "production" !== n.env.NODE_ENV ? c(null == e.props.checkedLink || null == e.props.valueLink, "Cannot provide a checkedLink and a valueLink. If you want to use checkedLink, you probably don't want to use valueLink and vice versa.") : c(null == e.props.checkedLink || null == e.props.valueLink) + } + + function o(e) { + r(e), "production" !== n.env.NODE_ENV ? c(null == e.props.value && null == e.props.onChange, "Cannot provide a valueLink and a value or onChange event. If you want to use value or onChange, you probably don't want to use valueLink.") : c(null == e.props.value && null == e.props.onChange) + } + + function a(e) { + r(e), "production" !== n.env.NODE_ENV ? c(null == e.props.checked && null == e.props.onChange, "Cannot provide a checkedLink and a checked property or onChange event. If you want to use checked or onChange, you probably don't want to use checkedLink") : c(null == e.props.checked && null == e.props.onChange) + } + + function i(e) { + this.props.valueLink.requestChange(e.target.value) + } + + function s(e) { + this.props.checkedLink.requestChange(e.target.checked) + } + var u = e("./ReactPropTypes"), + c = e("./invariant"), + l = { + button: !0, + checkbox: !0, + image: !0, + hidden: !0, + radio: !0, + reset: !0, + submit: !0 + }, + p = { + Mixin: { + propTypes: { + value: function(e, t) { + return !e[t] || l[e.type] || e.onChange || e.readOnly || e.disabled ? null : new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.") + }, + checked: function(e, t) { + return !e[t] || e.onChange || e.readOnly || e.disabled ? null : new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.") + }, + onChange: u.func + } + }, + getValue: function(e) { + return e.props.valueLink ? (o(e), e.props.valueLink.value) : e.props.value + }, + getChecked: function(e) { + return e.props.checkedLink ? (a(e), e.props.checkedLink.value) : e.props.checked + }, + getOnChange: function(e) { + return e.props.valueLink ? (o(e), i) : e.props.checkedLink ? (a(e), s) : e.props.onChange + } + }; + t.exports = p + }).call(this, e("_process")) + }, { + "./ReactPropTypes": 92, + "./invariant": 157, + _process: 2 + }], + 33: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e.remove() + } + var o = e("./ReactBrowserEventEmitter"), + a = e("./accumulateInto"), + i = e("./forEachAccumulated"), + s = e("./invariant"), + u = { + trapBubbledEvent: function(e, t) { + "production" !== n.env.NODE_ENV ? s(this.isMounted(), "Must be mounted to trap events") : s(this.isMounted()); + var r = this.getDOMNode(); + "production" !== n.env.NODE_ENV ? s(r, "LocalEventTrapMixin.trapBubbledEvent(...): Requires node to be rendered.") : s(r); + var i = o.trapBubbledEvent(e, t, r); + this._localEventListeners = a(this._localEventListeners, i) + }, + componentWillUnmount: function() { + this._localEventListeners && i(this._localEventListeners, r) + } + }; + t.exports = u + }).call(this, e("_process")) + }, { + "./ReactBrowserEventEmitter": 39, + "./accumulateInto": 125, + "./forEachAccumulated": 142, + "./invariant": 157, + _process: 2 + }], + 34: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./emptyFunction"), + o = n.topLevelTypes, + a = { + eventTypes: null, + extractEvents: function(e, t, n, a) { + if (e === o.topTouchStart) { + var i = a.target; + i && !i.onclick && (i.onclick = r) + } + } + }; + t.exports = a + }, { + "./EventConstants": 22, + "./emptyFunction": 136 + }], + 35: [function(e, t) { + "use strict"; + + function n(e) { + if (null == e) throw new TypeError("Object.assign target cannot be null or undefined"); + for (var t = Object(e), n = Object.prototype.hasOwnProperty, r = 1; r < arguments.length; r++) { + var o = arguments[r]; + if (null != o) { + var a = Object(o); + for (var i in a) n.call(a, i) && (t[i] = a[i]) + } + } + return t + } + t.exports = n + }, {}], + 36: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = function(e) { + var t = this; + if (t.instancePool.length) { + var n = t.instancePool.pop(); + return t.call(n, e), n + } + return new t(e) + }, + a = function(e, t) { + var n = this; + if (n.instancePool.length) { + var r = n.instancePool.pop(); + return n.call(r, e, t), r + } + return new n(e, t) + }, + i = function(e, t, n) { + var r = this; + if (r.instancePool.length) { + var o = r.instancePool.pop(); + return r.call(o, e, t, n), o + } + return new r(e, t, n) + }, + s = function(e, t, n, r, o) { + var a = this; + if (a.instancePool.length) { + var i = a.instancePool.pop(); + return a.call(i, e, t, n, r, o), i + } + return new a(e, t, n, r, o) + }, + u = function(e) { + var t = this; + "production" !== n.env.NODE_ENV ? r(e instanceof t, "Trying to release an instance into a pool of a different type.") : r(e instanceof t), e.destructor && e.destructor(), t.instancePool.length < t.poolSize && t.instancePool.push(e) + }, + c = 10, + l = o, + p = function(e, t) { + var n = e; + return n.instancePool = [], n.getPooled = t || l, n.poolSize || (n.poolSize = c), n.release = u, n + }, + d = { + addPoolingTo: p, + oneArgumentPooler: o, + twoArgumentPooler: a, + threeArgumentPooler: i, + fiveArgumentPooler: s + }; + t.exports = d + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 37: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./EventPluginUtils"), + o = e("./ReactChildren"), + a = e("./ReactComponent"), + i = e("./ReactClass"), + s = e("./ReactContext"), + u = e("./ReactCurrentOwner"), + c = e("./ReactElement"), + l = e("./ReactElementValidator"), + p = e("./ReactDOM"), + d = e("./ReactDOMTextComponent"), + f = e("./ReactDefaultInjection"), + h = e("./ReactInstanceHandles"), + v = e("./ReactMount"), + m = e("./ReactPerf"), + y = e("./ReactPropTypes"), + g = e("./ReactReconciler"), + E = e("./ReactServerRendering"), + C = e("./Object.assign"), + b = e("./findDOMNode"), + _ = e("./onlyChild"); + f.inject(); + var N = c.createElement, + O = c.createFactory, + R = c.cloneElement; + "production" !== n.env.NODE_ENV && (N = l.createElement, O = l.createFactory, R = l.cloneElement); + var D = m.measure("React", "render", v.render), + w = { + Children: { + map: o.map, + forEach: o.forEach, + count: o.count, + only: _ + }, + Component: a, + DOM: p, + PropTypes: y, + initializeTouchEvents: function(e) { + r.useTouchEvents = e + }, + createClass: i.createClass, + createElement: N, + cloneElement: R, + createFactory: O, + createMixin: function(e) { + return e + }, + constructAndRenderComponent: v.constructAndRenderComponent, + constructAndRenderComponentByID: v.constructAndRenderComponentByID, + findDOMNode: b, + render: D, + renderToString: E.renderToString, + renderToStaticMarkup: E.renderToStaticMarkup, + unmountComponentAtNode: v.unmountComponentAtNode, + isValidElement: c.isValidElement, + withContext: s.withContext, + __spread: C + }; + if ("undefined" != typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" == typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject && __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ + CurrentOwner: u, + InstanceHandles: h, + Mount: v, + Reconciler: g, + TextComponent: d + }), "production" !== n.env.NODE_ENV) { + var M = e("./ExecutionEnvironment"); + if (M.canUseDOM && window.top === window.self) { + navigator.userAgent.indexOf("Chrome") > -1 && "undefined" == typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && console.debug("Download the React DevTools for a better development experience: http://fb.me/react-devtools"); + for (var x = [Array.isArray, Array.prototype.every, Array.prototype.forEach, Array.prototype.indexOf, Array.prototype.map, Date.now, Function.prototype.bind, Object.keys, String.prototype.split, String.prototype.trim, Object.create, Object.freeze], T = 0; T < x.length; T++) + if (!x[T]) { + console.error("One or more ES5 shim/shams expected by React are not available: http://fb.me/react-warning-polyfills"); + break + } + } + } + w.version = "0.13.1", t.exports = w + }).call(this, e("_process")) + }, { + "./EventPluginUtils": 26, + "./ExecutionEnvironment": 28, + "./Object.assign": 35, + "./ReactChildren": 43, + "./ReactClass": 44, + "./ReactComponent": 45, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./ReactDOM": 52, + "./ReactDOMTextComponent": 63, + "./ReactDefaultInjection": 66, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactPerf": 88, + "./ReactPropTypes": 92, + "./ReactReconciler": 95, + "./ReactServerRendering": 98, + "./findDOMNode": 139, + "./onlyChild": 167, + _process: 2 + }], + 38: [function(e, t) { + "use strict"; + var n = e("./findDOMNode"), + r = { + getDOMNode: function() { + return n(this) + } + }; + t.exports = r + }, { + "./findDOMNode": 139 + }], + 39: [function(e, t) { + "use strict"; + + function n(e) { + return Object.prototype.hasOwnProperty.call(e, h) || (e[h] = d++, l[e[h]] = {}), l[e[h]] + } + var r = e("./EventConstants"), + o = e("./EventPluginHub"), + a = e("./EventPluginRegistry"), + i = e("./ReactEventEmitterMixin"), + s = e("./ViewportMetrics"), + u = e("./Object.assign"), + c = e("./isEventSupported"), + l = {}, + p = !1, + d = 0, + f = { + topBlur: "blur", + topChange: "change", + topClick: "click", + topCompositionEnd: "compositionend", + topCompositionStart: "compositionstart", + topCompositionUpdate: "compositionupdate", + topContextMenu: "contextmenu", + topCopy: "copy", + topCut: "cut", + topDoubleClick: "dblclick", + topDrag: "drag", + topDragEnd: "dragend", + topDragEnter: "dragenter", + topDragExit: "dragexit", + topDragLeave: "dragleave", + topDragOver: "dragover", + topDragStart: "dragstart", + topDrop: "drop", + topFocus: "focus", + topInput: "input", + topKeyDown: "keydown", + topKeyPress: "keypress", + topKeyUp: "keyup", + topMouseDown: "mousedown", + topMouseMove: "mousemove", + topMouseOut: "mouseout", + topMouseOver: "mouseover", + topMouseUp: "mouseup", + topPaste: "paste", + topScroll: "scroll", + topSelectionChange: "selectionchange", + topTextInput: "textInput", + topTouchCancel: "touchcancel", + topTouchEnd: "touchend", + topTouchMove: "touchmove", + topTouchStart: "touchstart", + topWheel: "wheel" + }, + h = "_reactListenersID" + String(Math.random()).slice(2), + v = u({}, i, { + ReactEventListener: null, + injection: { + injectReactEventListener: function(e) { + e.setHandleTopLevel(v.handleTopLevel), v.ReactEventListener = e + } + }, + setEnabled: function(e) { + v.ReactEventListener && v.ReactEventListener.setEnabled(e) + }, + isEnabled: function() { + return !(!v.ReactEventListener || !v.ReactEventListener.isEnabled()) + }, + listenTo: function(e, t) { + for (var o = t, i = n(o), s = a.registrationNameDependencies[e], u = r.topLevelTypes, l = 0, p = s.length; p > l; l++) { + var d = s[l]; + i.hasOwnProperty(d) && i[d] || (d === u.topWheel ? c("wheel") ? v.ReactEventListener.trapBubbledEvent(u.topWheel, "wheel", o) : c("mousewheel") ? v.ReactEventListener.trapBubbledEvent(u.topWheel, "mousewheel", o) : v.ReactEventListener.trapBubbledEvent(u.topWheel, "DOMMouseScroll", o) : d === u.topScroll ? c("scroll", !0) ? v.ReactEventListener.trapCapturedEvent(u.topScroll, "scroll", o) : v.ReactEventListener.trapBubbledEvent(u.topScroll, "scroll", v.ReactEventListener.WINDOW_HANDLE) : d === u.topFocus || d === u.topBlur ? (c("focus", !0) ? (v.ReactEventListener.trapCapturedEvent(u.topFocus, "focus", o), v.ReactEventListener.trapCapturedEvent(u.topBlur, "blur", o)) : c("focusin") && (v.ReactEventListener.trapBubbledEvent(u.topFocus, "focusin", o), v.ReactEventListener.trapBubbledEvent(u.topBlur, "focusout", o)), i[u.topBlur] = !0, i[u.topFocus] = !0) : f.hasOwnProperty(d) && v.ReactEventListener.trapBubbledEvent(d, f[d], o), i[d] = !0) + } + }, + trapBubbledEvent: function(e, t, n) { + return v.ReactEventListener.trapBubbledEvent(e, t, n) + }, + trapCapturedEvent: function(e, t, n) { + return v.ReactEventListener.trapCapturedEvent(e, t, n) + }, + ensureScrollValueMonitoring: function() { + if (!p) { + var e = s.refreshScrollValues; + v.ReactEventListener.monitorScrollValue(e), p = !0 + } + }, + eventNameDispatchConfigs: o.eventNameDispatchConfigs, + registrationNameModules: o.registrationNameModules, + putListener: o.putListener, + getListener: o.getListener, + deleteListener: o.deleteListener, + deleteAllListeners: o.deleteAllListeners + }); + t.exports = v + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPluginRegistry": 25, + "./Object.assign": 35, + "./ReactEventEmitterMixin": 73, + "./ViewportMetrics": 124, + "./isEventSupported": 158 + }], + 40: [function(e, t) { + "use strict"; + var n = e("./React"), + r = e("./Object.assign"), + o = n.createFactory(e("./ReactTransitionGroup")), + a = n.createFactory(e("./ReactCSSTransitionGroupChild")), + i = n.createClass({ + displayName: "ReactCSSTransitionGroup", + propTypes: { + transitionName: n.PropTypes.string.isRequired, + transitionAppear: n.PropTypes.bool, + transitionEnter: n.PropTypes.bool, + transitionLeave: n.PropTypes.bool + }, + getDefaultProps: function() { + return { + transitionAppear: !1, + transitionEnter: !0, + transitionLeave: !0 + } + }, + _wrapChild: function(e) { + return a({ + name: this.props.transitionName, + appear: this.props.transitionAppear, + enter: this.props.transitionEnter, + leave: this.props.transitionLeave + }, e) + }, + render: function() { + return o(r({}, this.props, { + childFactory: this._wrapChild + })) + } + }); + t.exports = i + }, { + "./Object.assign": 35, + "./React": 37, + "./ReactCSSTransitionGroupChild": 41, + "./ReactTransitionGroup": 104 + }], + 41: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./React"), + o = e("./CSSCore"), + a = e("./ReactTransitionEvents"), + i = e("./onlyChild"), + s = e("./warning"), + u = 17, + c = 5e3, + l = null; + "production" !== n.env.NODE_ENV && (l = function() { + "production" !== n.env.NODE_ENV ? s(!1, "transition(): tried to perform an animation without an animationend or transitionend event after timeout (%sms). You should either disable this transition in JS or add a CSS animation/transition.", c) : null + }); + var p = r.createClass({ + displayName: "ReactCSSTransitionGroupChild", + transition: function(e, t) { + var r = this.getDOMNode(), + i = this.props.name + "-" + e, + s = i + "-active", + u = null, + p = function(e) { + e && e.target !== r || ("production" !== n.env.NODE_ENV && clearTimeout(u), o.removeClass(r, i), o.removeClass(r, s), a.removeEndEventListener(r, p), t && t()) + }; + a.addEndEventListener(r, p), o.addClass(r, i), this.queueClass(s), "production" !== n.env.NODE_ENV && (u = setTimeout(l, c)) + }, + queueClass: function(e) { + this.classNameQueue.push(e), this.timeout || (this.timeout = setTimeout(this.flushClassNameQueue, u)) + }, + flushClassNameQueue: function() { + this.isMounted() && this.classNameQueue.forEach(o.addClass.bind(o, this.getDOMNode())), this.classNameQueue.length = 0, this.timeout = null + }, + componentWillMount: function() { + this.classNameQueue = [] + }, + componentWillUnmount: function() { + this.timeout && clearTimeout(this.timeout) + }, + componentWillAppear: function(e) { + this.props.appear ? this.transition("appear", e) : e() + }, + componentWillEnter: function(e) { + this.props.enter ? this.transition("enter", e) : e() + }, + componentWillLeave: function(e) { + this.props.leave ? this.transition("leave", e) : e() + }, + render: function() { + return i(this.props.children) + } + }); + t.exports = p + }).call(this, e("_process")) + }, { + "./CSSCore": 10, + "./React": 37, + "./ReactTransitionEvents": 103, + "./onlyChild": 167, + "./warning": 178, + _process: 2 + }], + 42: [function(e, t) { + "use strict"; + var n = e("./ReactReconciler"), + r = e("./flattenChildren"), + o = e("./instantiateReactComponent"), + a = e("./shouldUpdateReactComponent"), + i = { + instantiateChildren: function(e) { + var t = r(e); + for (var n in t) + if (t.hasOwnProperty(n)) { + var a = t[n], + i = o(a, null); + t[n] = i + } return t + }, + updateChildren: function(e, t, i, s) { + var u = r(t); + if (!u && !e) return null; + var c; + for (c in u) + if (u.hasOwnProperty(c)) { + var l = e && e[c], + p = l && l._currentElement, + d = u[c]; + if (a(p, d)) n.receiveComponent(l, d, i, s), u[c] = l; + else { + l && n.unmountComponent(l, c); + var f = o(d, null); + u[c] = f + } + } for (c in e) !e.hasOwnProperty(c) || u && u.hasOwnProperty(c) || n.unmountComponent(e[c]); + return u + }, + unmountChildren: function(e) { + for (var t in e) { + var r = e[t]; + n.unmountComponent(r) + } + } + }; + t.exports = i + }, { + "./ReactReconciler": 95, + "./flattenChildren": 140, + "./instantiateReactComponent": 156, + "./shouldUpdateReactComponent": 174 + }], + 43: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + this.forEachFunction = e, this.forEachContext = t + } + + function o(e, t, n, r) { + var o = e; + o.forEachFunction.call(o.forEachContext, t, r) + } + + function a(e, t, n) { + if (null == e) return e; + var a = r.getPooled(t, n); + f(e, o, a), r.release(a) + } + + function i(e, t, n) { + this.mapResult = e, this.mapFunction = t, this.mapContext = n + } + + function s(e, t, r, o) { + var a = e, + i = a.mapResult, + s = !i.hasOwnProperty(r); + if ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? h(s, "ReactChildren.map(...): Encountered two children with the same key, `%s`. Child keys must be unique; when two children share a key, only the first child will be used.", r) : null), s) { + var u = a.mapFunction.call(a.mapContext, t, o); + i[r] = u + } + } + + function u(e, t, n) { + if (null == e) return e; + var r = {}, + o = i.getPooled(r, t, n); + return f(e, s, o), i.release(o), d.create(r) + } + + function c() { + return null + } + + function l(e) { + return f(e, c, null) + } + var p = e("./PooledClass"), + d = e("./ReactFragment"), + f = e("./traverseAllChildren"), + h = e("./warning"), + v = p.twoArgumentPooler, + m = p.threeArgumentPooler; + p.addPoolingTo(r, v), p.addPoolingTo(i, m); + var y = { + forEach: a, + map: u, + count: l + }; + t.exports = y + }).call(this, e("_process")) + }, { + "./PooledClass": 36, + "./ReactFragment": 75, + "./traverseAllChildren": 176, + "./warning": 178, + _process: 2 + }], + 44: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t, r) { + for (var o in t) t.hasOwnProperty(o) && ("production" !== n.env.NODE_ENV ? R("function" == typeof t[o], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", e.displayName || "ReactClass", E[r], o) : null) + } + + function o(e, t) { + var r = x.hasOwnProperty(t) ? x[t] : null; + I.hasOwnProperty(t) && ("production" !== n.env.NODE_ENV ? _(r === w.OVERRIDE_BASE, "ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.", t) : _(r === w.OVERRIDE_BASE)), e.hasOwnProperty(t) && ("production" !== n.env.NODE_ENV ? _(r === w.DEFINE_MANY || r === w.DEFINE_MANY_MERGED, "ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.", t) : _(r === w.DEFINE_MANY || r === w.DEFINE_MANY_MERGED)) + } + + function a(e, t) { + if (t) { + "production" !== n.env.NODE_ENV ? _("function" != typeof t, "ReactClass: You're attempting to use a component class as a mixin. Instead, just use a regular object.") : _("function" != typeof t), "production" !== n.env.NODE_ENV ? _(!h.isValidElement(t), "ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.") : _(!h.isValidElement(t)); + var r = e.prototype; + t.hasOwnProperty(D) && T.mixins(e, t.mixins); + for (var a in t) + if (t.hasOwnProperty(a) && a !== D) { + var i = t[a]; + if (o(r, a), T.hasOwnProperty(a)) T[a](e, i); + else { + var s = x.hasOwnProperty(a), + l = r.hasOwnProperty(a), + p = i && i.__reactDontBind, + d = "function" == typeof i, + f = d && !s && !l && !p; + if (f) r.__reactAutoBindMap || (r.__reactAutoBindMap = {}), r.__reactAutoBindMap[a] = i, r[a] = i; + else if (l) { + var v = x[a]; + "production" !== n.env.NODE_ENV ? _(s && (v === w.DEFINE_MANY_MERGED || v === w.DEFINE_MANY), "ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.", v, a) : _(s && (v === w.DEFINE_MANY_MERGED || v === w.DEFINE_MANY)), v === w.DEFINE_MANY_MERGED ? r[a] = u(r[a], i) : v === w.DEFINE_MANY && (r[a] = c(r[a], i)) + } else r[a] = i, "production" !== n.env.NODE_ENV && "function" == typeof i && t.displayName && (r[a].displayName = t.displayName + "_" + a) + } + } + } + } + + function i(e, t) { + if (t) + for (var r in t) { + var o = t[r]; + if (t.hasOwnProperty(r)) { + var a = r in T; + "production" !== n.env.NODE_ENV ? _(!a, 'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\'t be on the "statics" key. Define it as an instance property instead; it will still be accessible on the constructor.', r) : _(!a); + var i = r in e; + "production" !== n.env.NODE_ENV ? _(!i, "ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.", r) : _(!i), e[r] = o + } + } + } + + function s(e, t) { + "production" !== n.env.NODE_ENV ? _(e && t && "object" == typeof e && "object" == typeof t, "mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.") : _(e && t && "object" == typeof e && "object" == typeof t); + for (var r in t) t.hasOwnProperty(r) && ("production" !== n.env.NODE_ENV ? _(void 0 === e[r], "mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.", r) : _(void 0 === e[r]), e[r] = t[r]); + return e + } + + function u(e, t) { + return function() { + var n = e.apply(this, arguments), + r = t.apply(this, arguments); + if (null == n) return r; + if (null == r) return n; + var o = {}; + return s(o, n), s(o, r), o + } + } + + function c(e, t) { + return function() { + e.apply(this, arguments), t.apply(this, arguments) + } + } + + function l(e, t) { + var r = t.bind(e); + if ("production" !== n.env.NODE_ENV) { + r.__reactBoundContext = e, r.__reactBoundMethod = t, r.__reactBoundArguments = null; + var o = e.constructor.displayName, + a = r.bind; + r.bind = function(i) { + for (var s = [], u = 1, c = arguments.length; c > u; u++) s.push(arguments[u]); + if (i !== e && null !== i) "production" !== n.env.NODE_ENV ? R(!1, "bind(): React component methods may only be bound to the component instance. See %s", o) : null; + else if (!s.length) return "production" !== n.env.NODE_ENV ? R(!1, "bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See %s", o) : null, r; + var l = a.apply(r, arguments); + return l.__reactBoundContext = e, l.__reactBoundMethod = t, l.__reactBoundArguments = s, l + } + } + return r + } + + function p(e) { + for (var t in e.__reactAutoBindMap) + if (e.__reactAutoBindMap.hasOwnProperty(t)) { + var n = e.__reactAutoBindMap[t]; + e[t] = l(e, v.guard(n, e.constructor.displayName + "." + t)) + } + } + var d = e("./ReactComponent"), + f = e("./ReactCurrentOwner"), + h = e("./ReactElement"), + v = e("./ReactErrorUtils"), + m = e("./ReactInstanceMap"), + y = e("./ReactLifeCycle"), + g = e("./ReactPropTypeLocations"), + E = e("./ReactPropTypeLocationNames"), + C = e("./ReactUpdateQueue"), + b = e("./Object.assign"), + _ = e("./invariant"), + N = e("./keyMirror"), + O = e("./keyOf"), + R = e("./warning"), + D = O({ + mixins: null + }), + w = N({ + DEFINE_ONCE: null, + DEFINE_MANY: null, + OVERRIDE_BASE: null, + DEFINE_MANY_MERGED: null + }), + M = [], + x = { + mixins: w.DEFINE_MANY, + statics: w.DEFINE_MANY, + propTypes: w.DEFINE_MANY, + contextTypes: w.DEFINE_MANY, + childContextTypes: w.DEFINE_MANY, + getDefaultProps: w.DEFINE_MANY_MERGED, + getInitialState: w.DEFINE_MANY_MERGED, + getChildContext: w.DEFINE_MANY_MERGED, + render: w.DEFINE_ONCE, + componentWillMount: w.DEFINE_MANY, + componentDidMount: w.DEFINE_MANY, + componentWillReceiveProps: w.DEFINE_MANY, + shouldComponentUpdate: w.DEFINE_ONCE, + componentWillUpdate: w.DEFINE_MANY, + componentDidUpdate: w.DEFINE_MANY, + componentWillUnmount: w.DEFINE_MANY, + updateComponent: w.OVERRIDE_BASE + }, + T = { + displayName: function(e, t) { + e.displayName = t + }, + mixins: function(e, t) { + if (t) + for (var n = 0; n < t.length; n++) a(e, t[n]) + }, + childContextTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.childContext), e.childContextTypes = b({}, e.childContextTypes, t) + }, + contextTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.context), e.contextTypes = b({}, e.contextTypes, t) + }, + getDefaultProps: function(e, t) { + e.getDefaultProps = e.getDefaultProps ? u(e.getDefaultProps, t) : t; + + }, + propTypes: function(e, t) { + "production" !== n.env.NODE_ENV && r(e, t, g.prop), e.propTypes = b({}, e.propTypes, t) + }, + statics: function(e, t) { + i(e, t) + } + }, + P = { + enumerable: !1, + get: function() { + var e = this.displayName || this.name || "Component"; + return "production" !== n.env.NODE_ENV ? R(!1, "%s.type is deprecated. Use %s directly to access the class.", e, e) : null, Object.defineProperty(this, "type", { + value: this + }), this + } + }, + I = { + replaceState: function(e, t) { + C.enqueueReplaceState(this, e), t && C.enqueueCallback(this, t) + }, + isMounted: function() { + if ("production" !== n.env.NODE_ENV) { + var e = f.current; + null !== e && ("production" !== n.env.NODE_ENV ? R(e._warnedAboutRefsInRender, "%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.", e.getName() || "A component") : null, e._warnedAboutRefsInRender = !0) + } + var t = m.get(this); + return t && t !== y.currentlyMountingInstance + }, + setProps: function(e, t) { + C.enqueueSetProps(this, e), t && C.enqueueCallback(this, t) + }, + replaceProps: function(e, t) { + C.enqueueReplaceProps(this, e), t && C.enqueueCallback(this, t) + } + }, + S = function() {}; + b(S.prototype, d.prototype, I); + var k = { + createClass: function(e) { + var t = function(e, r) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? R(this instanceof t, "Something is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory") : null), this.__reactAutoBindMap && p(this), this.props = e, this.context = r, this.state = null; + var o = this.getInitialState ? this.getInitialState() : null; + "production" !== n.env.NODE_ENV && "undefined" == typeof o && this.getInitialState._isMockFunction && (o = null), "production" !== n.env.NODE_ENV ? _("object" == typeof o && !Array.isArray(o), "%s.getInitialState(): must return an object or null", t.displayName || "ReactCompositeComponent") : _("object" == typeof o && !Array.isArray(o)), this.state = o + }; + t.prototype = new S, t.prototype.constructor = t, M.forEach(a.bind(null, t)), a(t, e), t.getDefaultProps && (t.defaultProps = t.getDefaultProps()), "production" !== n.env.NODE_ENV && (t.getDefaultProps && (t.getDefaultProps.isReactClassApproved = {}), t.prototype.getInitialState && (t.prototype.getInitialState.isReactClassApproved = {})), "production" !== n.env.NODE_ENV ? _(t.prototype.render, "createClass(...): Class specification must implement a `render` method.") : _(t.prototype.render), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? R(!t.prototype.componentShouldUpdate, "%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.", e.displayName || "A component") : null); + for (var r in x) t.prototype[r] || (t.prototype[r] = null); + if (t.type = t, "production" !== n.env.NODE_ENV) try { + Object.defineProperty(t, "type", P) + } catch (o) {} + return t + }, + injection: { + injectMixin: function(e) { + M.push(e) + } + } + }; + t.exports = k + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactComponent": 45, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactErrorUtils": 72, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./ReactUpdateQueue": 105, + "./invariant": 157, + "./keyMirror": 163, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 45: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + this.props = e, this.context = t + } + var o = e("./ReactUpdateQueue"), + a = e("./invariant"), + i = e("./warning"); + if (r.prototype.setState = function(e, t) { + "production" !== n.env.NODE_ENV ? a("object" == typeof e || "function" == typeof e || null == e, "setState(...): takes an object of state variables to update or a function which returns an object of state variables.") : a("object" == typeof e || "function" == typeof e || null == e), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? i(null != e, "setState(...): You passed an undefined or null state object; instead, use forceUpdate().") : null), o.enqueueSetState(this, e), t && o.enqueueCallback(this, t) + }, r.prototype.forceUpdate = function(e) { + o.enqueueForceUpdate(this), e && o.enqueueCallback(this, e) + }, "production" !== n.env.NODE_ENV) { + var s = { + getDOMNode: "getDOMNode", + isMounted: "isMounted", + replaceProps: "replaceProps", + replaceState: "replaceState", + setProps: "setProps" + }, + u = function(e, t) { + try { + Object.defineProperty(r.prototype, e, { + get: function() { + return void("production" !== n.env.NODE_ENV ? i(!1, "%s(...) is deprecated in plain JavaScript React classes.", t) : null) + } + }) + } catch (o) {} + }; + for (var c in s) s.hasOwnProperty(c) && u(c, s[c]) + } + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactUpdateQueue": 105, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 46: [function(e, t) { + "use strict"; + var n = e("./ReactDOMIDOperations"), + r = e("./ReactMount"), + o = { + processChildrenUpdates: n.dangerouslyProcessChildrenUpdates, + replaceNodeWithMarkupByID: n.dangerouslyReplaceNodeWithMarkupByID, + unmountIDFromEnvironment: function(e) { + r.purgeID(e) + } + }; + t.exports = o + }, { + "./ReactDOMIDOperations": 56, + "./ReactMount": 83 + }], + 47: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = !1, + a = { + unmountIDFromEnvironment: null, + replaceNodeWithMarkupByID: null, + processChildrenUpdates: null, + injection: { + injectEnvironment: function(e) { + "production" !== n.env.NODE_ENV ? r(!o, "ReactCompositeComponent: injectEnvironment() can only be called once.") : r(!o), a.unmountIDFromEnvironment = e.unmountIDFromEnvironment, a.replaceNodeWithMarkupByID = e.replaceNodeWithMarkupByID, a.processChildrenUpdates = e.processChildrenUpdates, o = !0 + } + } + }; + t.exports = a + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 48: [function(e, t) { + "use strict"; + var n = e("./shallowEqual"), + r = { + shouldComponentUpdate: function(e, t) { + return !n(this.props, e) || !n(this.state, t) + } + }; + t.exports = r + }, { + "./shallowEqual": 173 + }], + 49: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + var t = e._currentElement._owner || null; + if (t) { + var n = t.getName(); + if (n) return " Check the render method of `" + n + "`." + } + return "" + } + var o = e("./ReactComponentEnvironment"), + a = e("./ReactContext"), + i = e("./ReactCurrentOwner"), + s = e("./ReactElement"), + u = e("./ReactElementValidator"), + c = e("./ReactInstanceMap"), + l = e("./ReactLifeCycle"), + p = e("./ReactNativeComponent"), + d = e("./ReactPerf"), + f = e("./ReactPropTypeLocations"), + h = e("./ReactPropTypeLocationNames"), + v = e("./ReactReconciler"), + m = e("./ReactUpdates"), + y = e("./Object.assign"), + g = e("./emptyObject"), + E = e("./invariant"), + C = e("./shouldUpdateReactComponent"), + b = e("./warning"), + _ = 1, + N = { + construct: function(e) { + this._currentElement = e, this._rootNodeID = null, this._instance = null, this._pendingElement = null, this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1, this._renderedComponent = null, this._context = null, this._mountOrder = 0, this._isTopLevel = !1, this._pendingCallbacks = null + }, + mountComponent: function(e, t, r) { + this._context = r, this._mountOrder = _++, this._rootNodeID = e; + var o = this._processProps(this._currentElement.props), + a = this._processContext(this._currentElement._context), + i = p.getComponentClassForElement(this._currentElement), + s = new i(o, a); + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b(null != s.render, "%s(...): No `render` method found on the returned component instance: you may have forgotten to define `render` in your component or you may have accidentally tried to render an element whose type is a function that isn't a React component.", i.displayName || i.name || "Component") : null), s.props = o, s.context = a, s.refs = g, this._instance = s, c.set(s, this), "production" !== n.env.NODE_ENV && this._warnIfContextsDiffer(this._currentElement._context, r), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b(!s.getInitialState || s.getInitialState.isReactClassApproved, "getInitialState was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b(!s.propTypes, "propTypes was defined as an instance property on %s. Use a static property to define propTypes instead.", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b(!s.contextTypes, "contextTypes was defined as an instance property on %s. Use a static property to define contextTypes instead.", this.getName() || "a component") : null, "production" !== n.env.NODE_ENV ? b("function" != typeof s.componentShouldUpdate, "%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.", this.getName() || "A component") : null); + var u = s.state; + void 0 === u && (s.state = u = null), "production" !== n.env.NODE_ENV ? E("object" == typeof u && !Array.isArray(u), "%s.state: must be set to an object or null", this.getName() || "ReactCompositeComponent") : E("object" == typeof u && !Array.isArray(u)), this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1; + var d, f = l.currentlyMountingInstance; + l.currentlyMountingInstance = this; + try { + s.componentWillMount && (s.componentWillMount(), this._pendingStateQueue && (s.state = this._processPendingState(s.props, s.context))), d = this._renderValidatedComponent() + } finally { + l.currentlyMountingInstance = f + } + this._renderedComponent = this._instantiateReactComponent(d, this._currentElement.type); + var h = v.mountComponent(this._renderedComponent, e, t, this._processChildContext(r)); + return s.componentDidMount && t.getReactMountReady().enqueue(s.componentDidMount, s), h + }, + unmountComponent: function() { + var e = this._instance; + if (e.componentWillUnmount) { + var t = l.currentlyUnmountingInstance; + l.currentlyUnmountingInstance = this; + try { + e.componentWillUnmount() + } finally { + l.currentlyUnmountingInstance = t + } + } + v.unmountComponent(this._renderedComponent), this._renderedComponent = null, this._pendingStateQueue = null, this._pendingReplaceState = !1, this._pendingForceUpdate = !1, this._pendingCallbacks = null, this._pendingElement = null, this._context = null, this._rootNodeID = null, c.remove(e) + }, + _setPropsInternal: function(e, t) { + var n = this._pendingElement || this._currentElement; + this._pendingElement = s.cloneAndReplaceProps(n, y({}, n.props, e)), m.enqueueUpdate(this, t) + }, + _maskContext: function(e) { + var t = null; + if ("string" == typeof this._currentElement.type) return g; + var n = this._currentElement.type.contextTypes; + if (!n) return g; + t = {}; + for (var r in n) t[r] = e[r]; + return t + }, + _processContext: function(e) { + var t = this._maskContext(e); + if ("production" !== n.env.NODE_ENV) { + var r = p.getComponentClassForElement(this._currentElement); + r.contextTypes && this._checkPropTypes(r.contextTypes, t, f.context) + } + return t + }, + _processChildContext: function(e) { + var t = this._instance, + r = t.getChildContext && t.getChildContext(); + if (r) { + "production" !== n.env.NODE_ENV ? E("object" == typeof t.constructor.childContextTypes, "%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().", this.getName() || "ReactCompositeComponent") : E("object" == typeof t.constructor.childContextTypes), "production" !== n.env.NODE_ENV && this._checkPropTypes(t.constructor.childContextTypes, r, f.childContext); + for (var o in r) "production" !== n.env.NODE_ENV ? E(o in t.constructor.childContextTypes, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', this.getName() || "ReactCompositeComponent", o) : E(o in t.constructor.childContextTypes); + return y({}, e, r) + } + return e + }, + _processProps: function(e) { + if ("production" !== n.env.NODE_ENV) { + var t = p.getComponentClassForElement(this._currentElement); + t.propTypes && this._checkPropTypes(t.propTypes, e, f.prop) + } + return e + }, + _checkPropTypes: function(e, t, o) { + var a = this.getName(); + for (var i in e) + if (e.hasOwnProperty(i)) { + var s; + try { + "production" !== n.env.NODE_ENV ? E("function" == typeof e[i], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", a || "React class", h[o], i) : E("function" == typeof e[i]), s = e[i](t, i, a, o) + } catch (u) { + s = u + } + if (s instanceof Error) { + var c = r(this); + o === f.prop ? "production" !== n.env.NODE_ENV ? b(!1, "Failed Composite propType: %s%s", s.message, c) : null : "production" !== n.env.NODE_ENV ? b(!1, "Failed Context Types: %s%s", s.message, c) : null + } + } + }, + receiveComponent: function(e, t, n) { + var r = this._currentElement, + o = this._context; + this._pendingElement = null, this.updateComponent(t, r, e, o, n) + }, + performUpdateIfNecessary: function(e) { + null != this._pendingElement && v.receiveComponent(this, this._pendingElement || this._currentElement, e, this._context), (null !== this._pendingStateQueue || this._pendingForceUpdate) && ("production" !== n.env.NODE_ENV && u.checkAndWarnForMutatedProps(this._currentElement), this.updateComponent(e, this._currentElement, this._currentElement, this._context, this._context)) + }, + _warnIfContextsDiffer: function(e, t) { + e = this._maskContext(e), t = this._maskContext(t); + for (var r = Object.keys(t).sort(), o = this.getName() || "ReactCompositeComponent", a = 0; a < r.length; a++) { + var i = r[a]; + "production" !== n.env.NODE_ENV ? b(e[i] === t[i], "owner-based and parent-based contexts differ (values: `%s` vs `%s`) for key (%s) while mounting %s (see: http://fb.me/react-context-by-parent)", e[i], t[i], i, o) : null + } + }, + updateComponent: function(e, t, r, o, a) { + var i = this._instance, + s = i.context, + u = i.props; + t !== r && (s = this._processContext(r._context), u = this._processProps(r.props), "production" !== n.env.NODE_ENV && null != a && this._warnIfContextsDiffer(r._context, a), i.componentWillReceiveProps && i.componentWillReceiveProps(u, s)); + var c = this._processPendingState(u, s), + l = this._pendingForceUpdate || !i.shouldComponentUpdate || i.shouldComponentUpdate(u, c, s); + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? b("undefined" != typeof l, "%s.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.", this.getName() || "ReactCompositeComponent") : null), l ? (this._pendingForceUpdate = !1, this._performComponentUpdate(r, u, c, s, e, a)) : (this._currentElement = r, this._context = a, i.props = u, i.state = c, i.context = s) + }, + _processPendingState: function(e, t) { + var n = this._instance, + r = this._pendingStateQueue, + o = this._pendingReplaceState; + if (this._pendingReplaceState = !1, this._pendingStateQueue = null, !r) return n.state; + for (var a = y({}, o ? r[0] : n.state), i = o ? 1 : 0; i < r.length; i++) { + var s = r[i]; + y(a, "function" == typeof s ? s.call(n, a, e, t) : s) + } + return a + }, + _performComponentUpdate: function(e, t, n, r, o, a) { + var i = this._instance, + s = i.props, + u = i.state, + c = i.context; + i.componentWillUpdate && i.componentWillUpdate(t, n, r), this._currentElement = e, this._context = a, i.props = t, i.state = n, i.context = r, this._updateRenderedComponent(o, a), i.componentDidUpdate && o.getReactMountReady().enqueue(i.componentDidUpdate.bind(i, s, u, c), i) + }, + _updateRenderedComponent: function(e, t) { + var n = this._renderedComponent, + r = n._currentElement, + o = this._renderValidatedComponent(); + if (C(r, o)) v.receiveComponent(n, o, e, this._processChildContext(t)); + else { + var a = this._rootNodeID, + i = n._rootNodeID; + v.unmountComponent(n), this._renderedComponent = this._instantiateReactComponent(o, this._currentElement.type); + var s = v.mountComponent(this._renderedComponent, a, e, t); + this._replaceNodeWithMarkupByID(i, s) + } + }, + _replaceNodeWithMarkupByID: function(e, t) { + o.replaceNodeWithMarkupByID(e, t) + }, + _renderValidatedComponentWithoutOwnerOrContext: function() { + var e = this._instance, + t = e.render(); + return "production" !== n.env.NODE_ENV && "undefined" == typeof t && e.render._isMockFunction && (t = null), t + }, + _renderValidatedComponent: function() { + var e, t = a.current; + a.current = this._processChildContext(this._currentElement._context), i.current = this; + try { + e = this._renderValidatedComponentWithoutOwnerOrContext() + } finally { + a.current = t, i.current = null + } + return "production" !== n.env.NODE_ENV ? E(null === e || e === !1 || s.isValidElement(e), "%s.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.", this.getName() || "ReactCompositeComponent") : E(null === e || e === !1 || s.isValidElement(e)), e + }, + attachRef: function(e, t) { + var n = this.getPublicInstance(), + r = n.refs === g ? n.refs = {} : n.refs; + r[e] = t.getPublicInstance() + }, + detachRef: function(e) { + var t = this.getPublicInstance().refs; + delete t[e] + }, + getName: function() { + var e = this._currentElement.type, + t = this._instance && this._instance.constructor; + return e.displayName || t && t.displayName || e.name || t && t.name || null + }, + getPublicInstance: function() { + return this._instance + }, + _instantiateReactComponent: null + }; + d.measureMethods(N, "ReactCompositeComponent", { + mountComponent: "mountComponent", + updateComponent: "updateComponent", + _renderValidatedComponent: "_renderValidatedComponent" + }); + var O = { + Mixin: N + }; + t.exports = O + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactComponentEnvironment": 47, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactNativeComponent": 86, + "./ReactPerf": 88, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./ReactReconciler": 95, + "./ReactUpdates": 106, + "./emptyObject": 137, + "./invariant": 157, + "./shouldUpdateReactComponent": 174, + "./warning": 178, + _process: 2 + }], + 50: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./Object.assign"), + o = e("./emptyObject"), + a = e("./warning"), + i = !1, + s = { + current: o, + withContext: function(e, t) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? a(i, "withContext is deprecated and will be removed in a future version. Use a wrapper component with getChildContext instead.") : null, i = !0); + var o, u = s.current; + s.current = r({}, u, e); + try { + o = t() + } finally { + s.current = u + } + return o + } + }; + t.exports = s + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./emptyObject": 137, + "./warning": 178, + _process: 2 + }], + 51: [function(e, t) { + "use strict"; + var n = { + current: null + }; + t.exports = n + }, {}], + 52: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return "production" !== n.env.NODE_ENV ? a.createFactory(e) : o.createFactory(e) + } + var o = e("./ReactElement"), + a = e("./ReactElementValidator"), + i = e("./mapObject"), + s = i({ + a: "a", + abbr: "abbr", + address: "address", + area: "area", + article: "article", + aside: "aside", + audio: "audio", + b: "b", + base: "base", + bdi: "bdi", + bdo: "bdo", + big: "big", + blockquote: "blockquote", + body: "body", + br: "br", + button: "button", + canvas: "canvas", + caption: "caption", + cite: "cite", + code: "code", + col: "col", + colgroup: "colgroup", + data: "data", + datalist: "datalist", + dd: "dd", + del: "del", + details: "details", + dfn: "dfn", + dialog: "dialog", + div: "div", + dl: "dl", + dt: "dt", + em: "em", + embed: "embed", + fieldset: "fieldset", + figcaption: "figcaption", + figure: "figure", + footer: "footer", + form: "form", + h1: "h1", + h2: "h2", + h3: "h3", + h4: "h4", + h5: "h5", + h6: "h6", + head: "head", + header: "header", + hr: "hr", + html: "html", + i: "i", + iframe: "iframe", + img: "img", + input: "input", + ins: "ins", + kbd: "kbd", + keygen: "keygen", + label: "label", + legend: "legend", + li: "li", + link: "link", + main: "main", + map: "map", + mark: "mark", + menu: "menu", + menuitem: "menuitem", + meta: "meta", + meter: "meter", + nav: "nav", + noscript: "noscript", + object: "object", + ol: "ol", + optgroup: "optgroup", + option: "option", + output: "output", + p: "p", + param: "param", + picture: "picture", + pre: "pre", + progress: "progress", + q: "q", + rp: "rp", + rt: "rt", + ruby: "ruby", + s: "s", + samp: "samp", + script: "script", + section: "section", + select: "select", + small: "small", + source: "source", + span: "span", + strong: "strong", + style: "style", + sub: "sub", + summary: "summary", + sup: "sup", + table: "table", + tbody: "tbody", + td: "td", + textarea: "textarea", + tfoot: "tfoot", + th: "th", + thead: "thead", + time: "time", + title: "title", + tr: "tr", + track: "track", + u: "u", + ul: "ul", + "var": "var", + video: "video", + wbr: "wbr", + circle: "circle", + defs: "defs", + ellipse: "ellipse", + g: "g", + line: "line", + linearGradient: "linearGradient", + mask: "mask", + path: "path", + pattern: "pattern", + polygon: "polygon", + polyline: "polyline", + radialGradient: "radialGradient", + rect: "rect", + stop: "stop", + svg: "svg", + text: "text", + tspan: "tspan" + }, r); + t.exports = s + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./mapObject": 165, + _process: 2 + }], + 53: [function(e, t) { + "use strict"; + var n = e("./AutoFocusMixin"), + r = e("./ReactBrowserComponentMixin"), + o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./keyMirror"), + s = a.createFactory("button"), + u = i({ + onClick: !0, + onDoubleClick: !0, + onMouseDown: !0, + onMouseMove: !0, + onMouseUp: !0, + onClickCapture: !0, + onDoubleClickCapture: !0, + onMouseDownCapture: !0, + onMouseMoveCapture: !0, + onMouseUpCapture: !0 + }), + c = o.createClass({ + displayName: "ReactDOMButton", + tagName: "BUTTON", + mixins: [n, r], + render: function() { + var e = {}; + for (var t in this.props) !this.props.hasOwnProperty(t) || this.props.disabled && u[t] || (e[t] = this.props[t]); + return s(e, this.props.children) + } + }); + t.exports = c + }, { + "./AutoFocusMixin": 8, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./keyMirror": 163 + }], + 54: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e && (null != e.dangerouslySetInnerHTML && ("production" !== n.env.NODE_ENV ? y(null == e.children, "Can only set one of `children` or `props.dangerouslySetInnerHTML`.") : y(null == e.children), "production" !== n.env.NODE_ENV ? y(null != e.dangerouslySetInnerHTML.__html, "`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit http://fb.me/react-invariant-dangerously-set-inner-html for more information.") : y(null != e.dangerouslySetInnerHTML.__html)), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? C(null == e.innerHTML, "Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`.") : null, "production" !== n.env.NODE_ENV ? C(!e.contentEditable || null == e.children, "A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.") : null), "production" !== n.env.NODE_ENV ? y(null == e.style || "object" == typeof e.style, "The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.") : y(null == e.style || "object" == typeof e.style)) + } + + function o(e, t, r, o) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? C("onScroll" !== t || g("scroll", !0), "This browser doesn't support the `onScroll` event") : null); + var a = d.findReactContainerForID(e); + if (a) { + var i = a.nodeType === D ? a.ownerDocument : a; + _(t, i) + } + o.getPutListenerQueue().enqueuePutListener(e, t, r) + } + + function a(e) { + P.call(T, e) || ("production" !== n.env.NODE_ENV ? y(x.test(e), "Invalid tag: %s", e) : y(x.test(e)), T[e] = !0) + } + + function i(e) { + a(e), this._tag = e, this._renderedChildren = null, this._previousStyleCopy = null, this._rootNodeID = null + } + var s = e("./CSSPropertyOperations"), + u = e("./DOMProperty"), + c = e("./DOMPropertyOperations"), + l = e("./ReactBrowserEventEmitter"), + p = e("./ReactComponentBrowserEnvironment"), + d = e("./ReactMount"), + f = e("./ReactMultiChild"), + h = e("./ReactPerf"), + v = e("./Object.assign"), + m = e("./escapeTextContentForBrowser"), + y = e("./invariant"), + g = e("./isEventSupported"), + E = e("./keyOf"), + C = e("./warning"), + b = l.deleteListener, + _ = l.listenTo, + N = l.registrationNameModules, + O = { + string: !0, + number: !0 + }, + R = E({ + style: null + }), + D = 1, + w = null, + M = { + area: !0, + base: !0, + br: !0, + col: !0, + embed: !0, + hr: !0, + img: !0, + input: !0, + keygen: !0, + link: !0, + meta: !0, + param: !0, + source: !0, + track: !0, + wbr: !0 + }, + x = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/, + T = {}, + P = {}.hasOwnProperty; + i.displayName = "ReactDOMComponent", i.Mixin = { + construct: function(e) { + this._currentElement = e + }, + mountComponent: function(e, t, n) { + this._rootNodeID = e, r(this._currentElement.props); + var o = M[this._tag] ? "" : "" + this._tag + ">"; + return this._createOpenTagMarkupAndPutListeners(t) + this._createContentMarkup(t, n) + o + }, + _createOpenTagMarkupAndPutListeners: function(e) { + var t = this._currentElement.props, + n = "<" + this._tag; + for (var r in t) + if (t.hasOwnProperty(r)) { + var a = t[r]; + if (null != a) + if (N.hasOwnProperty(r)) o(this._rootNodeID, r, a, e); + else { + r === R && (a && (a = this._previousStyleCopy = v({}, t.style)), a = s.createMarkupForStyles(a)); + var i = c.createMarkupForProperty(r, a); + i && (n += " " + i) + } + } if (e.renderToStaticMarkup) return n + ">"; + var u = c.createMarkupForID(this._rootNodeID); + return n + " " + u + ">" + }, + _createContentMarkup: function(e, t) { + var n = ""; + ("listing" === this._tag || "pre" === this._tag || "textarea" === this._tag) && (n = "\n"); + var r = this._currentElement.props, + o = r.dangerouslySetInnerHTML; + if (null != o) { + if (null != o.__html) return n + o.__html + } else { + var a = O[typeof r.children] ? r.children : null, + i = null != a ? null : r.children; + if (null != a) return n + m(a); + if (null != i) { + var s = this.mountChildren(i, e, t); + return n + s.join("") + } + } + return n + }, + receiveComponent: function(e, t, n) { + var r = this._currentElement; + this._currentElement = e, this.updateComponent(t, r, e, n) + }, + updateComponent: function(e, t, n, o) { + r(this._currentElement.props), this._updateDOMProperties(t.props, e), this._updateDOMChildren(t.props, e, o) + }, + _updateDOMProperties: function(e, t) { + var n, r, a, i = this._currentElement.props; + for (n in e) + if (!i.hasOwnProperty(n) && e.hasOwnProperty(n)) + if (n === R) { + var s = this._previousStyleCopy; + for (r in s) s.hasOwnProperty(r) && (a = a || {}, a[r] = ""); + this._previousStyleCopy = null + } else N.hasOwnProperty(n) ? b(this._rootNodeID, n) : (u.isStandardName[n] || u.isCustomAttribute(n)) && w.deletePropertyByID(this._rootNodeID, n); + for (n in i) { + var c = i[n], + l = n === R ? this._previousStyleCopy : e[n]; + if (i.hasOwnProperty(n) && c !== l) + if (n === R) + if (c && (c = this._previousStyleCopy = v({}, c)), l) { + for (r in l) !l.hasOwnProperty(r) || c && c.hasOwnProperty(r) || (a = a || {}, a[r] = ""); + for (r in c) c.hasOwnProperty(r) && l[r] !== c[r] && (a = a || {}, a[r] = c[r]) + } else a = c; + else N.hasOwnProperty(n) ? o(this._rootNodeID, n, c, t) : (u.isStandardName[n] || u.isCustomAttribute(n)) && w.updatePropertyByID(this._rootNodeID, n, c) + } + a && w.updateStylesByID(this._rootNodeID, a) + }, + _updateDOMChildren: function(e, t, n) { + var r = this._currentElement.props, + o = O[typeof e.children] ? e.children : null, + a = O[typeof r.children] ? r.children : null, + i = e.dangerouslySetInnerHTML && e.dangerouslySetInnerHTML.__html, + s = r.dangerouslySetInnerHTML && r.dangerouslySetInnerHTML.__html, + u = null != o ? null : e.children, + c = null != a ? null : r.children, + l = null != o || null != i, + p = null != a || null != s; + null != u && null == c ? this.updateChildren(null, t, n) : l && !p && this.updateTextContent(""), null != a ? o !== a && this.updateTextContent("" + a) : null != s ? i !== s && w.updateInnerHTMLByID(this._rootNodeID, s) : null != c && this.updateChildren(c, t, n) + }, + unmountComponent: function() { + this.unmountChildren(), l.deleteAllListeners(this._rootNodeID), p.unmountIDFromEnvironment(this._rootNodeID), this._rootNodeID = null + } + }, h.measureMethods(i, "ReactDOMComponent", { + mountComponent: "mountComponent", + updateComponent: "updateComponent" + }), v(i.prototype, i.Mixin, f.Mixin), i.injection = { + injectIDOperations: function(e) { + i.BackendIDOperations = w = e + } + }, t.exports = i + }).call(this, e("_process")) + }, { + "./CSSPropertyOperations": 12, + "./DOMProperty": 17, + "./DOMPropertyOperations": 18, + "./Object.assign": 35, + "./ReactBrowserEventEmitter": 39, + "./ReactComponentBrowserEnvironment": 46, + "./ReactMount": 83, + "./ReactMultiChild": 84, + "./ReactPerf": 88, + "./escapeTextContentForBrowser": 138, + "./invariant": 157, + "./isEventSupported": 158, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 55: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("form"), + u = a.createClass({ + displayName: "ReactDOMForm", + tagName: "FORM", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topReset, "reset"), this.trapBubbledEvent(n.topLevelTypes.topSubmit, "submit") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 56: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./CSSPropertyOperations"), + o = e("./DOMChildrenOperations"), + a = e("./DOMPropertyOperations"), + i = e("./ReactMount"), + s = e("./ReactPerf"), + u = e("./invariant"), + c = e("./setInnerHTML"), + l = { + dangerouslySetInnerHTML: "`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.", + style: "`style` must be set using `updateStylesByID()`." + }, + p = { + updatePropertyByID: function(e, t, r) { + var o = i.getNode(e); + "production" !== n.env.NODE_ENV ? u(!l.hasOwnProperty(t), "updatePropertyByID(...): %s", l[t]) : u(!l.hasOwnProperty(t)), null != r ? a.setValueForProperty(o, t, r) : a.deleteValueForProperty(o, t) + }, + deletePropertyByID: function(e, t, r) { + var o = i.getNode(e); + "production" !== n.env.NODE_ENV ? u(!l.hasOwnProperty(t), "updatePropertyByID(...): %s", l[t]) : u(!l.hasOwnProperty(t)), a.deleteValueForProperty(o, t, r) + }, + updateStylesByID: function(e, t) { + var n = i.getNode(e); + r.setValueForStyles(n, t) + }, + updateInnerHTMLByID: function(e, t) { + var n = i.getNode(e); + c(n, t) + }, + updateTextContentByID: function(e, t) { + var n = i.getNode(e); + o.updateTextContent(n, t) + }, + dangerouslyReplaceNodeWithMarkupByID: function(e, t) { + var n = i.getNode(e); + o.dangerouslyReplaceNodeWithMarkup(n, t) + }, + dangerouslyProcessChildrenUpdates: function(e, t) { + for (var n = 0; n < e.length; n++) e[n].parentNode = i.getNode(e[n].parentID); + o.processUpdates(e, t) + } + }; + s.measureMethods(p, "ReactDOMIDOperations", { + updatePropertyByID: "updatePropertyByID", + deletePropertyByID: "deletePropertyByID", + updateStylesByID: "updateStylesByID", + updateInnerHTMLByID: "updateInnerHTMLByID", + updateTextContentByID: "updateTextContentByID", + dangerouslyReplaceNodeWithMarkupByID: "dangerouslyReplaceNodeWithMarkupByID", + dangerouslyProcessChildrenUpdates: "dangerouslyProcessChildrenUpdates" + }), t.exports = p + }).call(this, e("_process")) + }, { + "./CSSPropertyOperations": 12, + "./DOMChildrenOperations": 16, + "./DOMPropertyOperations": 18, + "./ReactMount": 83, + "./ReactPerf": 88, + "./invariant": 157, + "./setInnerHTML": 171, + _process: 2 + }], + 57: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("iframe"), + u = a.createClass({ + displayName: "ReactDOMIframe", + tagName: "IFRAME", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topLoad, "load") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 58: [function(e, t) { + "use strict"; + var n = e("./EventConstants"), + r = e("./LocalEventTrapMixin"), + o = e("./ReactBrowserComponentMixin"), + a = e("./ReactClass"), + i = e("./ReactElement"), + s = i.createFactory("img"), + u = a.createClass({ + displayName: "ReactDOMImg", + tagName: "IMG", + mixins: [o, r], + render: function() { + return s(this.props) + }, + componentDidMount: function() { + this.trapBubbledEvent(n.topLevelTypes.topLoad, "load"), this.trapBubbledEvent(n.topLevelTypes.topError, "error") + } + }); + t.exports = u + }, { + "./EventConstants": 22, + "./LocalEventTrapMixin": 33, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69 + }], + 59: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + this.isMounted() && this.forceUpdate() + } + var o = e("./AutoFocusMixin"), + a = e("./DOMPropertyOperations"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactMount"), + p = e("./ReactUpdates"), + d = e("./Object.assign"), + f = e("./invariant"), + h = c.createFactory("input"), + v = {}, + m = u.createClass({ + displayName: "ReactDOMInput", + tagName: "INPUT", + mixins: [o, i.Mixin, s], + getInitialState: function() { + var e = this.props.defaultValue; + return { + initialChecked: this.props.defaultChecked || !1, + initialValue: null != e ? e : null + } + }, + render: function() { + var e = d({}, this.props); + e.defaultChecked = null, e.defaultValue = null; + var t = i.getValue(this); + e.value = null != t ? t : this.state.initialValue; + var n = i.getChecked(this); + return e.checked = null != n ? n : this.state.initialChecked, e.onChange = this._handleChange, h(e, this.props.children) + }, + componentDidMount: function() { + var e = l.getID(this.getDOMNode()); + v[e] = this + }, + componentWillUnmount: function() { + var e = this.getDOMNode(), + t = l.getID(e); + delete v[t] + }, + componentDidUpdate: function() { + var e = this.getDOMNode(); + null != this.props.checked && a.setValueForProperty(e, "checked", this.props.checked || !1); + var t = i.getValue(this); + null != t && a.setValueForProperty(e, "value", "" + t) + }, + _handleChange: function(e) { + var t, o = i.getOnChange(this); + o && (t = o.call(this, e)), p.asap(r, this); + var a = this.props.name; + if ("radio" === this.props.type && null != a) { + for (var s = this.getDOMNode(), u = s; u.parentNode;) u = u.parentNode; + for (var c = u.querySelectorAll("input[name=" + JSON.stringify("" + a) + '][type="radio"]'), d = 0, h = c.length; h > d; d++) { + var m = c[d]; + if (m !== s && m.form === s.form) { + var y = l.getID(m); + "production" !== n.env.NODE_ENV ? f(y, "ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported.") : f(y); + var g = v[y]; + "production" !== n.env.NODE_ENV ? f(g, "ReactDOMInput: Unknown radio button ID %s.", y) : f(g), p.asap(r, g) + } + } + } + return t + } + }); + t.exports = m + }).call(this, e("_process")) + }, { + "./AutoFocusMixin": 8, + "./DOMPropertyOperations": 18, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./invariant": 157, + _process: 2 + }], + 60: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./ReactBrowserComponentMixin"), + o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./warning"), + s = a.createFactory("option"), + u = o.createClass({ + displayName: "ReactDOMOption", + tagName: "OPTION", + mixins: [r], + componentWillMount: function() { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? i(null == this.props.selected, "Use the `defaultValue` or `value` props on instead of setting `selected` on .") : null); + + }, + render: function() { + return s(this.props, this.props.children) + } + }); + t.exports = u + }).call(this, e("_process")) + }, { + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./warning": 178, + _process: 2 + }], + 61: [function(e, t) { + "use strict"; + + function n() { + if (this._pendingUpdate) { + this._pendingUpdate = !1; + var e = i.getValue(this); + null != e && this.isMounted() && o(this, e) + } + } + + function r(e, t) { + if (null == e[t]) return null; + if (e.multiple) { + if (!Array.isArray(e[t])) return new Error("The `" + t + "` prop supplied to must be an array if `multiple` is true.") + } else if (Array.isArray(e[t])) return new Error("The `" + t + "` prop supplied to must be a scalar value if `multiple` is false.") + } + + function o(e, t) { + var n, r, o, a = e.getDOMNode().options; + if (e.props.multiple) { + for (n = {}, r = 0, o = t.length; o > r; r++) n["" + t[r]] = !0; + for (r = 0, o = a.length; o > r; r++) { + var i = n.hasOwnProperty(a[r].value); + a[r].selected !== i && (a[r].selected = i) + } + } else { + for (n = "" + t, r = 0, o = a.length; o > r; r++) + if (a[r].value === n) return void(a[r].selected = !0); + a.length && (a[0].selected = !0) + } + } + var a = e("./AutoFocusMixin"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactUpdates"), + p = e("./Object.assign"), + d = c.createFactory("select"), + f = u.createClass({ + displayName: "ReactDOMSelect", + tagName: "SELECT", + mixins: [a, i.Mixin, s], + propTypes: { + defaultValue: r, + value: r + }, + render: function() { + var e = p({}, this.props); + return e.onChange = this._handleChange, e.value = null, d(e, this.props.children) + }, + componentWillMount: function() { + this._pendingUpdate = !1 + }, + componentDidMount: function() { + var e = i.getValue(this); + null != e ? o(this, e) : null != this.props.defaultValue && o(this, this.props.defaultValue) + }, + componentDidUpdate: function(e) { + var t = i.getValue(this); + null != t ? (this._pendingUpdate = !1, o(this, t)) : !e.multiple != !this.props.multiple && (null != this.props.defaultValue ? o(this, this.props.defaultValue) : o(this, this.props.multiple ? [] : "")) + }, + _handleChange: function(e) { + var t, r = i.getOnChange(this); + return r && (t = r.call(this, e)), this._pendingUpdate = !0, l.asap(n, this), t + } + }); + t.exports = f + }, { + "./AutoFocusMixin": 8, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactUpdates": 106 + }], + 62: [function(e, t) { + "use strict"; + + function n(e, t, n, r) { + return e === n && t === r + } + + function r(e) { + var t = document.selection, + n = t.createRange(), + r = n.text.length, + o = n.duplicate(); + o.moveToElementText(e), o.setEndPoint("EndToStart", n); + var a = o.text.length, + i = a + r; + return { + start: a, + end: i + } + } + + function o(e) { + var t = window.getSelection && window.getSelection(); + if (!t || 0 === t.rangeCount) return null; + var r = t.anchorNode, + o = t.anchorOffset, + a = t.focusNode, + i = t.focusOffset, + s = t.getRangeAt(0), + u = n(t.anchorNode, t.anchorOffset, t.focusNode, t.focusOffset), + c = u ? 0 : s.toString().length, + l = s.cloneRange(); + l.selectNodeContents(e), l.setEnd(s.startContainer, s.startOffset); + var p = n(l.startContainer, l.startOffset, l.endContainer, l.endOffset), + d = p ? 0 : l.toString().length, + f = d + c, + h = document.createRange(); + h.setStart(r, o), h.setEnd(a, i); + var v = h.collapsed; + return { + start: v ? f : d, + end: v ? d : f + } + } + + function a(e, t) { + var n, r, o = document.selection.createRange().duplicate(); + "undefined" == typeof t.end ? (n = t.start, r = n) : t.start > t.end ? (n = t.end, r = t.start) : (n = t.start, r = t.end), o.moveToElementText(e), o.moveStart("character", n), o.setEndPoint("EndToStart", o), o.moveEnd("character", r - n), o.select() + } + + function i(e, t) { + if (window.getSelection) { + var n = window.getSelection(), + r = e[c()].length, + o = Math.min(t.start, r), + a = "undefined" == typeof t.end ? o : Math.min(t.end, r); + if (!n.extend && o > a) { + var i = a; + a = o, o = i + } + var s = u(e, o), + l = u(e, a); + if (s && l) { + var p = document.createRange(); + p.setStart(s.node, s.offset), n.removeAllRanges(), o > a ? (n.addRange(p), n.extend(l.node, l.offset)) : (p.setEnd(l.node, l.offset), n.addRange(p)) + } + } + } + var s = e("./ExecutionEnvironment"), + u = e("./getNodeForCharacterOffset"), + c = e("./getTextContentAccessor"), + l = s.canUseDOM && "selection" in document && !("getSelection" in window), + p = { + getOffsets: l ? r : o, + setOffsets: l ? a : i + }; + t.exports = p + }, { + "./ExecutionEnvironment": 28, + "./getNodeForCharacterOffset": 150, + "./getTextContentAccessor": 152 + }], + 63: [function(e, t) { + "use strict"; + var n = e("./DOMPropertyOperations"), + r = e("./ReactComponentBrowserEnvironment"), + o = e("./ReactDOMComponent"), + a = e("./Object.assign"), + i = e("./escapeTextContentForBrowser"), + s = function() {}; + a(s.prototype, { + construct: function(e) { + this._currentElement = e, this._stringText = "" + e, this._rootNodeID = null, this._mountIndex = 0 + }, + mountComponent: function(e, t) { + this._rootNodeID = e; + var r = i(this._stringText); + return t.renderToStaticMarkup ? r : "" + r + "" + }, + receiveComponent: function(e) { + if (e !== this._currentElement) { + this._currentElement = e; + var t = "" + e; + t !== this._stringText && (this._stringText = t, o.BackendIDOperations.updateTextContentByID(this._rootNodeID, t)) + } + }, + unmountComponent: function() { + r.unmountIDFromEnvironment(this._rootNodeID) + } + }), t.exports = s + }, { + "./DOMPropertyOperations": 18, + "./Object.assign": 35, + "./ReactComponentBrowserEnvironment": 46, + "./ReactDOMComponent": 54, + "./escapeTextContentForBrowser": 138 + }], + 64: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + this.isMounted() && this.forceUpdate() + } + var o = e("./AutoFocusMixin"), + a = e("./DOMPropertyOperations"), + i = e("./LinkedValueUtils"), + s = e("./ReactBrowserComponentMixin"), + u = e("./ReactClass"), + c = e("./ReactElement"), + l = e("./ReactUpdates"), + p = e("./Object.assign"), + d = e("./invariant"), + f = e("./warning"), + h = c.createFactory("textarea"), + v = u.createClass({ + displayName: "ReactDOMTextarea", + tagName: "TEXTAREA", + mixins: [o, i.Mixin, s], + getInitialState: function() { + var e = this.props.defaultValue, + t = this.props.children; + null != t && ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? f(!1, "Use the `defaultValue` or `value` props instead of setting children on .") : null), "production" !== n.env.NODE_ENV ? d(null == e, "If you supply `defaultValue` on a , do not pass children.") : d(null == e), Array.isArray(t) && ("production" !== n.env.NODE_ENV ? d(t.length <= 1, " can only have at most one child.") : d(t.length <= 1), t = t[0]), e = "" + t), null == e && (e = ""); + var r = i.getValue(this); + return { + initialValue: "" + (null != r ? r : e) + } + }, + render: function() { + var e = p({}, this.props); + return "production" !== n.env.NODE_ENV ? d(null == e.dangerouslySetInnerHTML, "`dangerouslySetInnerHTML` does not make sense on .") : d(null == e.dangerouslySetInnerHTML), e.defaultValue = null, e.value = null, e.onChange = this._handleChange, h(e, this.state.initialValue) + }, + componentDidUpdate: function() { + var e = i.getValue(this); + if (null != e) { + var t = this.getDOMNode(); + a.setValueForProperty(t, "value", "" + e) + } + }, + _handleChange: function(e) { + var t, n = i.getOnChange(this); + return n && (t = n.call(this, e)), l.asap(r, this), t + } + }); + t.exports = v + }).call(this, e("_process")) + }, { + "./AutoFocusMixin": 8, + "./DOMPropertyOperations": 18, + "./LinkedValueUtils": 32, + "./Object.assign": 35, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactElement": 69, + "./ReactUpdates": 106, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 65: [function(e, t) { + "use strict"; + + function n() { + this.reinitializeTransaction() + } + var r = e("./ReactUpdates"), + o = e("./Transaction"), + a = e("./Object.assign"), + i = e("./emptyFunction"), + s = { + initialize: i, + close: function() { + p.isBatchingUpdates = !1 + } + }, + u = { + initialize: i, + close: r.flushBatchedUpdates.bind(r) + }, + c = [u, s]; + a(n.prototype, o.Mixin, { + getTransactionWrappers: function() { + return c + } + }); + var l = new n, + p = { + isBatchingUpdates: !1, + batchedUpdates: function(e, t, n, r, o) { + var a = p.isBatchingUpdates; + p.isBatchingUpdates = !0, a ? e(t, n, r, o) : l.perform(e, null, t, n, r, o) + } + }; + t.exports = p + }, { + "./Object.assign": 35, + "./ReactUpdates": 106, + "./Transaction": 123, + "./emptyFunction": 136 + }], + 66: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return h.createClass({ + tagName: e.toUpperCase(), + render: function() { + return new M(e, null, null, null, null, this.props) + } + }) + } + + function o() { + if (T.EventEmitter.injectReactEventListener(x), T.EventPluginHub.injectEventPluginOrder(u), T.EventPluginHub.injectInstanceHandle(P), T.EventPluginHub.injectMount(I), T.EventPluginHub.injectEventPluginsByName({ + SimpleEventPlugin: V, + EnterLeaveEventPlugin: c, + ChangeEventPlugin: i, + MobileSafariClickEventPlugin: d, + SelectEventPlugin: k, + BeforeInputEventPlugin: a + }), T.NativeComponent.injectGenericComponentClass(y), T.NativeComponent.injectTextComponentClass(w), T.NativeComponent.injectAutoWrapper(r), T.Class.injectMixin(f), T.NativeComponent.injectComponentClasses({ + button: g, + form: E, + iframe: _, + img: C, + input: N, + option: O, + select: R, + textarea: D, + html: L("html"), + head: L("head"), + body: L("body") + }), T.DOMProperty.injectDOMPropertyConfig(p), T.DOMProperty.injectDOMPropertyConfig(U), T.EmptyComponent.injectEmptyComponent("noscript"), T.Updates.injectReconcileTransaction(S), T.Updates.injectBatchingStrategy(m), T.RootIndex.injectCreateReactRootIndex(l.canUseDOM ? s.createReactRootIndex : A.createReactRootIndex), T.Component.injectEnvironment(v), T.DOMComponent.injectIDOperations(b), "production" !== n.env.NODE_ENV) { + var t = l.canUseDOM && window.location.href || ""; + if (/[?&]react_perf\b/.test(t)) { + var o = e("./ReactDefaultPerf"); + o.start() + } + } + } + var a = e("./BeforeInputEventPlugin"), + i = e("./ChangeEventPlugin"), + s = e("./ClientReactRootIndex"), + u = e("./DefaultEventPluginOrder"), + c = e("./EnterLeaveEventPlugin"), + l = e("./ExecutionEnvironment"), + p = e("./HTMLDOMPropertyConfig"), + d = e("./MobileSafariClickEventPlugin"), + f = e("./ReactBrowserComponentMixin"), + h = e("./ReactClass"), + v = e("./ReactComponentBrowserEnvironment"), + m = e("./ReactDefaultBatchingStrategy"), + y = e("./ReactDOMComponent"), + g = e("./ReactDOMButton"), + E = e("./ReactDOMForm"), + C = e("./ReactDOMImg"), + b = e("./ReactDOMIDOperations"), + _ = e("./ReactDOMIframe"), + N = e("./ReactDOMInput"), + O = e("./ReactDOMOption"), + R = e("./ReactDOMSelect"), + D = e("./ReactDOMTextarea"), + w = e("./ReactDOMTextComponent"), + M = e("./ReactElement"), + x = e("./ReactEventListener"), + T = e("./ReactInjection"), + P = e("./ReactInstanceHandles"), + I = e("./ReactMount"), + S = e("./ReactReconcileTransaction"), + k = e("./SelectEventPlugin"), + A = e("./ServerReactRootIndex"), + V = e("./SimpleEventPlugin"), + U = e("./SVGDOMPropertyConfig"), + L = e("./createFullPageComponent"); + t.exports = { + inject: o + } + }).call(this, e("_process")) + }, { + "./BeforeInputEventPlugin": 9, + "./ChangeEventPlugin": 14, + "./ClientReactRootIndex": 15, + "./DefaultEventPluginOrder": 20, + "./EnterLeaveEventPlugin": 21, + "./ExecutionEnvironment": 28, + "./HTMLDOMPropertyConfig": 30, + "./MobileSafariClickEventPlugin": 34, + "./ReactBrowserComponentMixin": 38, + "./ReactClass": 44, + "./ReactComponentBrowserEnvironment": 46, + "./ReactDOMButton": 53, + "./ReactDOMComponent": 54, + "./ReactDOMForm": 55, + "./ReactDOMIDOperations": 56, + "./ReactDOMIframe": 57, + "./ReactDOMImg": 58, + "./ReactDOMInput": 59, + "./ReactDOMOption": 60, + "./ReactDOMSelect": 61, + "./ReactDOMTextComponent": 63, + "./ReactDOMTextarea": 64, + "./ReactDefaultBatchingStrategy": 65, + "./ReactDefaultPerf": 67, + "./ReactElement": 69, + "./ReactEventListener": 74, + "./ReactInjection": 76, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactReconcileTransaction": 94, + "./SVGDOMPropertyConfig": 108, + "./SelectEventPlugin": 109, + "./ServerReactRootIndex": 110, + "./SimpleEventPlugin": 111, + "./createFullPageComponent": 132, + _process: 2 + }], + 67: [function(e, t) { + "use strict"; + + function n(e) { + return Math.floor(100 * e) / 100 + } + + function r(e, t, n) { + e[t] = (e[t] || 0) + n + } + var o = e("./DOMProperty"), + a = e("./ReactDefaultPerfAnalysis"), + i = e("./ReactMount"), + s = e("./ReactPerf"), + u = e("./performanceNow"), + c = { + _allMeasurements: [], + _mountStack: [0], + _injected: !1, + start: function() { + c._injected || s.injection.injectMeasure(c.measure), c._allMeasurements.length = 0, s.enableMeasure = !0 + }, + stop: function() { + s.enableMeasure = !1 + }, + getLastMeasurements: function() { + return c._allMeasurements + }, + printExclusive: function(e) { + e = e || c._allMeasurements; + var t = a.getExclusiveSummary(e); + console.table(t.map(function(e) { + return { + "Component class name": e.componentName, + "Total inclusive time (ms)": n(e.inclusive), + "Exclusive mount time (ms)": n(e.exclusive), + "Exclusive render time (ms)": n(e.render), + "Mount time per instance (ms)": n(e.exclusive / e.count), + "Render time per instance (ms)": n(e.render / e.count), + Instances: e.count + } + })) + }, + printInclusive: function(e) { + e = e || c._allMeasurements; + var t = a.getInclusiveSummary(e); + console.table(t.map(function(e) { + return { + "Owner > component": e.componentName, + "Inclusive time (ms)": n(e.time), + Instances: e.count + } + })), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + getMeasurementsSummaryMap: function(e) { + var t = a.getInclusiveSummary(e, !0); + return t.map(function(e) { + return { + "Owner > component": e.componentName, + "Wasted time (ms)": e.time, + Instances: e.count + } + }) + }, + printWasted: function(e) { + e = e || c._allMeasurements, console.table(c.getMeasurementsSummaryMap(e)), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + printDOM: function(e) { + e = e || c._allMeasurements; + var t = a.getDOMSummary(e); + console.table(t.map(function(e) { + var t = {}; + return t[o.ID_ATTRIBUTE_NAME] = e.id, t.type = e.type, t.args = JSON.stringify(e.args), t + })), console.log("Total time:", a.getTotalTime(e).toFixed(2) + " ms") + }, + _recordWrite: function(e, t, n, r) { + var o = c._allMeasurements[c._allMeasurements.length - 1].writes; + o[e] = o[e] || [], o[e].push({ + type: t, + time: n, + args: r + }) + }, + measure: function(e, t, n) { + return function() { + for (var o = [], a = 0, s = arguments.length; s > a; a++) o.push(arguments[a]); + var l, p, d; + if ("_renderNewRootComponent" === t || "flushBatchedUpdates" === t) return c._allMeasurements.push({ + exclusive: {}, + inclusive: {}, + render: {}, + counts: {}, + writes: {}, + displayNames: {}, + totalTime: 0 + }), d = u(), p = n.apply(this, o), c._allMeasurements[c._allMeasurements.length - 1].totalTime = u() - d, p; + if ("_mountImageIntoNode" === t || "ReactDOMIDOperations" === e) { + if (d = u(), p = n.apply(this, o), l = u() - d, "_mountImageIntoNode" === t) { + var f = i.getID(o[1]); + c._recordWrite(f, t, l, o[0]) + } else "dangerouslyProcessChildrenUpdates" === t ? o[0].forEach(function(e) { + var t = {}; + null !== e.fromIndex && (t.fromIndex = e.fromIndex), null !== e.toIndex && (t.toIndex = e.toIndex), null !== e.textContent && (t.textContent = e.textContent), null !== e.markupIndex && (t.markup = o[1][e.markupIndex]), c._recordWrite(e.parentID, e.type, l, t) + }) : c._recordWrite(o[0], t, l, Array.prototype.slice.call(o, 1)); + return p + } + if ("ReactCompositeComponent" !== e || "mountComponent" !== t && "updateComponent" !== t && "_renderValidatedComponent" !== t) return n.apply(this, o); + if ("string" == typeof this._currentElement.type) return n.apply(this, o); + var h = "mountComponent" === t ? o[0] : this._rootNodeID, + v = "_renderValidatedComponent" === t, + m = "mountComponent" === t, + y = c._mountStack, + g = c._allMeasurements[c._allMeasurements.length - 1]; + if (v ? r(g.counts, h, 1) : m && y.push(0), d = u(), p = n.apply(this, o), l = u() - d, v) r(g.render, h, l); + else if (m) { + var E = y.pop(); + y[y.length - 1] += l, r(g.exclusive, h, l - E), r(g.inclusive, h, l) + } else r(g.inclusive, h, l); + return g.displayNames[h] = { + current: this.getName(), + owner: this._currentElement._owner ? this._currentElement._owner.getName() : "" + }, p + } + } + }; + t.exports = c + }, { + "./DOMProperty": 17, + "./ReactDefaultPerfAnalysis": 68, + "./ReactMount": 83, + "./ReactPerf": 88, + "./performanceNow": 169 + }], + 68: [function(e, t) { + function n(e) { + for (var t = 0, n = 0; n < e.length; n++) { + var r = e[n]; + t += r.totalTime + } + return t + } + + function r(e) { + for (var t = [], n = 0; n < e.length; n++) { + var r, o = e[n]; + for (r in o.writes) o.writes[r].forEach(function(e) { + t.push({ + id: r, + type: c[e.type] || e.type, + args: e.args + }) + }) + } + return t + } + + function o(e) { + for (var t, n = {}, r = 0; r < e.length; r++) { + var o = e[r], + a = s({}, o.exclusive, o.inclusive); + for (var i in a) t = o.displayNames[i].current, n[t] = n[t] || { + componentName: t, + inclusive: 0, + exclusive: 0, + render: 0, + count: 0 + }, o.render[i] && (n[t].render += o.render[i]), o.exclusive[i] && (n[t].exclusive += o.exclusive[i]), o.inclusive[i] && (n[t].inclusive += o.inclusive[i]), o.counts[i] && (n[t].count += o.counts[i]) + } + var c = []; + for (t in n) n[t].exclusive >= u && c.push(n[t]); + return c.sort(function(e, t) { + return t.exclusive - e.exclusive + }), c + } + + function a(e, t) { + for (var n, r = {}, o = 0; o < e.length; o++) { + var a, c = e[o], + l = s({}, c.exclusive, c.inclusive); + t && (a = i(c)); + for (var p in l) + if (!t || a[p]) { + var d = c.displayNames[p]; + n = d.owner + " > " + d.current, r[n] = r[n] || { + componentName: n, + time: 0, + count: 0 + }, c.inclusive[p] && (r[n].time += c.inclusive[p]), c.counts[p] && (r[n].count += c.counts[p]) + } + } + var f = []; + for (n in r) r[n].time >= u && f.push(r[n]); + return f.sort(function(e, t) { + return t.time - e.time + }), f + } + + function i(e) { + var t = {}, + n = Object.keys(e.writes), + r = s({}, e.exclusive, e.inclusive); + for (var o in r) { + for (var a = !1, i = 0; i < n.length; i++) + if (0 === n[i].indexOf(o)) { + a = !0; + break + }! a && e.counts[o] > 0 && (t[o] = !0) + } + return t + } + var s = e("./Object.assign"), + u = 1.2, + c = { + _mountImageIntoNode: "set innerHTML", + INSERT_MARKUP: "set innerHTML", + MOVE_EXISTING: "move", + REMOVE_NODE: "remove", + TEXT_CONTENT: "set textContent", + updatePropertyByID: "update attribute", + deletePropertyByID: "delete attribute", + updateStylesByID: "update styles", + updateInnerHTMLByID: "set innerHTML", + dangerouslyReplaceNodeWithMarkupByID: "replace" + }, + l = { + getExclusiveSummary: o, + getInclusiveSummary: a, + getDOMSummary: r, + getTotalTime: n + }; + t.exports = l + }, { + "./Object.assign": 35 + }], + 69: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + Object.defineProperty(e, t, { + configurable: !1, + enumerable: !0, + get: function() { + return this._store ? this._store[t] : null + }, + set: function(e) { + "production" !== n.env.NODE_ENV ? u(!1, "Don't set the %s property of the React element. Instead, specify the correct value when initially creating the element.", t) : null, this._store[t] = e + } + }) + } + + function o(e) { + try { + var t = { + props: !0 + }; + for (var n in t) r(e, n); + l = !0 + } catch (o) {} + } + var a = e("./ReactContext"), + i = e("./ReactCurrentOwner"), + s = e("./Object.assign"), + u = e("./warning"), + c = { + key: !0, + ref: !0 + }, + l = !1, + p = function(e, t, r, o, a, i) { + if (this.type = e, this.key = t, this.ref = r, this._owner = o, this._context = a, "production" !== n.env.NODE_ENV) { + this._store = { + props: i, + originalProps: s({}, i) + }; + try { + Object.defineProperty(this._store, "validated", { + configurable: !1, + enumerable: !1, + writable: !0 + }) + } catch (u) {} + if (this._store.validated = !1, l) return void Object.freeze(this) + } + this.props = i + }; + p.prototype = { + _isReactElement: !0 + }, "production" !== n.env.NODE_ENV && o(p.prototype), p.createElement = function(e, t, n) { + var r, o = {}, + s = null, + u = null; + if (null != t) { + u = void 0 === t.ref ? null : t.ref, s = void 0 === t.key ? null : "" + t.key; + for (r in t) t.hasOwnProperty(r) && !c.hasOwnProperty(r) && (o[r] = t[r]) + } + var l = arguments.length - 2; + if (1 === l) o.children = n; + else if (l > 1) { + for (var d = Array(l), f = 0; l > f; f++) d[f] = arguments[f + 2]; + o.children = d + } + if (e && e.defaultProps) { + var h = e.defaultProps; + for (r in h) "undefined" == typeof o[r] && (o[r] = h[r]) + } + return new p(e, s, u, i.current, a.current, o) + }, p.createFactory = function(e) { + var t = p.createElement.bind(null, e); + return t.type = e, t + }, p.cloneAndReplaceProps = function(e, t) { + var r = new p(e.type, e.key, e.ref, e._owner, e._context, t); + return "production" !== n.env.NODE_ENV && (r._store.validated = e._store.validated), r + }, p.cloneElement = function(e, t, n) { + var r, o = s({}, e.props), + a = e.key, + u = e.ref, + l = e._owner; + if (null != t) { + void 0 !== t.ref && (u = t.ref, l = i.current), void 0 !== t.key && (a = "" + t.key); + for (r in t) t.hasOwnProperty(r) && !c.hasOwnProperty(r) && (o[r] = t[r]) + } + var d = arguments.length - 2; + if (1 === d) o.children = n; + else if (d > 1) { + for (var f = Array(d), h = 0; d > h; h++) f[h] = arguments[h + 2]; + o.children = f + } + return new p(e.type, a, u, l, e._context, o) + }, p.isValidElement = function(e) { + var t = !(!e || !e._isReactElement); + return t + }, t.exports = p + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactContext": 50, + "./ReactCurrentOwner": 51, + "./warning": 178, + _process: 2 + }], + 70: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + if (E.current) { + var e = E.current.getName(); + if (e) return " Check the render method of `" + e + "`." + } + return "" + } + + function o(e) { + var t = e && e.getPublicInstance(); + if (!t) return void 0; + var n = t.constructor; + return n ? n.displayName || n.name || void 0 : void 0 + } + + function a() { + var e = E.current; + return e && o(e) || void 0 + } + + function i(e, t) { + e._store.validated || null != e.key || (e._store.validated = !0, u('Each child in an array or iterator should have a unique "key" prop.', e, t)) + } + + function s(e, t, n) { + D.test(e) && u("Child objects should have non-numeric keys so ordering is preserved.", t, n) + } + + function u(e, t, r) { + var i = a(), + s = "string" == typeof r ? r : r.displayName || r.name, + u = i || s, + c = O[e] || (O[e] = {}); + if (!c.hasOwnProperty(u)) { + c[u] = !0; + var l = i ? " Check the render method of " + i + "." : s ? " Check the React.render call using <" + s + ">." : "", + p = ""; + if (t && t._owner && t._owner !== E.current) { + var d = o(t._owner); + p = " It was passed a child from " + d + "." + } + "production" !== n.env.NODE_ENV ? N(!1, e + "%s%s See http://fb.me/react-warning-keys for more information.", l, p) : null + } + } + + function c(e, t) { + if (Array.isArray(e)) + for (var n = 0; n < e.length; n++) { + var r = e[n]; + v.isValidElement(r) && i(r, t) + } else if (v.isValidElement(e)) e._store.validated = !0; + else if (e) { + var o = b(e); + if (o) { + if (o !== e.entries) + for (var a, u = o.call(e); !(a = u.next()).done;) v.isValidElement(a.value) && i(a.value, t) + } else if ("object" == typeof e) { + var c = m.extractIfFragment(e); + for (var l in c) c.hasOwnProperty(l) && s(l, c[l], t) + } + } + } + + function l(e, t, o, a) { + for (var i in t) + if (t.hasOwnProperty(i)) { + var s; + try { + "production" !== n.env.NODE_ENV ? _("function" == typeof t[i], "%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.", e || "React class", g[a], i) : _("function" == typeof t[i]), s = t[i](o, i, e, a) + } catch (u) { + s = u + } + if (s instanceof Error && !(s.message in R)) { + R[s.message] = !0; + var c = r(this); + "production" !== n.env.NODE_ENV ? N(!1, "Failed propType: %s%s", s.message, c) : null + } + } + } + + function p(e, t) { + var r = t.type, + o = "string" == typeof r ? r : r.displayName, + a = t._owner ? t._owner.getPublicInstance().constructor.displayName : null, + i = e + "|" + o + "|" + a; + if (!w.hasOwnProperty(i)) { + w[i] = !0; + var s = ""; + o && (s = " <" + o + " />"); + var u = ""; + a && (u = " The element was created by " + a + "."), "production" !== n.env.NODE_ENV ? N(!1, "Don't set .props.%s of the React component%s. Instead, specify the correct value when initially creating the element.%s", e, s, u) : null + } + } + + function d(e, t) { + return e !== e ? t !== t : 0 === e && 0 === t ? 1 / e === 1 / t : e === t + } + + function f(e) { + if (e._store) { + var t = e._store.originalProps, + n = e.props; + for (var r in n) n.hasOwnProperty(r) && (t.hasOwnProperty(r) && d(t[r], n[r]) || (p(r, e), t[r] = n[r])) + } + } + + function h(e) { + if (null != e.type) { + var t = C.getComponentClassForElement(e), + r = t.displayName || t.name; + t.propTypes && l(r, t.propTypes, e.props, y.prop), "function" == typeof t.getDefaultProps && ("production" !== n.env.NODE_ENV ? N(t.getDefaultProps.isReactClassApproved, "getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.") : null) + } + } + var v = e("./ReactElement"), + m = e("./ReactFragment"), + y = e("./ReactPropTypeLocations"), + g = e("./ReactPropTypeLocationNames"), + E = e("./ReactCurrentOwner"), + C = e("./ReactNativeComponent"), + b = e("./getIteratorFn"), + _ = e("./invariant"), + N = e("./warning"), + O = {}, + R = {}, + D = /^\d+$/, + w = {}, + M = { + checkAndWarnForMutatedProps: f, + createElement: function(e) { + "production" !== n.env.NODE_ENV ? N(null != e, "React.createElement: type should not be null or undefined. It should be a string (for DOM elements) or a ReactClass (for composite components).") : null; + var t = v.createElement.apply(this, arguments); + if (null == t) return t; + for (var r = 2; r < arguments.length; r++) c(arguments[r], e); + return h(t), t + }, + createFactory: function(e) { + var t = M.createElement.bind(null, e); + if (t.type = e, "production" !== n.env.NODE_ENV) try { + Object.defineProperty(t, "type", { + enumerable: !1, + get: function() { + return "production" !== n.env.NODE_ENV ? N(!1, "Factory.type is deprecated. Access the class directly before passing it to createFactory.") : null, Object.defineProperty(this, "type", { + value: e + }), e + } + }) + } catch (r) {} + return t + }, + cloneElement: function() { + for (var e = v.cloneElement.apply(this, arguments), t = 2; t < arguments.length; t++) c(arguments[t], e.type); + return h(e), e + } + }; + t.exports = M + }).call(this, e("_process")) + }, { + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactFragment": 75, + "./ReactNativeComponent": 86, + "./ReactPropTypeLocationNames": 90, + "./ReactPropTypeLocations": 91, + "./getIteratorFn": 148, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 71: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + l[e] = !0 + } + + function o(e) { + delete l[e] + } + + function a(e) { + return !!l[e] + } + var i, s = e("./ReactElement"), + u = e("./ReactInstanceMap"), + c = e("./invariant"), + l = {}, + p = { + injectEmptyComponent: function(e) { + i = s.createFactory(e) + } + }, + d = function() {}; + d.prototype.componentDidMount = function() { + var e = u.get(this); + e && r(e._rootNodeID) + }, d.prototype.componentWillUnmount = function() { + var e = u.get(this); + e && o(e._rootNodeID) + }, d.prototype.render = function() { + return "production" !== n.env.NODE_ENV ? c(i, "Trying to return null from a render, but no null placeholder component was injected.") : c(i), i() + }; + var f = s.createElement(d), + h = { + emptyElement: f, + injection: p, + isNullComponentID: a + }; + t.exports = h + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactInstanceMap": 79, + "./invariant": 157, + _process: 2 + }], + 72: [function(e, t) { + "use strict"; + var n = { + guard: function(e) { + return e + } + }; + t.exports = n + }, {}], + 73: [function(e, t) { + "use strict"; + + function n(e) { + r.enqueueEvents(e), r.processEventQueue() + } + var r = e("./EventPluginHub"), + o = { + handleTopLevel: function(e, t, o, a) { + var i = r.extractEvents(e, t, o, a); + n(i) + } + }; + t.exports = o + }, { + "./EventPluginHub": 24 + }], + 74: [function(e, t) { + "use strict"; + + function n(e) { + var t = l.getID(e), + n = c.getReactRootIDFromNodeID(t), + r = l.findReactContainerForID(n), + o = l.getFirstReactDOM(r); + return o + } + + function r(e, t) { + this.topLevelType = e, this.nativeEvent = t, this.ancestors = [] + } + + function o(e) { + for (var t = l.getFirstReactDOM(f(e.nativeEvent)) || window, r = t; r;) e.ancestors.push(r), r = n(r); + for (var o = 0, a = e.ancestors.length; a > o; o++) { + t = e.ancestors[o]; + var i = l.getID(t) || ""; + v._handleTopLevel(e.topLevelType, t, i, e.nativeEvent) + } + } + + function a(e) { + var t = h(window); + e(t) + } + var i = e("./EventListener"), + s = e("./ExecutionEnvironment"), + u = e("./PooledClass"), + c = e("./ReactInstanceHandles"), + l = e("./ReactMount"), + p = e("./ReactUpdates"), + d = e("./Object.assign"), + f = e("./getEventTarget"), + h = e("./getUnboundedScrollPosition"); + d(r.prototype, { + destructor: function() { + this.topLevelType = null, this.nativeEvent = null, this.ancestors.length = 0 + } + }), u.addPoolingTo(r, u.twoArgumentPooler); + var v = { + _enabled: !0, + _handleTopLevel: null, + WINDOW_HANDLE: s.canUseDOM ? window : null, + setHandleTopLevel: function(e) { + v._handleTopLevel = e + }, + setEnabled: function(e) { + v._enabled = !!e + }, + isEnabled: function() { + return v._enabled + }, + trapBubbledEvent: function(e, t, n) { + var r = n; + return r ? i.listen(r, t, v.dispatchEvent.bind(null, e)) : null + }, + trapCapturedEvent: function(e, t, n) { + var r = n; + return r ? i.capture(r, t, v.dispatchEvent.bind(null, e)) : null + }, + monitorScrollValue: function(e) { + var t = a.bind(null, e); + i.listen(window, "scroll", t) + }, + dispatchEvent: function(e, t) { + if (v._enabled) { + var n = r.getPooled(e, t); + try { + p.batchedUpdates(o, n) + } finally { + r.release(n) + } + } + } + }; + t.exports = v + }, { + "./EventListener": 23, + "./ExecutionEnvironment": 28, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactInstanceHandles": 78, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./getEventTarget": 147, + "./getUnboundedScrollPosition": 153 + }], + 75: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./ReactElement"), + o = e("./warning"); + if ("production" !== n.env.NODE_ENV) { + var a = "_reactFragment", + i = "_reactDidWarn", + s = !1; + try { + var u = function() { + return 1 + }; + Object.defineProperty({}, a, { + enumerable: !1, + value: !0 + }), Object.defineProperty({}, "key", { + enumerable: !0, + get: u + }), s = !0 + } catch (c) {} + var l = function(e, t) { + Object.defineProperty(e, t, { + enumerable: !0, + get: function() { + return "production" !== n.env.NODE_ENV ? o(this[i], "A ReactFragment is an opaque type. Accessing any of its properties is deprecated. Pass it to one of the React.Children helpers.") : null, this[i] = !0, this[a][t] + }, + set: function(e) { + "production" !== n.env.NODE_ENV ? o(this[i], "A ReactFragment is an immutable opaque type. Mutating its properties is deprecated.") : null, this[i] = !0, this[a][t] = e + } + }) + }, + p = {}, + d = function(e) { + var t = ""; + for (var n in e) t += n + ":" + typeof e[n] + ","; + var r = !!p[t]; + return p[t] = !0, r + } + } + var f = { + create: function(e) { + if ("production" !== n.env.NODE_ENV) { + if ("object" != typeof e || !e || Array.isArray(e)) return "production" !== n.env.NODE_ENV ? o(!1, "React.addons.createFragment only accepts a single object.", e) : null, e; + if (r.isValidElement(e)) return "production" !== n.env.NODE_ENV ? o(!1, "React.addons.createFragment does not accept a ReactElement without a wrapper object.") : null, e; + if (s) { + var t = {}; + Object.defineProperty(t, a, { + enumerable: !1, + value: e + }), Object.defineProperty(t, i, { + writable: !0, + enumerable: !1, + value: !1 + }); + for (var u in e) l(t, u); + return Object.preventExtensions(t), t + } + } + return e + }, + extract: function(e) { + return "production" !== n.env.NODE_ENV && s ? e[a] ? e[a] : ("production" !== n.env.NODE_ENV ? o(d(e), "Any use of a keyed object should be wrapped in React.addons.createFragment(object) before being passed as a child.") : null, e) : e + }, + extractIfFragment: function(e) { + if ("production" !== n.env.NODE_ENV && s) { + if (e[a]) return e[a]; + for (var t in e) + if (e.hasOwnProperty(t) && r.isValidElement(e[t])) return f.extract(e) + } + return e + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./warning": 178, + _process: 2 + }], + 76: [function(e, t) { + "use strict"; + var n = e("./DOMProperty"), + r = e("./EventPluginHub"), + o = e("./ReactComponentEnvironment"), + a = e("./ReactClass"), + i = e("./ReactEmptyComponent"), + s = e("./ReactBrowserEventEmitter"), + u = e("./ReactNativeComponent"), + c = e("./ReactDOMComponent"), + l = e("./ReactPerf"), + p = e("./ReactRootIndex"), + d = e("./ReactUpdates"), + f = { + Component: o.injection, + Class: a.injection, + DOMComponent: c.injection, + DOMProperty: n.injection, + EmptyComponent: i.injection, + EventPluginHub: r.injection, + EventEmitter: s.injection, + NativeComponent: u.injection, + Perf: l.injection, + RootIndex: p.injection, + Updates: d.injection + }; + t.exports = f + }, { + "./DOMProperty": 17, + "./EventPluginHub": 24, + "./ReactBrowserEventEmitter": 39, + "./ReactClass": 44, + "./ReactComponentEnvironment": 47, + "./ReactDOMComponent": 54, + "./ReactEmptyComponent": 71, + "./ReactNativeComponent": 86, + "./ReactPerf": 88, + "./ReactRootIndex": 97, + "./ReactUpdates": 106 + }], + 77: [function(e, t) { + "use strict"; + + function n(e) { + return o(document.documentElement, e) + } + var r = e("./ReactDOMSelection"), + o = e("./containsNode"), + a = e("./focusNode"), + i = e("./getActiveElement"), + s = { + hasSelectionCapabilities: function(e) { + return e && ("INPUT" === e.nodeName && "text" === e.type || "TEXTAREA" === e.nodeName || "true" === e.contentEditable) + }, + getSelectionInformation: function() { + var e = i(); + return { + focusedElem: e, + selectionRange: s.hasSelectionCapabilities(e) ? s.getSelection(e) : null + } + }, + restoreSelection: function(e) { + var t = i(), + r = e.focusedElem, + o = e.selectionRange; + t !== r && n(r) && (s.hasSelectionCapabilities(r) && s.setSelection(r, o), a(r)) + }, + getSelection: function(e) { + var t; + if ("selectionStart" in e) t = { + start: e.selectionStart, + end: e.selectionEnd + }; + else if (document.selection && "INPUT" === e.nodeName) { + var n = document.selection.createRange(); + n.parentElement() === e && (t = { + start: -n.moveStart("character", -e.value.length), + end: -n.moveEnd("character", -e.value.length) + }) + } else t = r.getOffsets(e); + return t || { + start: 0, + end: 0 + } + }, + setSelection: function(e, t) { + var n = t.start, + o = t.end; + if ("undefined" == typeof o && (o = n), "selectionStart" in e) e.selectionStart = n, e.selectionEnd = Math.min(o, e.value.length); + else if (document.selection && "INPUT" === e.nodeName) { + var a = e.createTextRange(); + a.collapse(!0), a.moveStart("character", n), a.moveEnd("character", o - n), a.select() + } else r.setOffsets(e, t) + } + }; + t.exports = s + }, { + "./ReactDOMSelection": 62, + "./containsNode": 130, + "./focusNode": 141, + "./getActiveElement": 143 + }], + 78: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + return f + e.toString(36) + } + + function o(e, t) { + return e.charAt(t) === f || t === e.length + } + + function a(e) { + return "" === e || e.charAt(0) === f && e.charAt(e.length - 1) !== f + } + + function i(e, t) { + return 0 === t.indexOf(e) && o(t, e.length) + } + + function s(e) { + return e ? e.substr(0, e.lastIndexOf(f)) : "" + } + + function u(e, t) { + if ("production" !== n.env.NODE_ENV ? d(a(e) && a(t), "getNextDescendantID(%s, %s): Received an invalid React DOM ID.", e, t) : d(a(e) && a(t)), "production" !== n.env.NODE_ENV ? d(i(e, t), "getNextDescendantID(...): React has made an invalid assumption about the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.", e, t) : d(i(e, t)), e === t) return e; + var r, s = e.length + h; + for (r = s; r < t.length && !o(t, r); r++); + return t.substr(0, r) + } + + function c(e, t) { + var r = Math.min(e.length, t.length); + if (0 === r) return ""; + for (var i = 0, s = 0; r >= s; s++) + if (o(e, s) && o(t, s)) i = s; + else if (e.charAt(s) !== t.charAt(s)) break; + var u = e.substr(0, i); + return "production" !== n.env.NODE_ENV ? d(a(u), "getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s", e, t, u) : d(a(u)), u + } + + function l(e, t, r, o, a, c) { + e = e || "", t = t || "", "production" !== n.env.NODE_ENV ? d(e !== t, "traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.", e) : d(e !== t); + var l = i(t, e); + "production" !== n.env.NODE_ENV ? d(l || i(e, t), "traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do not have a parent path.", e, t) : d(l || i(e, t)); + for (var p = 0, f = l ? s : u, h = e;; h = f(h, t)) { + var m; + if (a && h === e || c && h === t || (m = r(h, l, o)), m === !1 || h === t) break; + "production" !== n.env.NODE_ENV ? d(p++ < v, "traverseParentPath(%s, %s, ...): Detected an infinite loop while traversing the React DOM ID tree. This may be due to malformed IDs: %s", e, t) : d(p++ < v) + } + } + var p = e("./ReactRootIndex"), + d = e("./invariant"), + f = ".", + h = f.length, + v = 100, + m = { + createReactRootID: function() { + return r(p.createReactRootIndex()) + }, + createReactID: function(e, t) { + return e + t + }, + getReactRootIDFromNodeID: function(e) { + if (e && e.charAt(0) === f && e.length > 1) { + var t = e.indexOf(f, 1); + return t > -1 ? e.substr(0, t) : e + } + return null + }, + traverseEnterLeave: function(e, t, n, r, o) { + var a = c(e, t); + a !== e && l(e, a, n, r, !1, !0), a !== t && l(a, t, n, o, !0, !1) + }, + traverseTwoPhase: function(e, t, n) { + e && (l("", e, t, n, !0, !1), l(e, "", t, n, !1, !0)) + }, + traverseAncestors: function(e, t, n) { + l("", e, t, n, !0, !1) + }, + _getFirstCommonAncestorID: c, + _getNextDescendantID: u, + isAncestorIDOf: i, + SEPARATOR: f + }; + t.exports = m + }).call(this, e("_process")) + }, { + "./ReactRootIndex": 97, + "./invariant": 157, + _process: 2 + }], + 79: [function(e, t) { + "use strict"; + var n = { + remove: function(e) { + e._reactInternalInstance = void 0 + }, + get: function(e) { + return e._reactInternalInstance + }, + has: function(e) { + return void 0 !== e._reactInternalInstance + }, + set: function(e, t) { + e._reactInternalInstance = t + } + }; + t.exports = n + }, {}], + 80: [function(e, t) { + "use strict"; + var n = { + currentlyMountingInstance: null, + currentlyUnmountingInstance: null + }; + t.exports = n + }, {}], + 81: [function(e, t) { + "use strict"; + + function n(e, t) { + this.value = e, this.requestChange = t + } + + function r(e) { + var t = { + value: "undefined" == typeof e ? o.PropTypes.any.isRequired : e.isRequired, + requestChange: o.PropTypes.func.isRequired + }; + return o.PropTypes.shape(t) + } + var o = e("./React"); + n.PropTypes = { + link: r + }, t.exports = n + }, { + "./React": 37 + }], + 82: [function(e, t) { + "use strict"; + var n = e("./adler32"), + r = { + CHECKSUM_ATTR_NAME: "data-react-checksum", + addChecksumToMarkup: function(e) { + var t = n(e); + return e.replace(">", " " + r.CHECKSUM_ATTR_NAME + '="' + t + '">') + }, + canReuseMarkup: function(e, t) { + var o = t.getAttribute(r.CHECKSUM_ATTR_NAME); + o = o && parseInt(o, 10); + var a = n(e); + return a === o + } + }; + t.exports = r + }, { + "./adler32": 126 + }], + 83: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + for (var n = Math.min(e.length, t.length), r = 0; n > r; r++) + if (e.charAt(r) !== t.charAt(r)) return r; + return e.length === t.length ? -1 : n + } + + function o(e) { + var t = P(e); + return t && Y.getID(t) + } + + function a(e) { + var t = i(e); + if (t) + if (j.hasOwnProperty(t)) { + var r = j[t]; + r !== e && ("production" !== n.env.NODE_ENV ? S(!l(r, t), "ReactMount: Two valid but unequal nodes with the same `%s`: %s", L, t) : S(!l(r, t)), j[t] = e) + } else j[t] = e; + return t + } + + function i(e) { + return e && e.getAttribute && e.getAttribute(L) || "" + } + + function s(e, t) { + var n = i(e); + n !== t && delete j[n], e.setAttribute(L, t), j[t] = e + } + + function u(e) { + return j.hasOwnProperty(e) && l(j[e], e) || (j[e] = Y.findReactNodeByID(e)), j[e] + } + + function c(e) { + var t = N.get(e)._rootNodeID; + return b.isNullComponentID(t) ? null : (j.hasOwnProperty(t) && l(j[t], t) || (j[t] = Y.findReactNodeByID(t)), j[t]) + } + + function l(e, t) { + if (e) { + "production" !== n.env.NODE_ENV ? S(i(e) === t, "ReactMount: Unexpected modification of `%s`", L) : S(i(e) === t); + var r = Y.findReactContainerForID(t); + if (r && T(r, e)) return !0 + } + return !1 + } + + function p(e) { + delete j[e] + } + + function d(e) { + var t = j[e]; + return t && l(t, e) ? void(z = t) : !1 + } + + function f(e) { + z = null, _.traverseAncestors(e, d); + var t = z; + return z = null, t + } + + function h(e, t, n, r, o) { + var a = D.mountComponent(e, t, r, x); + e._isTopLevel = !0, Y._mountImageIntoNode(a, n, o) + } + + function v(e, t, n, r) { + var o = M.ReactReconcileTransaction.getPooled(); + o.perform(h, null, e, t, n, o, r), M.ReactReconcileTransaction.release(o) + } + var m = e("./DOMProperty"), + y = e("./ReactBrowserEventEmitter"), + g = e("./ReactCurrentOwner"), + E = e("./ReactElement"), + C = e("./ReactElementValidator"), + b = e("./ReactEmptyComponent"), + _ = e("./ReactInstanceHandles"), + N = e("./ReactInstanceMap"), + O = e("./ReactMarkupChecksum"), + R = e("./ReactPerf"), + D = e("./ReactReconciler"), + w = e("./ReactUpdateQueue"), + M = e("./ReactUpdates"), + x = e("./emptyObject"), + T = e("./containsNode"), + P = e("./getReactRootElementInContainer"), + I = e("./instantiateReactComponent"), + S = e("./invariant"), + k = e("./setInnerHTML"), + A = e("./shouldUpdateReactComponent"), + V = e("./warning"), + U = _.SEPARATOR, + L = m.ID_ATTRIBUTE_NAME, + j = {}, + F = 1, + B = 9, + W = {}, + H = {}; + if ("production" !== n.env.NODE_ENV) var q = {}; + var K = [], + z = null, + Y = { + _instancesByReactRootID: W, + scrollMonitor: function(e, t) { + t() + }, + _updateRootComponent: function(e, t, r, a) { + return "production" !== n.env.NODE_ENV && C.checkAndWarnForMutatedProps(t), Y.scrollMonitor(r, function() { + w.enqueueElementInternal(e, t), a && w.enqueueCallbackInternal(e, a) + }), "production" !== n.env.NODE_ENV && (q[o(r)] = P(r)), e + }, + _registerComponent: function(e, t) { + "production" !== n.env.NODE_ENV ? S(t && (t.nodeType === F || t.nodeType === B), "_registerComponent(...): Target container is not a DOM element.") : S(t && (t.nodeType === F || t.nodeType === B)), y.ensureScrollValueMonitoring(); + var r = Y.registerContainer(t); + return W[r] = e, r + }, + _renderNewRootComponent: function(e, t, r) { + "production" !== n.env.NODE_ENV ? V(null == g.current, "_renderNewRootComponent(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null; + var o = I(e, null), + a = Y._registerComponent(o, t); + return M.batchedUpdates(v, o, a, t, r), "production" !== n.env.NODE_ENV && (q[a] = P(t)), o + }, + render: function(e, t, r) { + "production" !== n.env.NODE_ENV ? S(E.isValidElement(e), "React.render(): Invalid component element.%s", "string" == typeof e ? " Instead of passing an element string, make sure to instantiate it by passing it to React.createElement." : "function" == typeof e ? " Instead of passing a component class, make sure to instantiate it by passing it to React.createElement." : null != e && void 0 !== e.props ? " This may be caused by unintentionally loading two independent copies of React." : "") : S(E.isValidElement(e)); + var a = W[o(t)]; + if (a) { + var i = a._currentElement; + if (A(i, e)) return Y._updateRootComponent(a, e, t, r).getPublicInstance(); + Y.unmountComponentAtNode(t) + } + var s = P(t), + u = s && Y.isRenderedByReact(s); + if ("production" !== n.env.NODE_ENV && (!u || s.nextSibling)) + for (var c = s; c;) { + if (Y.isRenderedByReact(c)) { + "production" !== n.env.NODE_ENV ? V(!1, "render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup.") : null; + break + } + c = c.nextSibling + } + var l = u && !a, + p = Y._renderNewRootComponent(e, t, l).getPublicInstance(); + return r && r.call(p), p + }, + constructAndRenderComponent: function(e, t, n) { + var r = E.createElement(e, t); + return Y.render(r, n) + }, + constructAndRenderComponentByID: function(e, t, r) { + var o = document.getElementById(r); + return "production" !== n.env.NODE_ENV ? S(o, 'Tried to get element with id of "%s" but it is not present on the page.', r) : S(o), Y.constructAndRenderComponent(e, t, o) + }, + registerContainer: function(e) { + var t = o(e); + return t && (t = _.getReactRootIDFromNodeID(t)), t || (t = _.createReactRootID()), H[t] = e, t + }, + unmountComponentAtNode: function(e) { + "production" !== n.env.NODE_ENV ? V(null == g.current, "unmountComponentAtNode(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null, "production" !== n.env.NODE_ENV ? S(e && (e.nodeType === F || e.nodeType === B), "unmountComponentAtNode(...): Target container is not a DOM element.") : S(e && (e.nodeType === F || e.nodeType === B)); + var t = o(e), + r = W[t]; + return r ? (Y.unmountComponentFromNode(r, e), delete W[t], delete H[t], "production" !== n.env.NODE_ENV && delete q[t], !0) : !1 + }, + unmountComponentFromNode: function(e, t) { + for (D.unmountComponent(e), t.nodeType === B && (t = t.documentElement); t.lastChild;) t.removeChild(t.lastChild) + }, + findReactContainerForID: function(e) { + var t = _.getReactRootIDFromNodeID(e), + r = H[t]; + if ("production" !== n.env.NODE_ENV) { + var o = q[t]; + if (o && o.parentNode !== r) { + "production" !== n.env.NODE_ENV ? S(i(o) === t, "ReactMount: Root element ID differed from reactRootID.") : S(i(o) === t); + var a = r.firstChild; + a && t === i(a) ? q[t] = a : "production" !== n.env.NODE_ENV ? V(!1, "ReactMount: Root element has been removed from its original container. New container:", o.parentNode) : null + } + } + return r + }, + findReactNodeByID: function(e) { + var t = Y.findReactContainerForID(e); + return Y.findComponentRoot(t, e) + }, + isRenderedByReact: function(e) { + if (1 !== e.nodeType) return !1; + var t = Y.getID(e); + return t ? t.charAt(0) === U : !1 + }, + getFirstReactDOM: function(e) { + for (var t = e; t && t.parentNode !== t;) { + if (Y.isRenderedByReact(t)) return t; + t = t.parentNode + } + return null + }, + findComponentRoot: function(e, t) { + var r = K, + o = 0, + a = f(t) || e; + for (r[0] = a.firstChild, r.length = 1; o < r.length;) { + for (var i, s = r[o++]; s;) { + var u = Y.getID(s); + u ? t === u ? i = s : _.isAncestorIDOf(u, t) && (r.length = o = 0, r.push(s.firstChild)) : r.push(s.firstChild), s = s.nextSibling + } + if (i) return r.length = 0, i + } + r.length = 0, "production" !== n.env.NODE_ENV ? S(!1, "findComponentRoot(..., %s): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID `%s`.", t, Y.getID(e)) : S(!1) + }, + _mountImageIntoNode: function(e, t, o) { + if ("production" !== n.env.NODE_ENV ? S(t && (t.nodeType === F || t.nodeType === B), "mountComponentIntoNode(...): Target container is not valid.") : S(t && (t.nodeType === F || t.nodeType === B)), o) { + var a = P(t); + if (O.canReuseMarkup(e, a)) return; + var i = a.getAttribute(O.CHECKSUM_ATTR_NAME); + a.removeAttribute(O.CHECKSUM_ATTR_NAME); + var s = a.outerHTML; + a.setAttribute(O.CHECKSUM_ATTR_NAME, i); + var u = r(e, s), + c = " (client) " + e.substring(u - 20, u + 20) + "\n (server) " + s.substring(u - 20, u + 20); + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side:\n%s", c) : S(t.nodeType !== B), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? V(!1, "React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:\n%s", c) : null) + } + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document but you didn't use server rendering. We can't do this without using server rendering due to cross-browser quirks. See React.renderToString() for server rendering.") : S(t.nodeType !== B), k(t, e) + }, + getReactRootID: o, + getID: a, + setID: s, + getNode: u, + getNodeFromInstance: c, + purgeID: p + }; + R.measureMethods(Y, "ReactMount", { + _renderNewRootComponent: "_renderNewRootComponent", + _mountImageIntoNode: "_mountImageIntoNode" + }), t.exports = Y + }).call(this, e("_process")) + }, { + "./DOMProperty": 17, + "./ReactBrowserEventEmitter": 39, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMarkupChecksum": 82, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./ReactUpdateQueue": 105, + "./ReactUpdates": 106, + "./containsNode": 130, + "./emptyObject": 137, + "./getReactRootElementInContainer": 151, + "./instantiateReactComponent": 156, + "./invariant": 157, + "./setInnerHTML": 171, + "./shouldUpdateReactComponent": 174, + "./warning": 178, + _process: 2 + }], + 84: [function(e, t) { + "use strict"; + + function n(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.INSERT_MARKUP, + markupIndex: h.push(t) - 1, + textContent: null, + fromIndex: null, + toIndex: n + }) + } + + function r(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.MOVE_EXISTING, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: n + }) + } + + function o(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.REMOVE_NODE, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: null + }) + } + + function a(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.TEXT_CONTENT, + markupIndex: null, + textContent: t, + fromIndex: null, + toIndex: null + }) + } + + function i() { + f.length && (u.processChildrenUpdates(f, h), s()) + } + + function s() { + f.length = 0, h.length = 0 + } + var u = e("./ReactComponentEnvironment"), + c = e("./ReactMultiChildUpdateTypes"), + l = e("./ReactReconciler"), + p = e("./ReactChildReconciler"), + d = 0, + f = [], + h = [], + v = { + Mixin: { + mountChildren: function(e, t, n) { + var r = p.instantiateChildren(e, t, n); + this._renderedChildren = r; + var o = [], + a = 0; + for (var i in r) + if (r.hasOwnProperty(i)) { + var s = r[i], + u = this._rootNodeID + i, + c = l.mountComponent(s, u, t, n); + s._mountIndex = a, o.push(c), a++ + } return o + }, + updateTextContent: function(e) { + d++; + var t = !0; + try { + var n = this._renderedChildren; + p.unmountChildren(n); + for (var r in n) n.hasOwnProperty(r) && this._unmountChildByName(n[r], r); + this.setTextContent(e), t = !1 + } finally { + d--, d || (t ? s() : i()) + } + }, + updateChildren: function(e, t, n) { + d++; + var r = !0; + try { + this._updateChildren(e, t, n), r = !1 + } finally { + d--, d || (r ? s() : i()) + } + }, + _updateChildren: function(e, t, n) { + var r = this._renderedChildren, + o = p.updateChildren(r, e, t, n); + if (this._renderedChildren = o, o || r) { + var a, i = 0, + s = 0; + for (a in o) + if (o.hasOwnProperty(a)) { + var u = r && r[a], + c = o[a]; + u === c ? (this.moveChild(u, s, i), i = Math.max(u._mountIndex, i), u._mountIndex = s) : (u && (i = Math.max(u._mountIndex, i), this._unmountChildByName(u, a)), this._mountChildByNameAtIndex(c, a, s, t, n)), s++ + } for (a in r) !r.hasOwnProperty(a) || o && o.hasOwnProperty(a) || this._unmountChildByName(r[a], a) + } + }, + unmountChildren: function() { + var e = this._renderedChildren; + p.unmountChildren(e), this._renderedChildren = null + }, + moveChild: function(e, t, n) { + e._mountIndex < n && r(this._rootNodeID, e._mountIndex, t) + }, + createChild: function(e, t) { + n(this._rootNodeID, t, e._mountIndex) + }, + removeChild: function(e) { + o(this._rootNodeID, e._mountIndex) + }, + setTextContent: function(e) { + a(this._rootNodeID, e) + }, + _mountChildByNameAtIndex: function(e, t, n, r, o) { + var a = this._rootNodeID + t, + i = l.mountComponent(e, a, r, o); + e._mountIndex = n, this.createChild(e, i) + }, + _unmountChildByName: function(e) { + this.removeChild(e), e._mountIndex = null + } + } + }; + t.exports = v + }, { + "./ReactChildReconciler": 42, + "./ReactComponentEnvironment": 47, + "./ReactMultiChildUpdateTypes": 85, + "./ReactReconciler": 95 + }], + 85: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + INSERT_MARKUP: null, + MOVE_EXISTING: null, + REMOVE_NODE: null, + TEXT_CONTENT: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 86: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + if ("function" == typeof e.type) return e.type; + var t = e.type, + n = p[t]; + return null == n && (p[t] = n = c(t)), n + } + + function o(e) { + return "production" !== n.env.NODE_ENV ? u(l, "There is no registered component for the tag %s", e.type) : u(l), new l(e.type, e.props) + } + + function a(e) { + return new d(e) + } + + function i(e) { + return e instanceof d + } + var s = e("./Object.assign"), + u = e("./invariant"), + c = null, + l = null, + p = {}, + d = null, + f = { + injectGenericComponentClass: function(e) { + l = e + }, + injectTextComponentClass: function(e) { + d = e + }, + injectComponentClasses: function(e) { + s(p, e) + }, + injectAutoWrapper: function(e) { + c = e + } + }, + h = { + getComponentClassForElement: r, + createInternalComponent: o, + createInstanceForText: a, + isTextComponent: i, + injection: f + }; + t.exports = h + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./invariant": 157, + _process: 2 + }], + 87: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + isValidOwner: function(e) { + return !(!e || "function" != typeof e.attachRef || "function" != typeof e.detachRef) + }, + addComponentAsRefTo: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.attachRef(t, e) + }, + removeComponentAsRefFrom: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This usually means that you're trying to remove a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.getPublicInstance().refs[t] === e.getPublicInstance() && a.detachRef(t) + } + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 88: [function(e, t) { + (function(e) { + "use strict"; + + function n(e, t, n) { + return n + } + var r = { + enableMeasure: !1, + storedMeasure: n, + measureMethods: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) + for (var a in o) o.hasOwnProperty(a) && (t[a] = r.measure(n, o[a], t[a])) + }, + measure: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) { + var a = null, + i = function() { + return r.enableMeasure ? (a || (a = r.storedMeasure(t, n, o)), a.apply(this, arguments)) : o.apply(this, arguments) + }; + return i.displayName = t + "_" + n, i + } + return o + }, + injection: { + injectMeasure: function(e) { + r.storedMeasure = e + } + } + }; + t.exports = r + }).call(this, e("_process")) + }, { + _process: 2 + }], + 89: [function(e, t) { + "use strict"; + + function n(e) { + return function(t, n, r) { + t[n] = t.hasOwnProperty(n) ? e(t[n], r) : r + } + } + + function r(e, t) { + for (var n in t) + if (t.hasOwnProperty(n)) { + var r = u[n]; + r && u.hasOwnProperty(n) ? r(e, n, t[n]) : e.hasOwnProperty(n) || (e[n] = t[n]) + } return e + } + var o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./joinClasses"), + s = n(function(e, t) { + return o({}, t, e) + }), + u = { + children: a, + className: n(i), + style: s + }, + c = { + mergeProps: function(e, t) { + return r(o({}, e), t) + } + }; + t.exports = c + }, { + "./Object.assign": 35, + "./emptyFunction": 136, + "./joinClasses": 162 + }], + 90: [function(e, t) { + (function(e) { + "use strict"; + var n = {}; + "production" !== e.env.NODE_ENV && (n = { + prop: "prop", + context: "context", + childContext: "child context" + }), t.exports = n + }).call(this, e("_process")) + }, { + _process: 2 + }], + 91: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + prop: null, + context: null, + childContext: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 92: [function(e, t) { + "use strict"; + + function n(e) { + function t(t, n, r, o, a) { + if (o = o || C, null == n[r]) { + var i = g[a]; + return t ? new Error("Required " + i + " `" + r + "` was not specified in " + ("`" + o + "`.")) : null + } + return e(n, r, o, a) + } + var n = t.bind(null, !1); + return n.isRequired = t.bind(null, !0), n + } + + function r(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if (i !== e) { + var s = g[o], + u = v(a); + return new Error("Invalid " + s + " `" + n + "` of type `" + u + "` " + ("supplied to `" + r + "`, expected `" + e + "`.")) + } + return null + } + return n(t) + } + + function o() { + return n(E.thatReturns(null)) + } + + function a(e) { + function t(t, n, r, o) { + var a = t[n]; + if (!Array.isArray(a)) { + var i = g[o], + s = h(a); + return new Error("Invalid " + i + " `" + n + "` of type " + ("`" + s + "` supplied to `" + r + "`, expected an array.")) + } + for (var u = 0; u < a.length; u++) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } + return null + } + return n(t) + } + + function i() { + function e(e, t, n, r) { + if (!m.isValidElement(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactElement.")) + } + return null + } + return n(e) + } + + function s(e) { + function t(t, n, r, o) { + if (!(t[n] instanceof e)) { + var a = g[o], + i = e.name || C; + return new Error("Invalid " + a + " `" + n + "` supplied to " + ("`" + r + "`, expected instance of `" + i + "`.")) + } + return null + } + return n(t) + } + + function u(e) { + function t(t, n, r, o) { + for (var a = t[n], i = 0; i < e.length; i++) + if (a === e[i]) return null; + var s = g[o], + u = JSON.stringify(e); + return new Error("Invalid " + s + " `" + n + "` of value `" + a + "` " + ("supplied to `" + r + "`, expected one of " + u + ".")) + } + return n(t) + } + + function c(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type " + ("`" + i + "` supplied to `" + r + "`, expected an object.")) + } + for (var u in a) + if (a.hasOwnProperty(u)) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } return null + } + return n(t) + } + + function l(e) { + function t(t, n, r, o) { + for (var a = 0; a < e.length; a++) { + var i = e[a]; + if (null == i(t, n, r, o)) return null + } + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` supplied to " + ("`" + r + "`.")) + } + return n(t) + } + + function p() { + function e(e, t, n, r) { + if (!f(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactNode.")) + } + return null + } + return n(e) + } + + function d(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type `" + i + "` " + ("supplied to `" + r + "`, expected `object`.")) + } + for (var u in e) { + var c = e[u]; + if (c) { + var l = c(a, u, r, o); + if (l) return l + } + } + return null + } + return n(t) + } + + function f(e) { + switch (typeof e) { + case "number": + case "string": + case "undefined": + return !0; + case "boolean": + return !e; + case "object": + if (Array.isArray(e)) return e.every(f); + if (null === e || m.isValidElement(e)) return !0; + e = y.extractIfFragment(e); + for (var t in e) + if (!f(e[t])) return !1; + return !0; + default: + return !1 + } + } + + function h(e) { + var t = typeof e; + return Array.isArray(e) ? "array" : e instanceof RegExp ? "object" : t + } + + function v(e) { + var t = h(e); + if ("object" === t) { + if (e instanceof Date) return "date"; + if (e instanceof RegExp) return "regexp" + } + return t + } + var m = e("./ReactElement"), + y = e("./ReactFragment"), + g = e("./ReactPropTypeLocationNames"), + E = e("./emptyFunction"), + C = "<>", + b = i(), + _ = p(), + N = { + array: r("array"), + bool: r("boolean"), + func: r("function"), + number: r("number"), + object: r("object"), + string: r("string"), + any: o(), + arrayOf: a, + element: b, + instanceOf: s, + node: _, + objectOf: c, + oneOf: u, + oneOfType: l, + shape: d + }; + t.exports = N + }, { + "./ReactElement": 69, + "./ReactFragment": 75, + "./ReactPropTypeLocationNames": 90, + "./emptyFunction": 136 + }], + 93: [function(e, t) { + "use strict"; + + function n() { + this.listenersToPut = [] + } + var r = e("./PooledClass"), + o = e("./ReactBrowserEventEmitter"), + a = e("./Object.assign"); + a(n.prototype, { + enqueuePutListener: function(e, t, n) { + this.listenersToPut.push({ + rootNodeID: e, + propKey: t, + propValue: n + }) + }, + putListeners: function() { + for (var e = 0; e < this.listenersToPut.length; e++) { + var t = this.listenersToPut[e]; + o.putListener(t.rootNodeID, t.propKey, t.propValue) + } + }, + reset: function() { + this.listenersToPut.length = 0 + }, + destructor: function() { + this.reset() + } + }), r.addPoolingTo(n), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39 + }], + 94: [function(e, t) { + "use strict"; + + function n() { + this.reinitializeTransaction(), this.renderToStaticMarkup = !1, this.reactMountReady = r.getPooled(null), this.putListenerQueue = s.getPooled() + } + var r = e("./CallbackQueue"), + o = e("./PooledClass"), + a = e("./ReactBrowserEventEmitter"), + i = e("./ReactInputSelection"), + s = e("./ReactPutListenerQueue"), + u = e("./Transaction"), + c = e("./Object.assign"), + l = { + initialize: i.getSelectionInformation, + close: i.restoreSelection + }, + p = { + initialize: function() { + var e = a.isEnabled(); + return a.setEnabled(!1), e + }, + close: function(e) { + a.setEnabled(e) + } + }, + d = { + initialize: function() { + this.reactMountReady.reset() + }, + close: function() { + this.reactMountReady.notifyAll() + } + }, + f = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: function() { + this.putListenerQueue.putListeners() + } + }, + h = [f, l, p, d], + v = { + getTransactionWrappers: function() { + return h + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + r.release(this.reactMountReady), this.reactMountReady = null, s.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + c(n.prototype, u.Mixin, v), o.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39, + "./ReactInputSelection": 77, + "./ReactPutListenerQueue": 93, + "./Transaction": 123 + }], + 95: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + o.attachRefs(this, this._currentElement) + } + var o = e("./ReactRef"), + a = e("./ReactElementValidator"), + i = { + mountComponent: function(e, t, o, i) { + var s = e.mountComponent(t, o, i); + return "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(e._currentElement), o.getReactMountReady().enqueue(r, e), s + }, + unmountComponent: function(e) { + o.detachRefs(e, e._currentElement), e.unmountComponent() + }, + receiveComponent: function(e, t, i, s) { + var u = e._currentElement; + if (t !== u || null == t._owner) { + "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(t); + var c = o.shouldUpdateRefs(u, t); + c && o.detachRefs(e, u), e.receiveComponent(t, i, s), c && i.getReactMountReady().enqueue(r, e) + } + }, + performUpdateIfNecessary: function(e, t) { + e.performUpdateIfNecessary(t) + } + }; + t.exports = i + }).call(this, e("_process")) + }, { + "./ReactElementValidator": 70, + "./ReactRef": 96, + _process: 2 + }], + 96: [function(e, t) { + "use strict"; + + function n(e, t, n) { + "function" == typeof e ? e(t.getPublicInstance()) : o.addComponentAsRefTo(t, e, n) + } + + function r(e, t, n) { + "function" == typeof e ? e(null) : o.removeComponentAsRefFrom(t, e, n) + } + var o = e("./ReactOwner"), + a = {}; + a.attachRefs = function(e, t) { + var r = t.ref; + null != r && n(r, e, t._owner) + }, a.shouldUpdateRefs = function(e, t) { + return t._owner !== e._owner || t.ref !== e.ref + }, a.detachRefs = function(e, t) { + var n = t.ref; + null != n && r(n, e, t._owner) + }, t.exports = a + }, { + "./ReactOwner": 87 + }], + 97: [function(e, t) { + "use strict"; + var n = { + injectCreateReactRootIndex: function(e) { + r.createReactRootIndex = e + } + }, + r = { + createReactRootIndex: null, + injection: n + }; + t.exports = r + }, {}], + 98: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToString(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!1), t.perform(function() { + var n = l(e, null), + o = n.mountComponent(r, t, c); + return s.addChecksumToMarkup(o) + }, null) + } finally { + u.release(t) + } + } + + function o(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToStaticMarkup(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!0), t.perform(function() { + var n = l(e, null); + return n.mountComponent(r, t, c) + }, null) + } finally { + u.release(t) + } + } + var a = e("./ReactElement"), + i = e("./ReactInstanceHandles"), + s = e("./ReactMarkupChecksum"), + u = e("./ReactServerRenderingTransaction"), + c = e("./emptyObject"), + l = e("./instantiateReactComponent"), + p = e("./invariant"); + t.exports = { + renderToString: r, + renderToStaticMarkup: o + } + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactInstanceHandles": 78, + "./ReactMarkupChecksum": 82, + "./ReactServerRenderingTransaction": 99, + "./emptyObject": 137, + "./instantiateReactComponent": 156, + "./invariant": 157, + _process: 2 + }], + 99: [function(e, t) { + "use strict"; + + function n(e) { + this.reinitializeTransaction(), this.renderToStaticMarkup = e, this.reactMountReady = o.getPooled(null), this.putListenerQueue = a.getPooled() + } + var r = e("./PooledClass"), + o = e("./CallbackQueue"), + a = e("./ReactPutListenerQueue"), + i = e("./Transaction"), + s = e("./Object.assign"), + u = e("./emptyFunction"), + c = { + initialize: function() { + this.reactMountReady.reset() + }, + close: u + }, + l = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: u + }, + p = [l, c], + d = { + getTransactionWrappers: function() { + return p + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + o.release(this.reactMountReady), this.reactMountReady = null, a.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + s(n.prototype, i.Mixin, d), r.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactPutListenerQueue": 93, + "./Transaction": 123, + "./emptyFunction": 136 + }], + 100: [function(e, t) { + "use strict"; + + function n(e, t) { + var n = {}; + return function(r) { + n[t] = r, e.setState(n) + } + } + var r = { + createStateSetter: function(e, t) { + return function(n, r, o, a, i, s) { + var u = t.call(e, n, r, o, a, i, s); + u && e.setState(u) + } + }, + createStateKeySetter: function(e, t) { + var r = e.__keySetters || (e.__keySetters = {}); + return r[t] || (r[t] = n(e, t)) + } + }; + r.Mixin = { + createStateSetter: function(e) { + return r.createStateSetter(this, e) + }, + createStateKeySetter: function(e) { + return r.createStateKeySetter(this, e) + } + }, t.exports = r + }, {}], + 101: [function(e, t) { + "use strict"; + + function n() {} + + function r(e) { + return function(t, r) { + var o; + b.isDOMComponent(t) ? o = t.getDOMNode() : t.tagName && (o = t); + var a = new n; + a.target = o; + var i = new g(d.eventNameDispatchConfigs[e], m.getID(o), a); + E(i, r), u.accumulateTwoPhaseDispatches(i), y.batchedUpdates(function() { + s.enqueueEvents(i), s.processEventQueue() + }) + } + } + + function o() { + b.Simulate = {}; + var e; + for (e in d.eventNameDispatchConfigs) b.Simulate[e] = r(e) + } + + function a(e) { + return function(t, r) { + var o = new n(e); + E(o, r), b.isDOMComponent(t) ? b.simulateNativeEventOnDOMComponent(e, t, o) : t.tagName && b.simulateNativeEventOnNode(e, t, o) + } + } + var i = e("./EventConstants"), + s = e("./EventPluginHub"), + u = e("./EventPropagators"), + c = e("./React"), + l = e("./ReactElement"), + p = e("./ReactEmptyComponent"), + d = e("./ReactBrowserEventEmitter"), + f = e("./ReactCompositeComponent"), + h = e("./ReactInstanceHandles"), + v = e("./ReactInstanceMap"), + m = e("./ReactMount"), + y = e("./ReactUpdates"), + g = e("./SyntheticEvent"), + E = e("./Object.assign"), + C = i.topLevelTypes, + b = { + renderIntoDocument: function(e) { + var t = document.createElement("div"); + return c.render(e, t) + }, + isElement: function(e) { + return l.isValidElement(e) + }, + isElementOfType: function(e, t) { + return l.isValidElement(e) && e.type === t + }, + isDOMComponent: function(e) { + return !!(e && e.tagName && e.getDOMNode) + }, + isDOMComponentElement: function(e) { + return !!(e && l.isValidElement(e) && e.tagName) + }, + isCompositeComponent: function(e) { + return "function" == typeof e.render && "function" == typeof e.setState + }, + isCompositeComponentWithType: function(e, t) { + return !(!b.isCompositeComponent(e) || e.constructor !== t) + }, + isCompositeComponentElement: function(e) { + if (!l.isValidElement(e)) return !1; + var t = e.type.prototype; + return "function" == typeof t.render && "function" == typeof t.setState + }, + isCompositeComponentElementWithType: function(e, t) { + return !(!b.isCompositeComponentElement(e) || e.constructor !== t) + }, + getRenderedChildOfCompositeComponent: function(e) { + if (!b.isCompositeComponent(e)) return null; + var t = v.get(e); + return t._renderedComponent.getPublicInstance() + }, + findAllInRenderedTree: function(e, t) { + if (!e) return []; + var n = t(e) ? [e] : []; + if (b.isDOMComponent(e)) { + var r, o = v.get(e), + a = o._renderedComponent._renderedChildren; + for (r in a) a.hasOwnProperty(r) && a[r].getPublicInstance && (n = n.concat(b.findAllInRenderedTree(a[r].getPublicInstance(), t))) + } else b.isCompositeComponent(e) && (n = n.concat(b.findAllInRenderedTree(b.getRenderedChildOfCompositeComponent(e), t))); + return n + }, + scryRenderedDOMComponentsWithClass: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + var n = e.props.className; + return b.isDOMComponent(e) && n && -1 !== (" " + n + " ").indexOf(" " + t + " ") + }) + }, + findRenderedDOMComponentWithClass: function(e, t) { + var n = b.scryRenderedDOMComponentsWithClass(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match (found: " + n.length + ") for class:" + t); + return n[0] + }, + scryRenderedDOMComponentsWithTag: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isDOMComponent(e) && e.tagName === t.toUpperCase() + }) + }, + findRenderedDOMComponentWithTag: function(e, t) { + var n = b.scryRenderedDOMComponentsWithTag(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for tag:" + t); + return n[0] + }, + scryRenderedComponentsWithType: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isCompositeComponentWithType(e, t) + }) + }, + findRenderedComponentWithType: function(e, t) { + var n = b.scryRenderedComponentsWithType(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for componentType:" + t); + return n[0] + }, + mockComponent: function(e, t) { + return t = t || e.mockTagName || "div", e.prototype.render.mockImplementation(function() { + return c.createElement(t, null, this.props.children) + }), this + }, + simulateNativeEventOnNode: function(e, t, n) { + n.target = t, d.ReactEventListener.dispatchEvent(e, n) + }, + simulateNativeEventOnDOMComponent: function(e, t, n) { + b.simulateNativeEventOnNode(e, t.getDOMNode(), n) + }, + nativeTouchData: function(e, t) { + return { + touches: [{ + pageX: e, + pageY: t + }] + } + }, + createRenderer: function() { + return new _ + }, + Simulate: null, + SimulateNative: {} + }, + _ = function() { + this._instance = null + }; + _.prototype.getRenderOutput = function() { + return this._instance && this._instance._renderedComponent && this._instance._renderedComponent._renderedOutput || null + }; + var N = function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }; + N.prototype = { + mountComponent: function() {}, + receiveComponent: function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }, + unmountComponent: function() {} + }; + var O = function() {}; + E(O.prototype, f.Mixin, { + _instantiateReactComponent: function(e) { + return new N(e) + }, + _replaceNodeWithMarkupByID: function() {}, + _renderValidatedComponent: f.Mixin._renderValidatedComponentWithoutOwnerOrContext + }), _.prototype.render = function(e, t) { + var n = y.ReactReconcileTransaction.getPooled(); + this._render(e, n, t), y.ReactReconcileTransaction.release(n) + }, _.prototype.unmount = function() { + this._instance && this._instance.unmountComponent() + }, _.prototype._render = function(e, t, n) { + if (this._instance) this._instance.receiveComponent(e, t, n); + else { + var r = h.createReactRootID(), + o = new O(e.type); + o.construct(e), o.mountComponent(r, t, n), this._instance = o + } + }; + var R = s.injection.injectEventPluginOrder; + s.injection.injectEventPluginOrder = function() { + R.apply(this, arguments), o() + }; + var D = s.injection.injectEventPluginsByName; + s.injection.injectEventPluginsByName = function() { + D.apply(this, arguments), o() + }, o(); + var w; + for (w in C) { + var M = 0 === w.indexOf("top") ? w.charAt(3).toLowerCase() + w.substr(4) : w; + b.SimulateNative[M] = a(w) + } + t.exports = b + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPropagators": 27, + "./Object.assign": 35, + "./React": 37, + "./ReactBrowserEventEmitter": 39, + "./ReactCompositeComponent": 49, + "./ReactElement": 69, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./SyntheticEvent": 115 + }], + 102: [function(e, t) { + "use strict"; + var n = e("./ReactChildren"), + r = e("./ReactFragment"), + o = { + getChildMapping: function(e) { + return e ? r.extract(n.map(e, function(e) { + return e + })) : e + }, + mergeChildMappings: function(e, t) { + function n(n) { + return t.hasOwnProperty(n) ? t[n] : e[n] + } + e = e || {}, t = t || {}; + var r = {}, + o = []; + for (var a in e) t.hasOwnProperty(a) ? o.length && (r[a] = o, o = []) : o.push(a); + var i, s = {}; + for (var u in t) { + if (r.hasOwnProperty(u)) + for (i = 0; i < r[u].length; i++) { + var c = r[u][i]; + s[r[u][i]] = n(c) + } + s[u] = n(u) + } + for (i = 0; i < o.length; i++) s[o[i]] = n(o[i]); + return s + } + }; + t.exports = o + }, { + "./ReactChildren": 43, + "./ReactFragment": 75 + }], + 103: [function(e, t) { + "use strict"; + + function n() { + var e = document.createElement("div"), + t = e.style; + "AnimationEvent" in window || delete i.animationend.animation, "TransitionEvent" in window || delete i.transitionend.transition; + for (var n in i) { + var r = i[n]; + for (var o in r) + if (o in t) { + s.push(r[o]); + break + } + } + } + + function r(e, t, n) { + e.addEventListener(t, n, !1) + } + + function o(e, t, n) { + e.removeEventListener(t, n, !1) + } + var a = e("./ExecutionEnvironment"), + i = { + transitionend: { + transition: "transitionend", + WebkitTransition: "webkitTransitionEnd", + MozTransition: "mozTransitionEnd", + OTransition: "oTransitionEnd", + msTransition: "MSTransitionEnd" + }, + animationend: { + animation: "animationend", + WebkitAnimation: "webkitAnimationEnd", + MozAnimation: "mozAnimationEnd", + OAnimation: "oAnimationEnd", + msAnimation: "MSAnimationEnd" + } + }, + s = []; + a.canUseDOM && n(); + var u = { + addEndEventListener: function(e, t) { + return 0 === s.length ? void window.setTimeout(t, 0) : void s.forEach(function(n) { + r(e, n, t) + }) + }, + removeEndEventListener: function(e, t) { + 0 !== s.length && s.forEach(function(n) { + o(e, n, t) + }) + } + }; + t.exports = u + }, { + "./ExecutionEnvironment": 28 + }], + 104: [function(e, t) { + "use strict"; + var n = e("./React"), + r = e("./ReactTransitionChildMapping"), + o = e("./Object.assign"), + a = e("./cloneWithProps"), + i = e("./emptyFunction"), + s = n.createClass({ + displayName: "ReactTransitionGroup", + propTypes: { + component: n.PropTypes.any, + childFactory: n.PropTypes.func + }, + getDefaultProps: function() { + return { + component: "span", + childFactory: i.thatReturnsArgument + } + }, + getInitialState: function() { + return { + children: r.getChildMapping(this.props.children) + } + }, + componentWillMount: function() { + this.currentlyTransitioningKeys = {}, this.keysToEnter = [], this.keysToLeave = [] + }, + componentDidMount: function() { + var e = this.state.children; + for (var t in e) e[t] && this.performAppear(t) + }, + componentWillReceiveProps: function(e) { + var t = r.getChildMapping(e.children), + n = this.state.children; + this.setState({ + children: r.mergeChildMappings(n, t) + }); + var o; + for (o in t) { + var a = n && n.hasOwnProperty(o); + !t[o] || a || this.currentlyTransitioningKeys[o] || this.keysToEnter.push(o) + } + for (o in n) { + var i = t && t.hasOwnProperty(o); + !n[o] || i || this.currentlyTransitioningKeys[o] || this.keysToLeave.push(o) + } + }, + componentDidUpdate: function() { + var e = this.keysToEnter; + this.keysToEnter = [], e.forEach(this.performEnter); + var t = this.keysToLeave; + this.keysToLeave = [], t.forEach(this.performLeave) + }, + performAppear: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillAppear ? t.componentWillAppear(this._handleDoneAppearing.bind(this, e)) : this._handleDoneAppearing(e) + }, + _handleDoneAppearing: function(e) { + var t = this.refs[e]; + t.componentDidAppear && t.componentDidAppear(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performEnter: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillEnter ? t.componentWillEnter(this._handleDoneEntering.bind(this, e)) : this._handleDoneEntering(e) + }, + _handleDoneEntering: function(e) { + var t = this.refs[e]; + t.componentDidEnter && t.componentDidEnter(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performLeave: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillLeave ? t.componentWillLeave(this._handleDoneLeaving.bind(this, e)) : this._handleDoneLeaving(e) + }, + _handleDoneLeaving: function(e) { + var t = this.refs[e]; + t.componentDidLeave && t.componentDidLeave(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + if (n && n.hasOwnProperty(e)) this.performEnter(e); + else { + var a = o({}, this.state.children); + delete a[e], this.setState({ + children: a + }) + } + }, + render: function() { + var e = []; + for (var t in this.state.children) { + var r = this.state.children[t]; + r && e.push(a(this.props.childFactory(r), { + ref: t, + key: t + })) + } + return n.createElement(this.props.component, this.props, e) + } + }); + t.exports = s + }, { + "./Object.assign": 35, + "./React": 37, + "./ReactTransitionChildMapping": 102, + "./cloneWithProps": 129, + "./emptyFunction": 136 + }], + 105: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e !== a.currentlyMountingInstance && c.enqueueUpdate(e) + } + + function o(e, t) { + "production" !== n.env.NODE_ENV ? p(null == i.current, "%s(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.", t) : p(null == i.current); + var r = u.get(e); + return r ? r === a.currentlyUnmountingInstance ? null : r : ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? d(!t, "%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op.", t, t) : null), null) + } + var a = e("./ReactLifeCycle"), + i = e("./ReactCurrentOwner"), + s = e("./ReactElement"), + u = e("./ReactInstanceMap"), + c = e("./ReactUpdates"), + l = e("./Object.assign"), + p = e("./invariant"), + d = e("./warning"), + f = { + enqueueCallback: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t); + var i = o(e); + return i && i !== a.currentlyMountingInstance ? (i._pendingCallbacks ? i._pendingCallbacks.push(t) : i._pendingCallbacks = [t], void r(i)) : null + }, + enqueueCallbackInternal: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t), e._pendingCallbacks ? e._pendingCallbacks.push(t) : e._pendingCallbacks = [t], r(e) + }, + enqueueForceUpdate: function(e) { + var t = o(e, "forceUpdate"); + t && (t._pendingForceUpdate = !0, r(t)) + }, + enqueueReplaceState: function(e, t) { + var n = o(e, "replaceState"); + n && (n._pendingStateQueue = [t], n._pendingReplaceState = !0, r(n)) + }, + enqueueSetState: function(e, t) { + var n = o(e, "setState"); + if (n) { + var a = n._pendingStateQueue || (n._pendingStateQueue = []); + a.push(t), r(n) + } + }, + enqueueSetProps: function(e, t) { + var a = o(e, "setProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "setProps(...): You called `setProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement, + u = l({}, i.props, t); + a._pendingElement = s.cloneAndReplaceProps(i, u), r(a) + } + }, + enqueueReplaceProps: function(e, t) { + var a = o(e, "replaceProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "replaceProps(...): You called `replaceProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement; + a._pendingElement = s.cloneAndReplaceProps(i, t), r(a) + } + }, + enqueueElementInternal: function(e, t) { + e._pendingElement = t, r(e) + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactUpdates": 106, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 106: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + "production" !== n.env.NODE_ENV ? y(M.ReactReconcileTransaction && _, "ReactUpdates: must inject a reconcile transaction class and batching strategy") : y(M.ReactReconcileTransaction && _) + } + + function o() { + this.reinitializeTransaction(), this.dirtyComponentsLength = null, this.callbackQueue = l.getPooled(), this.reconcileTransaction = M.ReactReconcileTransaction.getPooled() + } + + function a(e, t, n, o, a) { + r(), _.batchedUpdates(e, t, n, o, a) + } + + function i(e, t) { + return e._mountOrder - t._mountOrder + } + + function s(e) { + var t = e.dirtyComponentsLength; + "production" !== n.env.NODE_ENV ? y(t === E.length, "Expected flush transaction's stored dirty-components length (%s) to match dirty-components array length (%s).", t, E.length) : y(t === E.length), E.sort(i); + for (var r = 0; t > r; r++) { + var o = E[r], + a = o._pendingCallbacks; + if (o._pendingCallbacks = null, h.performUpdateIfNecessary(o, e.reconcileTransaction), a) + for (var s = 0; s < a.length; s++) e.callbackQueue.enqueue(a[s], o.getPublicInstance()) + } + } + + function u(e) { + return r(), "production" !== n.env.NODE_ENV ? g(null == d.current, "enqueueUpdate(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null, _.isBatchingUpdates ? void E.push(e) : void _.batchedUpdates(u, e) + } + + function c(e, t) { + "production" !== n.env.NODE_ENV ? y(_.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched.") : y(_.isBatchingUpdates), C.enqueue(e, t), b = !0 + } + var l = e("./CallbackQueue"), + p = e("./PooledClass"), + d = e("./ReactCurrentOwner"), + f = e("./ReactPerf"), + h = e("./ReactReconciler"), + v = e("./Transaction"), + m = e("./Object.assign"), + y = e("./invariant"), + g = e("./warning"), + E = [], + C = l.getPooled(), + b = !1, + _ = null, + N = { + initialize: function() { + this.dirtyComponentsLength = E.length + }, + close: function() { + this.dirtyComponentsLength !== E.length ? (E.splice(0, this.dirtyComponentsLength), D()) : E.length = 0 + } + }, + O = { + initialize: function() { + this.callbackQueue.reset() + }, + close: function() { + this.callbackQueue.notifyAll() + } + }, + R = [N, O]; + m(o.prototype, v.Mixin, { + getTransactionWrappers: function() { + return R + }, + destructor: function() { + this.dirtyComponentsLength = null, l.release(this.callbackQueue), this.callbackQueue = null, M.ReactReconcileTransaction.release(this.reconcileTransaction), this.reconcileTransaction = null + }, + perform: function(e, t, n) { + return v.Mixin.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, e, t, n) + } + }), p.addPoolingTo(o); + var D = function() { + for (; E.length || b;) { + if (E.length) { + var e = o.getPooled(); + e.perform(s, null, e), o.release(e) + } + if (b) { + b = !1; + var t = C; + C = l.getPooled(), t.notifyAll(), l.release(t) + } + } + }; + D = f.measure("ReactUpdates", "flushBatchedUpdates", D); + var w = { + injectReconcileTransaction: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a reconcile transaction class") : y(e), M.ReactReconcileTransaction = e + }, + injectBatchingStrategy: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a batching strategy") : y(e), "production" !== n.env.NODE_ENV ? y("function" == typeof e.batchedUpdates, "ReactUpdates: must provide a batchedUpdates() function") : y("function" == typeof e.batchedUpdates), "production" !== n.env.NODE_ENV ? y("boolean" == typeof e.isBatchingUpdates, "ReactUpdates: must provide an isBatchingUpdates boolean attribute") : y("boolean" == typeof e.isBatchingUpdates), _ = e + } + }, + M = { + ReactReconcileTransaction: null, + batchedUpdates: a, + enqueueUpdate: u, + flushBatchedUpdates: D, + injection: w, + asap: c + }; + t.exports = M + }).call(this, e("_process")) + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactCurrentOwner": 51, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./Transaction": 123, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 107: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./LinkedStateMixin"), + o = e("./React"), + a = e("./ReactComponentWithPureRenderMixin"), + i = e("./ReactCSSTransitionGroup"), + s = e("./ReactFragment"), + u = e("./ReactTransitionGroup"), + c = e("./ReactUpdates"), + l = e("./cx"), + p = e("./cloneWithProps"), + d = e("./update"); + o.addons = { + CSSTransitionGroup: i, + LinkedStateMixin: r, + PureRenderMixin: a, + TransitionGroup: u, + batchedUpdates: c.batchedUpdates, + classSet: l, + cloneWithProps: p, + createFragment: s.create, + update: d + }, "production" !== n.env.NODE_ENV && (o.addons.Perf = e("./ReactDefaultPerf"), o.addons.TestUtils = e("./ReactTestUtils")), t.exports = o + }).call(this, e("_process")) + }, { + "./LinkedStateMixin": 31, + "./React": 37, + "./ReactCSSTransitionGroup": 40, + "./ReactComponentWithPureRenderMixin": 48, + "./ReactDefaultPerf": 67, + "./ReactFragment": 75, + "./ReactTestUtils": 101, + "./ReactTransitionGroup": 104, + "./ReactUpdates": 106, + "./cloneWithProps": 129, + "./cx": 134, + "./update": 177, + _process: 2 + }], + 108: [function(e, t) { + "use strict"; + var n = e("./DOMProperty"), + r = n.injection.MUST_USE_ATTRIBUTE, + o = { + Properties: { + cx: r, + cy: r, + d: r, + dx: r, + dy: r, + fill: r, + fillOpacity: r, + fontFamily: r, + fontSize: r, + fx: r, + fy: r, + gradientTransform: r, + gradientUnits: r, + markerEnd: r, + markerMid: r, + markerStart: r, + offset: r, + opacity: r, + patternContentUnits: r, + patternUnits: r, + points: r, + preserveAspectRatio: r, + r: r, + rx: r, + ry: r, + spreadMethod: r, + stopColor: r, + stopOpacity: r, + stroke: r, + strokeDasharray: r, + strokeLinecap: r, + strokeOpacity: r, + strokeWidth: r, + textAnchor: r, + transform: r, + version: r, + viewBox: r, + x1: r, + x2: r, + x: r, + y1: r, + y2: r, + y: r + }, + DOMAttributeNames: { + fillOpacity: "fill-opacity", + fontFamily: "font-family", + fontSize: "font-size", + gradientTransform: "gradientTransform", + gradientUnits: "gradientUnits", + markerEnd: "marker-end", + markerMid: "marker-mid", + markerStart: "marker-start", + patternContentUnits: "patternContentUnits", + patternUnits: "patternUnits", + preserveAspectRatio: "preserveAspectRatio", + spreadMethod: "spreadMethod", + stopColor: "stop-color", + stopOpacity: "stop-opacity", + strokeDasharray: "stroke-dasharray", + strokeLinecap: "stroke-linecap", + strokeOpacity: "stroke-opacity", + strokeWidth: "stroke-width", + textAnchor: "text-anchor", + viewBox: "viewBox" + } + }; + t.exports = o + }, { + "./DOMProperty": 17 + }], + 109: [function(e, t) { + "use strict"; + + function n(e) { + if ("selectionStart" in e && i.hasSelectionCapabilities(e)) return { + start: e.selectionStart, + end: e.selectionEnd + }; + if (window.getSelection) { + var t = window.getSelection(); + return { + anchorNode: t.anchorNode, + anchorOffset: t.anchorOffset, + focusNode: t.focusNode, + focusOffset: t.focusOffset + } + } + if (document.selection) { + var n = document.selection.createRange(); + return { + parentElement: n.parentElement(), + text: n.text, + top: n.boundingTop, + left: n.boundingLeft + } + } + } + + function r(e) { + if (y || null == h || h !== u()) return null; + var t = n(h); + if (!m || !p(m, t)) { + m = t; + var r = s.getPooled(f.select, v, e); + return r.type = "select", r.target = h, a.accumulateTwoPhaseDispatches(r), r + } + } + var o = e("./EventConstants"), + a = e("./EventPropagators"), + i = e("./ReactInputSelection"), + s = e("./SyntheticEvent"), + u = e("./getActiveElement"), + c = e("./isTextInputElement"), + l = e("./keyOf"), + p = e("./shallowEqual"), + d = o.topLevelTypes, + f = { + select: { + phasedRegistrationNames: { + bubbled: l({ + onSelect: null + }), + captured: l({ + onSelectCapture: null + }) + }, + dependencies: [d.topBlur, d.topContextMenu, d.topFocus, d.topKeyDown, d.topMouseDown, d.topMouseUp, d.topSelectionChange] + } + }, + h = null, + v = null, + m = null, + y = !1, + g = { + eventTypes: f, + extractEvents: function(e, t, n, o) { + switch (e) { + case d.topFocus: + (c(t) || "true" === t.contentEditable) && (h = t, v = n, m = null); + break; + case d.topBlur: + h = null, v = null, m = null; + break; + case d.topMouseDown: + y = !0; + break; + case d.topContextMenu: + case d.topMouseUp: + return y = !1, r(o); + case d.topSelectionChange: + case d.topKeyDown: + case d.topKeyUp: + return r(o) + } + } + }; + t.exports = g + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ReactInputSelection": 77, + "./SyntheticEvent": 115, + "./getActiveElement": 143, + "./isTextInputElement": 160, + "./keyOf": 164, + "./shallowEqual": 173 + }], + 110: [function(e, t) { + "use strict"; + var n = Math.pow(2, 53), + r = { + createReactRootIndex: function() { + return Math.ceil(Math.random() * n) + } + }; + t.exports = r + }, {}], + 111: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./EventConstants"), + o = e("./EventPluginUtils"), + a = e("./EventPropagators"), + i = e("./SyntheticClipboardEvent"), + s = e("./SyntheticEvent"), + u = e("./SyntheticFocusEvent"), + c = e("./SyntheticKeyboardEvent"), + l = e("./SyntheticMouseEvent"), + p = e("./SyntheticDragEvent"), + d = e("./SyntheticTouchEvent"), + f = e("./SyntheticUIEvent"), + h = e("./SyntheticWheelEvent"), + v = e("./getEventCharCode"), + m = e("./invariant"), + y = e("./keyOf"), + g = e("./warning"), + E = r.topLevelTypes, + C = { + blur: { + phasedRegistrationNames: { + bubbled: y({ + onBlur: !0 + }), + captured: y({ + onBlurCapture: !0 + }) + } + }, + click: { + phasedRegistrationNames: { + bubbled: y({ + onClick: !0 + }), + captured: y({ + onClickCapture: !0 + }) + } + }, + contextMenu: { + phasedRegistrationNames: { + bubbled: y({ + onContextMenu: !0 + }), + captured: y({ + onContextMenuCapture: !0 + }) + } + }, + copy: { + phasedRegistrationNames: { + bubbled: y({ + onCopy: !0 + }), + captured: y({ + onCopyCapture: !0 + }) + } + }, + cut: { + phasedRegistrationNames: { + bubbled: y({ + onCut: !0 + }), + captured: y({ + onCutCapture: !0 + }) + } + }, + doubleClick: { + phasedRegistrationNames: { + bubbled: y({ + onDoubleClick: !0 + }), + captured: y({ + onDoubleClickCapture: !0 + }) + } + }, + drag: { + phasedRegistrationNames: { + bubbled: y({ + onDrag: !0 + }), + captured: y({ + onDragCapture: !0 + }) + } + }, + dragEnd: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnd: !0 + }), + captured: y({ + onDragEndCapture: !0 + }) + } + }, + dragEnter: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnter: !0 + }), + captured: y({ + onDragEnterCapture: !0 + }) + } + }, + dragExit: { + phasedRegistrationNames: { + bubbled: y({ + onDragExit: !0 + }), + captured: y({ + onDragExitCapture: !0 + }) + } + }, + dragLeave: { + phasedRegistrationNames: { + bubbled: y({ + onDragLeave: !0 + }), + captured: y({ + onDragLeaveCapture: !0 + }) + } + }, + dragOver: { + phasedRegistrationNames: { + bubbled: y({ + onDragOver: !0 + }), + captured: y({ + onDragOverCapture: !0 + }) + } + }, + dragStart: { + phasedRegistrationNames: { + bubbled: y({ + onDragStart: !0 + }), + captured: y({ + onDragStartCapture: !0 + }) + } + }, + drop: { + phasedRegistrationNames: { + bubbled: y({ + onDrop: !0 + }), + captured: y({ + onDropCapture: !0 + }) + } + }, + focus: { + phasedRegistrationNames: { + bubbled: y({ + onFocus: !0 + }), + captured: y({ + onFocusCapture: !0 + }) + } + }, + input: { + phasedRegistrationNames: { + bubbled: y({ + onInput: !0 + }), + captured: y({ + onInputCapture: !0 + }) + } + }, + keyDown: { + phasedRegistrationNames: { + bubbled: y({ + onKeyDown: !0 + }), + captured: y({ + onKeyDownCapture: !0 + }) + } + }, + keyPress: { + phasedRegistrationNames: { + bubbled: y({ + onKeyPress: !0 + }), + captured: y({ + onKeyPressCapture: !0 + }) + } + }, + keyUp: { + phasedRegistrationNames: { + bubbled: y({ + onKeyUp: !0 + }), + captured: y({ + onKeyUpCapture: !0 + }) + } + }, + load: { + phasedRegistrationNames: { + bubbled: y({ + onLoad: !0 + }), + captured: y({ + onLoadCapture: !0 + }) + } + }, + error: { + phasedRegistrationNames: { + bubbled: y({ + onError: !0 + }), + captured: y({ + onErrorCapture: !0 + }) + } + }, + mouseDown: { + phasedRegistrationNames: { + bubbled: y({ + onMouseDown: !0 + }), + captured: y({ + onMouseDownCapture: !0 + }) + } + }, + mouseMove: { + phasedRegistrationNames: { + bubbled: y({ + onMouseMove: !0 + }), + captured: y({ + onMouseMoveCapture: !0 + }) + } + }, + mouseOut: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOut: !0 + }), + captured: y({ + onMouseOutCapture: !0 + }) + } + }, + mouseOver: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOver: !0 + }), + captured: y({ + onMouseOverCapture: !0 + }) + } + }, + mouseUp: { + phasedRegistrationNames: { + bubbled: y({ + onMouseUp: !0 + }), + captured: y({ + onMouseUpCapture: !0 + }) + } + }, + paste: { + phasedRegistrationNames: { + bubbled: y({ + onPaste: !0 + }), + captured: y({ + onPasteCapture: !0 + }) + } + }, + reset: { + phasedRegistrationNames: { + bubbled: y({ + onReset: !0 + }), + captured: y({ + onResetCapture: !0 + }) + } + }, + scroll: { + phasedRegistrationNames: { + bubbled: y({ + onScroll: !0 + }), + captured: y({ + onScrollCapture: !0 + }) + } + }, + submit: { + phasedRegistrationNames: { + bubbled: y({ + onSubmit: !0 + }), + captured: y({ + onSubmitCapture: !0 + }) + } + }, + touchCancel: { + phasedRegistrationNames: { + bubbled: y({ + onTouchCancel: !0 + }), + captured: y({ + onTouchCancelCapture: !0 + }) + } + }, + touchEnd: { + phasedRegistrationNames: { + bubbled: y({ + onTouchEnd: !0 + }), + captured: y({ + onTouchEndCapture: !0 + }) + } + }, + touchMove: { + phasedRegistrationNames: { + bubbled: y({ + onTouchMove: !0 + }), + captured: y({ + onTouchMoveCapture: !0 + }) + } + }, + touchStart: { + phasedRegistrationNames: { + bubbled: y({ + onTouchStart: !0 + }), + captured: y({ + onTouchStartCapture: !0 + }) + } + }, + wheel: { + phasedRegistrationNames: { + bubbled: y({ + onWheel: !0 + }), + captured: y({ + onWheelCapture: !0 + }) + } + } + }, + b = { + topBlur: C.blur, + topClick: C.click, + topContextMenu: C.contextMenu, + topCopy: C.copy, + topCut: C.cut, + topDoubleClick: C.doubleClick, + topDrag: C.drag, + topDragEnd: C.dragEnd, + topDragEnter: C.dragEnter, + topDragExit: C.dragExit, + topDragLeave: C.dragLeave, + topDragOver: C.dragOver, + topDragStart: C.dragStart, + topDrop: C.drop, + topError: C.error, + topFocus: C.focus, + topInput: C.input, + topKeyDown: C.keyDown, + topKeyPress: C.keyPress, + topKeyUp: C.keyUp, + topLoad: C.load, + topMouseDown: C.mouseDown, + topMouseMove: C.mouseMove, + topMouseOut: C.mouseOut, + topMouseOver: C.mouseOver, + topMouseUp: C.mouseUp, + topPaste: C.paste, + topReset: C.reset, + topScroll: C.scroll, + topSubmit: C.submit, + topTouchCancel: C.touchCancel, + topTouchEnd: C.touchEnd, + topTouchMove: C.touchMove, + topTouchStart: C.touchStart, + topWheel: C.wheel + }; + for (var _ in b) b[_].dependencies = [_]; + var N = { + eventTypes: C, + executeDispatch: function(e, t, r) { + var a = o.executeDispatch(e, t, r); + "production" !== n.env.NODE_ENV ? g("boolean" != typeof a, "Returning `false` from an event handler is deprecated and will be ignored in a future release. Instead, manually call e.stopPropagation() or e.preventDefault(), as appropriate.") : null, a === !1 && (e.stopPropagation(), e.preventDefault()) + }, + extractEvents: function(e, t, r, o) { + var y = b[e]; + if (!y) return null; + var g; + switch (e) { + case E.topInput: + case E.topLoad: + case E.topError: + case E.topReset: + case E.topSubmit: + g = s; + break; + case E.topKeyPress: + if (0 === v(o)) return null; + case E.topKeyDown: + case E.topKeyUp: + g = c; + break; + case E.topBlur: + case E.topFocus: + g = u; + break; + case E.topClick: + if (2 === o.button) return null; + case E.topContextMenu: + case E.topDoubleClick: + case E.topMouseDown: + case E.topMouseMove: + case E.topMouseOut: + case E.topMouseOver: + case E.topMouseUp: + g = l; + break; + case E.topDrag: + case E.topDragEnd: + case E.topDragEnter: + case E.topDragExit: + case E.topDragLeave: + case E.topDragOver: + case E.topDragStart: + case E.topDrop: + g = p; + break; + case E.topTouchCancel: + case E.topTouchEnd: + case E.topTouchMove: + case E.topTouchStart: + g = d; + break; + case E.topScroll: + g = f; + break; + case E.topWheel: + g = h; + break; + case E.topCopy: + case E.topCut: + case E.topPaste: + g = i + } + "production" !== n.env.NODE_ENV ? m(g, "SimpleEventPlugin: Unhandled event type, `%s`.", e) : m(g); + var C = g.getPooled(y, r, o); + return a.accumulateTwoPhaseDispatches(C), C + } + }; + t.exports = N + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./EventPluginUtils": 26, + "./EventPropagators": 27, + "./SyntheticClipboardEvent": 112, + "./SyntheticDragEvent": 114, + "./SyntheticEvent": 115, + "./SyntheticFocusEvent": 116, + "./SyntheticKeyboardEvent": 118, + "./SyntheticMouseEvent": 119, + "./SyntheticTouchEvent": 120, + "./SyntheticUIEvent": 121, + "./SyntheticWheelEvent": 122, + "./getEventCharCode": 144, + "./invariant": 157, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 112: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + clipboardData: function(e) { + return "clipboardData" in e ? e.clipboardData : window.clipboardData + } + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 113: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 114: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + dataTransfer: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 115: [function(e, t) { + "use strict"; + + function n(e, t, n) { + this.dispatchConfig = e, this.dispatchMarker = t, this.nativeEvent = n; + var r = this.constructor.Interface; + for (var o in r) + if (r.hasOwnProperty(o)) { + var i = r[o]; + this[o] = i ? i(n) : n[o] + } var s = null != n.defaultPrevented ? n.defaultPrevented : n.returnValue === !1; + this.isDefaultPrevented = s ? a.thatReturnsTrue : a.thatReturnsFalse, this.isPropagationStopped = a.thatReturnsFalse + } + var r = e("./PooledClass"), + o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./getEventTarget"), + s = { + type: null, + target: i, + currentTarget: a.thatReturnsNull, + eventPhase: null, + bubbles: null, + cancelable: null, + timeStamp: function(e) { + return e.timeStamp || Date.now() + }, + defaultPrevented: null, + isTrusted: null + }; + o(n.prototype, { + preventDefault: function() { + this.defaultPrevented = !0; + var e = this.nativeEvent; + e.preventDefault ? e.preventDefault() : e.returnValue = !1, this.isDefaultPrevented = a.thatReturnsTrue + }, + stopPropagation: function() { + var e = this.nativeEvent; + e.stopPropagation ? e.stopPropagation() : e.cancelBubble = !0, this.isPropagationStopped = a.thatReturnsTrue + }, + persist: function() { + this.isPersistent = a.thatReturnsTrue + }, + isPersistent: a.thatReturnsFalse, + destructor: function() { + var e = this.constructor.Interface; + for (var t in e) this[t] = null; + this.dispatchConfig = null, this.dispatchMarker = null, this.nativeEvent = null + } + }), n.Interface = s, n.augmentClass = function(e, t) { + var n = this, + a = Object.create(n.prototype); + o(a, e.prototype), e.prototype = a, e.prototype.constructor = e, e.Interface = o({}, n.Interface, t), e.augmentClass = n.augmentClass, r.addPoolingTo(e, r.threeArgumentPooler) + }, r.addPoolingTo(n, r.threeArgumentPooler), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./emptyFunction": 136, + "./getEventTarget": 147 + }], + 116: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = { + relatedTarget: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticUIEvent": 121 + }], + 117: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 118: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventCharCode"), + a = e("./getEventKey"), + i = e("./getEventModifierState"), + s = { + key: a, + location: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + repeat: null, + locale: null, + getModifierState: i, + charCode: function(e) { + return "keypress" === e.type ? o(e) : 0 + }, + keyCode: function(e) { + return "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + }, + which: function(e) { + return "keypress" === e.type ? o(e) : "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + } + }; + r.augmentClass(n, s), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventCharCode": 144, + "./getEventKey": 145, + "./getEventModifierState": 146 + }], + 119: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./ViewportMetrics"), + a = e("./getEventModifierState"), + i = { + screenX: null, + screenY: null, + clientX: null, + clientY: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + getModifierState: a, + button: function(e) { + var t = e.button; + return "which" in e ? t : 2 === t ? 2 : 4 === t ? 1 : 0 + }, + buttons: null, + relatedTarget: function(e) { + return e.relatedTarget || (e.fromElement === e.srcElement ? e.toElement : e.fromElement) + }, + pageX: function(e) { + return "pageX" in e ? e.pageX : e.clientX + o.currentScrollLeft + }, + pageY: function(e) { + return "pageY" in e ? e.pageY : e.clientY + o.currentScrollTop + } + }; + r.augmentClass(n, i), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./ViewportMetrics": 124, + "./getEventModifierState": 146 + }], + 120: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventModifierState"), + a = { + touches: null, + targetTouches: null, + changedTouches: null, + altKey: null, + metaKey: null, + ctrlKey: null, + shiftKey: null, + getModifierState: o + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventModifierState": 146 + }], + 121: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = e("./getEventTarget"), + a = { + view: function(e) { + if (e.view) return e.view; + var t = o(e); + if (null != t && t.window === t) return t; + var n = t.ownerDocument; + return n ? n.defaultView || n.parentWindow : window + }, + detail: function(e) { + return e.detail || 0 + } + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticEvent": 115, + "./getEventTarget": 147 + }], + 122: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + deltaX: function(e) { + return "deltaX" in e ? e.deltaX : "wheelDeltaX" in e ? -e.wheelDeltaX : 0 + }, + deltaY: function(e) { + return "deltaY" in e ? e.deltaY : "wheelDeltaY" in e ? -e.wheelDeltaY : "wheelDelta" in e ? -e.wheelDelta : 0 + }, + deltaZ: null, + deltaMode: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 123: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + reinitializeTransaction: function() { + this.transactionWrappers = this.getTransactionWrappers(), this.wrapperInitData ? this.wrapperInitData.length = 0 : this.wrapperInitData = [], this._isInTransaction = !1 + }, + _isInTransaction: !1, + getTransactionWrappers: null, + isInTransaction: function() { + return !!this._isInTransaction + }, + perform: function(e, t, o, a, i, s, u, c) { + "production" !== n.env.NODE_ENV ? r(!this.isInTransaction(), "Transaction.perform(...): Cannot initialize a transaction when there is already an outstanding transaction.") : r(!this.isInTransaction()); + var l, p; + try { + this._isInTransaction = !0, l = !0, this.initializeAll(0), p = e.call(t, o, a, i, s, u, c), l = !1 + } finally { + try { + if (l) try { + this.closeAll(0) + } catch (d) {} else this.closeAll(0) + } finally { + this._isInTransaction = !1 + } + } + return p + }, + initializeAll: function(e) { + for (var t = this.transactionWrappers, n = e; n < t.length; n++) { + var r = t[n]; + try { + this.wrapperInitData[n] = a.OBSERVED_ERROR, this.wrapperInitData[n] = r.initialize ? r.initialize.call(this) : null + } finally { + if (this.wrapperInitData[n] === a.OBSERVED_ERROR) try { + this.initializeAll(n + 1) + } catch (o) {} + } + } + }, + closeAll: function(e) { + "production" !== n.env.NODE_ENV ? r(this.isInTransaction(), "Transaction.closeAll(): Cannot close transaction when none are open.") : r(this.isInTransaction()); + for (var t = this.transactionWrappers, o = e; o < t.length; o++) { + var i, s = t[o], + u = this.wrapperInitData[o]; + try { + i = !0, u !== a.OBSERVED_ERROR && s.close && s.close.call(this, u), i = !1 + } finally { + if (i) try { + this.closeAll(o + 1) + } catch (c) {} + } + } + this.wrapperInitData.length = 0 + } + }, + a = { + Mixin: o, + OBSERVED_ERROR: {} + }; + t.exports = a + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 124: [function(e, t) { + "use strict"; + var n = { + currentScrollLeft: 0, + currentScrollTop: 0, + refreshScrollValues: function(e) { + n.currentScrollLeft = e.x, n.currentScrollTop = e.y + } + }; + t.exports = n + }, {}], + 125: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + if ("production" !== n.env.NODE_ENV ? o(null != t, "accumulateInto(...): Accumulated items must not be null or undefined.") : o(null != t), null == e) return t; + var r = Array.isArray(e), + a = Array.isArray(t); + return r && a ? (e.push.apply(e, t), e) : r ? (e.push(t), e) : a ? [e].concat(t) : [e, t] + } + var o = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 126: [function(e, t) { + "use strict"; + + function n(e) { + for (var t = 1, n = 0, o = 0; o < e.length; o++) t = (t + e.charCodeAt(o)) % r, n = (n + t) % r; + return t | n << 16 + } + var r = 65521; + t.exports = n + }, {}], + 127: [function(e, t) { + function n(e) { + return e.replace(r, function(e, t) { + return t.toUpperCase() + }) + } + var r = /-(.)/g; + t.exports = n + }, {}], + 128: [function(e, t) { + "use strict"; + + function n(e) { + return r(e.replace(o, "ms-")) + } + var r = e("./camelize"), + o = /^-ms-/; + t.exports = n + }, { + "./camelize": 127 + }], + 129: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? s(!e.ref, "You are calling cloneWithProps() on a child with a ref. This is dangerous because you're creating a new child which will not be added as a ref to its parent.") : null); + var r = a.mergeProps(t, e.props); + return !r.hasOwnProperty(u) && e.props.hasOwnProperty(u) && (r.children = e.props.children), o.createElement(e.type, r) + } + var o = e("./ReactElement"), + a = e("./ReactPropTransferer"), + i = e("./keyOf"), + s = e("./warning"), + u = i({ + children: null + }); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactPropTransferer": 89, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 130: [function(e, t) { + function n(e, t) { + return e && t ? e === t ? !0 : r(e) ? !1 : r(t) ? n(e, t.parentNode) : e.contains ? e.contains(t) : e.compareDocumentPosition ? !!(16 & e.compareDocumentPosition(t)) : !1 : !1 + } + var r = e("./isTextNode"); + t.exports = n + }, { + "./isTextNode": 161 + }], + 131: [function(e, t) { + function n(e) { + return !!e && ("object" == typeof e || "function" == typeof e) && "length" in e && !("setInterval" in e) && "number" != typeof e.nodeType && (Array.isArray(e) || "callee" in e || "item" in e) + } + + function r(e) { + return n(e) ? Array.isArray(e) ? e.slice() : o(e) : [e] + } + var o = e("./toArray"); + t.exports = r + }, { + "./toArray": 175 + }], + 132: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + var t = a.createFactory(e), + r = o.createClass({ + tagName: e.toUpperCase(), + displayName: "ReactFullPageComponent" + e, + componentWillUnmount: function() { + "production" !== n.env.NODE_ENV ? i(!1, "%s tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg , , and ) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.", this.constructor.displayName) : i(!1) + }, + render: function() { + return t(this.props) + } + }); + return r + } + var o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactClass": 44, + "./ReactElement": 69, + "./invariant": 157, + _process: 2 + }], + 133: [function(e, t) { + (function(n) { + function r(e) { + var t = e.match(l); + return t && t[1].toLowerCase() + } + + function o(e, t) { + var o = c; + "production" !== n.env.NODE_ENV ? u(!!c, "createNodesFromMarkup dummy not initialized") : u(!!c); + var a = r(e), + l = a && s(a); + if (l) { + o.innerHTML = l[1] + e + l[2]; + for (var p = l[0]; p--;) o = o.lastChild + } else o.innerHTML = e; + var d = o.getElementsByTagName("script"); + d.length && ("production" !== n.env.NODE_ENV ? u(t, "createNodesFromMarkup(...): Unexpected + + diff --git a/DS4Windows/DS4Control/ControlSerivce.cs b/DS4Windows/DS4Control/ControlSerivce.cs deleted file mode 100644 index 28759a73fc..0000000000 --- a/DS4Windows/DS4Control/ControlSerivce.cs +++ /dev/null @@ -1,851 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using System.IO; -using System.Reflection; -using System.Media; -using System.Threading.Tasks; -using static DS4Windows.Global; -namespace DS4Windows -{ - public class ControlService - { - public X360Device x360Bus; - public DS4Device[] DS4Controllers = new DS4Device[4]; - public Mouse[] touchPad = new Mouse[4]; - private bool running = false; - private DS4State[] MappedState = new DS4State[4]; - private DS4State[] CurrentState = new DS4State[4]; - private DS4State[] PreviousState = new DS4State[4]; - public DS4StateExposed[] ExposedState = new DS4StateExposed[4]; - public bool recordingMacro = false; - public event EventHandler Debug = null; - public bool eastertime = false; - private int eCode = 0; - bool[] buttonsdown = { false, false, false, false }; - List dcs = new List(); - bool[] held = new bool[4]; - int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - SoundPlayer sp = new SoundPlayer(); - - private class X360Data - { - public byte[] Report = new byte[28]; - public byte[] Rumble = new byte[8]; - } - private X360Data[] processingData = new X360Data[4]; - - public ControlService() - { - sp.Stream = Properties.Resources.EE; - x360Bus = new X360Device(); - AddtoDS4List(); - for (int i = 0; i < DS4Controllers.Length; i++) - { - processingData[i] = new X360Data(); - MappedState[i] = new DS4State(); - CurrentState[i] = new DS4State(); - PreviousState[i] = new DS4State(); - ExposedState[i] = new DS4StateExposed(CurrentState[i]); - } - } - - void AddtoDS4List() - { - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Circle); - dcs.Add(DS4Controls.Square); - dcs.Add(DS4Controls.Triangle); - dcs.Add(DS4Controls.Options); - dcs.Add(DS4Controls.Share); - dcs.Add(DS4Controls.DpadUp); - dcs.Add(DS4Controls.DpadDown); - dcs.Add(DS4Controls.DpadLeft); - dcs.Add(DS4Controls.DpadRight); - dcs.Add(DS4Controls.PS); - dcs.Add(DS4Controls.L1); - dcs.Add(DS4Controls.R1); - dcs.Add(DS4Controls.L2); - dcs.Add(DS4Controls.R2); - dcs.Add(DS4Controls.L3); - dcs.Add(DS4Controls.R3); - dcs.Add(DS4Controls.LXPos); - dcs.Add(DS4Controls.LXNeg); - dcs.Add(DS4Controls.LYPos); - dcs.Add(DS4Controls.LYNeg); - dcs.Add(DS4Controls.RXPos); - dcs.Add(DS4Controls.RXNeg); - dcs.Add(DS4Controls.RYPos); - dcs.Add(DS4Controls.RYNeg); - dcs.Add(DS4Controls.SwipeUp); - dcs.Add(DS4Controls.SwipeDown); - dcs.Add(DS4Controls.SwipeLeft); - dcs.Add(DS4Controls.SwipeRight); - } - - private async void WarnExclusiveModeFailure(DS4Device device) - { - if (DS4Devices.isExclusiveMode && !device.IsExclusive) - { - await System.Threading.Tasks.Task.Delay(5); - String message = Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.MacAddress) + " " + Properties.Resources.QuitOtherPrograms; - LogDebug(message, true); - Log.LogToTray(message, true); - } - } - public bool Start(bool showlog = true) - { - if (x360Bus.Open() && x360Bus.Start()) - { - if (showlog) - LogDebug(Properties.Resources.Starting); - DS4Devices.isExclusiveMode = UseExclusiveMode; - if (showlog) - { - LogDebug(Properties.Resources.SearchingController); - LogDebug(DS4Devices.isExclusiveMode ? Properties.Resources.UsingExclusive: Properties.Resources.UsingShared); - } - try - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - int ind = 0; - DS4LightBar.defualtLight = false; - foreach (DS4Device device in devices) - { - if (showlog) - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[ind] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[ind] = new Mouse(ind, device); - device.LightBarColor = MainColor[ind]; - if (!DinputOnly[ind]) - x360Bus.Plugin(ind); - device.Report += this.On_Report; - TouchPadOn(ind, device); - //string filename = ProfilePath[ind]; - ind++; - if (showlog) - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[ind-1] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", ind.ToString()).Replace("*Profile name*", ProfilePath[ind-1]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (ind).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - if (ind >= 4) // out of Xinput devices! - break; - } - } - catch (Exception e) - { - LogDebug(e.Message); - Log.LogToTray(e.Message); - } - running = true; - - } - return true; - } - - public bool Stop(bool showlog = true) - { - if (running) - { - running = false; - if (showlog) - LogDebug(Properties.Resources.StoppingX360); - bool anyUnplugged = false; - for (int i = 0; i < DS4Controllers.Length; i++) - { - if (DS4Controllers[i] != null) - { - if (DCBTatStop && !DS4Controllers[i].Charging && showlog) - DS4Controllers[i].DisconnectBT(); - else - { - DS4LightBar.forcelight[i] = false; - DS4LightBar.forcedFlash[i] = 0; - DS4LightBar.defualtLight = true; - DS4LightBar.updateLightBar(DS4Controllers[i], i, CurrentState[i], ExposedState[i], touchPad[i]); - System.Threading.Thread.Sleep(50); - } - CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(i); - anyUnplugged = true; - DS4Controllers[i] = null; - touchPad[i] = null; - } - } - if (anyUnplugged) - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - x360Bus.UnplugAll(); - x360Bus.Stop(); - if (showlog) - LogDebug(Properties.Resources.StoppingDS4); - DS4Devices.stopControllers(); - if (showlog) - LogDebug(Properties.Resources.StoppedDS4Windows); - ControllerStatusChanged(this); - } - return true; - } - - public bool HotPlug() - { - if (running) - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - foreach (DS4Device device in devices) - { - if (device.IsDisconnecting) - continue; - if (((Func)delegate - { - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] != null && DS4Controllers[Index].MacAddress == device.MacAddress) - return true; - return false; - })()) - continue; - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] == null) - { - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[Index] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[Index] = new Mouse(Index, device); - device.LightBarColor = MainColor[Index]; - device.Report += this.On_Report; - if (!DinputOnly[Index]) - x360Bus.Plugin(Index); - TouchPadOn(Index, device); - //string filename = Path.GetFileName(ProfilePath[Index]); - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - - break; - } - } - } - return true; - } - - public void TouchPadOn(int ind, DS4Device device) - { - ITouchpadBehaviour tPad = touchPad[ind]; - device.Touchpad.TouchButtonDown += tPad.touchButtonDown; - device.Touchpad.TouchButtonUp += tPad.touchButtonUp; - device.Touchpad.TouchesBegan += tPad.touchesBegan; - device.Touchpad.TouchesMoved += tPad.touchesMoved; - device.Touchpad.TouchesEnded += tPad.touchesEnded; - device.Touchpad.TouchUnchanged += tPad.touchUnchanged; - device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; - //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - ControllerStatusChanged(this); - } - - public void TimeoutConnection(DS4Device d) - { - try - { - System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); - sw.Start(); - while (!d.IsAlive()) - { - if (sw.ElapsedMilliseconds < 1000) - System.Threading.Thread.SpinWait(500); - //If weve been waiting less than 1 second let the thread keep its processing chunk - else - System.Threading.Thread.Sleep(500); - //If weve been waiting more than 1 second give up some resources - - if (sw.ElapsedMilliseconds > 5000) throw new TimeoutException(); //Weve waited long enough - } - sw.Reset(); - } - catch (TimeoutException) - { - Stop(false); - Start(false); - } - } - - public string getDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - String battery; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Charged; - else - battery = Properties.Resources.Charging.Replace("*number*", d.Battery.ToString()); - } - else - { - battery = Properties.Resources.Battery.Replace("*number*", d.Battery.ToString()); - } - return d.MacAddress + " (" + d.ConnectionType + "), " + battery; - //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); - } - else - return String.Empty; - } - - public string getDS4MacAddress(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - return d.MacAddress; - } - else - return String.Empty; - } - - public string getShortDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return (d.ConnectionType + " " + battery); - } - else - return Properties.Resources.NoneText; - } - - public string getDS4Battery(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return battery; - } - else - return Properties.Resources.NA; - } - - public string getDS4Status(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - return d.ConnectionType+""; - } - else - return Properties.Resources.NoneText; - } - - - private int XINPUT_UNPLUG_SETTLE_TIME = 250; // Inhibit races that occur with the asynchronous teardown of ScpVBus -> X360 driver instance. - //Called when DS4 is disconnected or timed out - protected virtual void On_DS4Removal(object sender, EventArgs e) - { - DS4Device device = (DS4Device)sender; - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (DS4Controllers[i] != null && device.MacAddress == DS4Controllers[i].MacAddress) - ind = i; - if (ind != -1) - { - CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(ind); - string removed = Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind +1).ToString()); - if (DS4Controllers[ind].Battery <= 20 && - DS4Controllers[ind].ConnectionType == ConnectionType.BT && !DS4Controllers[ind].Charging) - removed += ". " + Properties.Resources.ChargeController; - LogDebug(removed); - Log.LogToTray(removed); - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - DS4Controllers[ind] = null; - touchPad[ind] = null; - ControllerStatusChanged(this); - } - } - public bool[] lag = { false, false, false, false }; - //Called every time the new input report has arrived - protected virtual void On_Report(object sender, EventArgs e) - { - - DS4Device device = (DS4Device)sender; - - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (device == DS4Controllers[i]) - ind = i; - - if (ind != -1) - { - if (FlushHIDQueue[ind]) - device.FlushHID(); - if (!string.IsNullOrEmpty(device.error)) - { - LogDebug(device.error); - } - if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) - { - if (device.Latency >= FlashWhenLateAt && !lag[ind]) - LagFlashWarning(ind, true); - else if (device.Latency < FlashWhenLateAt && lag[ind]) - LagFlashWarning(ind, false); - } - device.getExposedState(ExposedState[ind], CurrentState[ind]); - DS4State cState = CurrentState[ind]; - device.getPreviousState(PreviousState[ind]); - DS4State pState = PreviousState[ind]; - if (pState.Battery != cState.Battery) - ControllerStatusChanged(this); - CheckForHotkeys(ind, cState, pState); - if (eastertime) - EasterTime(ind); - GetInputkeys(ind); - if (LSCurve[ind] != 0 || RSCurve[ind] != 0 || LSDeadzone[ind] != 0 || RSDeadzone[ind] != 0 || - L2Deadzone[ind] != 0 || R2Deadzone[ind] != 0 || LSSens[ind] != 0 || RSSens[ind] != 0 || - L2Sens[ind] != 0 || R2Sens[ind] != 0) //if a curve or deadzone is in place - cState = Mapping.SetCurveAndDeadzone(ind, cState); - if (!recordingMacro && (!string.IsNullOrEmpty(tempprofilename[ind]) || - HasCustomAction(ind) || HasCustomExtras(ind) || ProfileActions[ind].Count > 0)) - { - Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); - cState = MappedState[ind]; - } - //if (HasCustomExtras(ind)) - // DoExtras(ind); - - // Update the GUI/whatever. - DS4LightBar.updateLightBar(device, ind, cState, ExposedState[ind], touchPad[ind]); - - x360Bus.Parse(cState, processingData[ind].Report, ind); - // We push the translated Xinput state, and simultaneously we - // pull back any possible rumble data coming from Xinput consumers. - if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) - { - Byte Big = (Byte)(processingData[ind].Rumble[3]); - Byte Small = (Byte)(processingData[ind].Rumble[4]); - - if (processingData[ind].Rumble[1] == 0x08) - { - setRumble(Big, Small, ind); - } - } - - // Output any synthetic events. - Mapping.Commit(ind); - // Pull settings updates. - device.IdleTimeout = IdleDisconnectTimeout[ind]; - } - } - - public void LagFlashWarning(int ind, bool on) - { - if (on) - { - lag[ind] = true; - LogDebug(Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); - if (FlashWhenLate) - { - DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = 2; - DS4LightBar.forcelight[ind] = true; - } - } - else - { - lag[ind] = false; - LogDebug(Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - } - } - - /* private void DoExtras(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - DS4Controls helddown = DS4Controls.None; - foreach (KeyValuePair p in getCustomExtras(ind)) - { - if (Mapping.getBoolMapping(ind, p.Key, cState, eState, tp)) - { - helddown = p.Key; - break; - } - } - if (helddown != DS4Controls.None) - { - string p = getCustomExtras(ind)[helddown]; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[ind] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - setRumble((byte)extras[0], (byte)extras[1], ind); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = (byte)extras[6]; - DS4LightBar.forcelight[ind] = true; - } - if (extras[7] == 1) - { - if (oldmouse[ind] == -1) - oldmouse[ind] = ButtonMouseSensitivity[ind]; - ButtonMouseSensitivity[ind] = extras[8]; - } - } - catch { } - } - else if (held[ind]) - { - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - ButtonMouseSensitivity[ind] = oldmouse[ind]; - oldmouse[ind] = -1; - setRumble(0, 0, ind); - held[ind] = false; - } - }*/ - - - - public void EasterTime(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - - bool pb = false; - foreach (DS4Controls dc in dcs) - { - if (Mapping.getBoolMapping(ind, dc, cState, eState, tp)) - { - pb = true; - break; - } - } - int temp = eCode; - //Looks like you found the easter egg code, since you're already cheating, - //I scrambled the code for you :) - if (pb && !buttonsdown[ind]) - { - if (cState.Cross && eCode == 9) - eCode++; - else if (!cState.Cross && eCode == 9) - eCode = 0; - else if (cState.DpadLeft && eCode == 6) - eCode++; - else if (!cState.DpadLeft && eCode == 6) - eCode = 0; - else if (cState.DpadRight && eCode == 7) - eCode++; - else if (!cState.DpadRight && eCode == 7) - eCode = 0; - else if (cState.DpadLeft && eCode == 4) - eCode++; - else if (!cState.DpadLeft && eCode == 4) - eCode = 0; - else if (cState.DpadDown && eCode == 2) - eCode++; - else if (!cState.DpadDown && eCode == 2) - eCode = 0; - else if (cState.DpadRight && eCode == 5) - eCode++; - else if (!cState.DpadRight && eCode == 5) - eCode = 0; - else if (cState.DpadUp && eCode == 1) - eCode++; - else if (!cState.DpadUp && eCode == 1) - eCode = 0; - else if (cState.DpadDown && eCode == 3) - eCode++; - else if (!cState.DpadDown && eCode == 3) - eCode = 0; - else if (cState.Circle && eCode == 8) - eCode++; - else if (!cState.Circle && eCode == 8) - eCode = 0; - - if (cState.DpadUp && eCode == 0) - eCode++; - - if (eCode == 10) - { - string message = "(!)"; - sp.Play(); - LogDebug(message, true); - eCode = 0; - } - - if (temp != eCode) - Console.WriteLine(eCode); - buttonsdown[ind] = true; - } - else if (!pb) - buttonsdown[ind] = false; - } - - public string GetInputkeys(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return "Cross"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return "Circle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return "Triangle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return "Square"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return "L1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return "R1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return "L2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return "R2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return "L3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return "R3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return "Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return "Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return "Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return "Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return "Share"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return "Options"; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return "PS"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return "LS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return "LS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return "LS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return "LS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return "RS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return "RS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return "RS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return "RS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return "Touch Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return "Touch Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return "Touch Multi"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return "Touch Upper"; - return "nothing"; - } - - public DS4Controls GetInputkeysDS4(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return DS4Controls.Cross; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return DS4Controls.Circle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return DS4Controls.Triangle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return DS4Controls.Square; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return DS4Controls.L1; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return DS4Controls.R1; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return DS4Controls.L2; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return DS4Controls.R2; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return DS4Controls.L3; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return DS4Controls.R3; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return DS4Controls.DpadUp; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return DS4Controls.DpadDown; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return DS4Controls.DpadLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return DS4Controls.DpadRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return DS4Controls.Share; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return DS4Controls.Options; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return DS4Controls.PS; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return DS4Controls.LXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return DS4Controls.LXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return DS4Controls.LYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return DS4Controls.LYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return DS4Controls.RXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return DS4Controls.RXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return DS4Controls.RYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return DS4Controls.RYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return DS4Controls.TouchLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return DS4Controls.TouchRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return DS4Controls.TouchMulti; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return DS4Controls.TouchUpper; - return DS4Controls.None; - } - - public bool[] touchreleased = { true, true, true, true }, touchslid = { false, false, false, false }; - public byte[] oldtouchvalue = { 0, 0, 0, 0 }; - public int[] oldscrollvalue = { 0, 0, 0, 0 }; - protected virtual void CheckForHotkeys(int deviceID, DS4State cState, DS4State pState) - { - if (!UseTPforControls[deviceID] && cState.Touch1 && pState.PS) - { - if (TouchSensitivity[deviceID] > 0 && touchreleased[deviceID]) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - else if (touchreleased[deviceID]) - { - TouchSensitivity[deviceID] = oldtouchvalue[deviceID]; - ScrollSensitivity[deviceID] = oldscrollvalue[deviceID]; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - } - else - touchreleased[deviceID] = true; - } - - public virtual void StartTPOff(int deviceID) - { - if (deviceID < 4) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - } - } - - public virtual string TouchpadSlide(int ind) - { - DS4State cState = CurrentState[ind]; - string slidedir = "none"; - if (DS4Controllers[ind] != null && cState.Touch2 && !(touchPad[ind].dragging || touchPad[ind].dragging2)) - if (touchPad[ind].slideright && !touchslid[ind]) - { - slidedir = "right"; - touchslid[ind] = true; - } - else if (touchPad[ind].slideleft && !touchslid[ind]) - { - slidedir = "left"; - touchslid[ind] = true; - } - else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) - { - slidedir = ""; - touchslid[ind] = false; - } - return slidedir; - } - public virtual void LogDebug(String Data, bool warning = false) - { - Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); - if (Debug != null) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - OnDebug(this, args); - } - } - - public virtual void OnDebug(object sender, DebugEventArgs args) - { - if (Debug != null) - Debug(this, args); - } - - //sets the rumble adjusted with rumble boost - public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) - { - byte boost = RumbleBoost[deviceNum]; - uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; - if (lightBoosted > 255) - lightBoosted = 255; - uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; - if (heavyBoosted > 255) - heavyBoosted = 255; - if (deviceNum < 4) - if (DS4Controllers[deviceNum] != null) - DS4Controllers[deviceNum].setRumble((byte)lightBoosted, (byte)heavyBoosted); - } - - public DS4State getDS4State(int ind) - { - return CurrentState[ind]; - } - public DS4State getDS4StateMapped(int ind) - { - return MappedState[ind]; - } - } -} diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs new file mode 100644 index 0000000000..067e33d3c4 --- /dev/null +++ b/DS4Windows/DS4Control/ControlService.cs @@ -0,0 +1,1613 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System.Diagnostics; +using static DS4Windows.Global; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class ControlService + { + public ViGEmClient vigemTestClient = null; + public const int DS4_CONTROLLER_COUNT = 4; + public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT]; + public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT]; + public bool running = false; + private DS4State[] MappedState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] CurrentState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] PreviousState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] TempState = new DS4State[DS4_CONTROLLER_COUNT]; + public DS4StateExposed[] ExposedState = new DS4StateExposed[DS4_CONTROLLER_COUNT]; + public ControllerSlotManager slotManager = new ControllerSlotManager(); + public bool recordingMacro = false; + public event EventHandler Debug = null; + bool[] buttonsdown = new bool[4] { false, false, false, false }; + bool[] held = new bool[DS4_CONTROLLER_COUNT]; + int[] oldmouse = new int[DS4_CONTROLLER_COUNT] { -1, -1, -1, -1 }; + public OutputDevice[] outputDevices = new OutputDevice[4] { null, null, null, null }; + Thread tempThread; + Thread tempBusThread; + public List affectedDevs = new List() + { + @"HID\VID_054C&PID_05C4", + @"HID\VID_054C&PID_09CC&MI_03", + @"HID\VID_054C&PID_0BA0&MI_03", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&05c4", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&09cc", + }; + public bool suspending; + //SoundPlayer sp = new SoundPlayer(); + private UdpServer _udpServer; + private OutputSlotManager outputslotMan; + + public event EventHandler ServiceStarted; + public event EventHandler PreServiceStop; + public event EventHandler ServiceStopped; + public event EventHandler RunningChanged; + //public event EventHandler HotplugFinished; + public delegate void HotplugControllerHandler(ControlService sender, DS4Device device, int index); + public event HotplugControllerHandler HotplugController; + + private byte[][] udpOutBuffers = new byte[4][] + { + new byte[100], new byte[100], + new byte[100], new byte[100] + }; + + + void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta) + { + //meta = new DualShockPadMeta(); + meta.PadId = (byte) padIdx; + meta.Model = DsModel.DS4; + + var d = DS4Controllers[padIdx]; + if (d == null) + { + meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + meta.ConnectionType = DsConnection.None; + meta.Model = DsModel.None; + meta.BatteryStatus = 0; + meta.IsActive = false; + return; + //return meta; + } + + bool isValidSerial = false; + //if (d.isValidSerial()) + //{ + string stringMac = d.getMacAddress(); + if (!string.IsNullOrEmpty(stringMac)) + { + stringMac = string.Join("", stringMac.Split(':')); + //stringMac = stringMac.Replace(":", "").Trim(); + meta.PadMacAddress = System.Net.NetworkInformation.PhysicalAddress.Parse(stringMac); + isValidSerial = d.isValidSerial(); + } + //} + + if (!isValidSerial) + { + //meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + } + else + { + if (d.isSynced() || d.IsAlive()) + meta.PadState = DsState.Connected; + else + meta.PadState = DsState.Reserved; + } + + meta.ConnectionType = (d.getConnectionType() == ConnectionType.USB) ? DsConnection.Usb : DsConnection.Bluetooth; + meta.IsActive = !d.isDS4Idle(); + + if (d.isCharging() && d.getBattery() >= 100) + meta.BatteryStatus = DsBattery.Charged; + else + { + if (d.getBattery() >= 95) + meta.BatteryStatus = DsBattery.Full; + else if (d.getBattery() >= 70) + meta.BatteryStatus = DsBattery.High; + else if (d.getBattery() >= 50) + meta.BatteryStatus = DsBattery.Medium; + else if (d.getBattery() >= 20) + meta.BatteryStatus = DsBattery.Low; + else if (d.getBattery() >= 5) + meta.BatteryStatus = DsBattery.Dying; + else + meta.BatteryStatus = DsBattery.None; + } + + //return meta; + } + + private object busThrLck = new object(); + private bool busThrRunning = false; + private Queue busEvtQueue = new Queue(); + private object busEvtQueueLock = new object(); + public ControlService() + { + Crc32Algorithm.InitializeTable(DS4Device.DefaultPolynomial); + + //sp.Stream = DS4WinWPF.Properties.Resources.EE; + // Cause thread affinity to not be tied to main GUI thread + tempBusThread = new Thread(() => + { + //_udpServer = new UdpServer(GetPadDetailForIdx); + busThrRunning = true; + + while (busThrRunning) + { + lock (busEvtQueueLock) + { + Action tempAct = null; + for (int actInd = 0, actLen = busEvtQueue.Count; actInd < actLen; actInd++) + { + tempAct = busEvtQueue.Dequeue(); + tempAct.Invoke(); + } + } + + lock (busThrLck) + Monitor.Wait(busThrLck); + } + }); + tempBusThread.Priority = ThreadPriority.Normal; + tempBusThread.IsBackground = true; + tempBusThread.Start(); + //while (_udpServer == null) + //{ + // Thread.SpinWait(500); + //} + + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + MappedState[i] = new DS4State(); + CurrentState[i] = new DS4State(); + TempState[i] = new DS4State(); + PreviousState[i] = new DS4State(); + ExposedState[i] = new DS4StateExposed(CurrentState[i]); + } + + outputslotMan = new OutputSlotManager(); + DS4Devices.RequestElevation += DS4Devices_RequestElevation; + } + + private void DS4Devices_RequestElevation(RequestElevationArgs args) + { + // Launches an elevated child process to re-enable device + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exelocation); + startInfo.Verb = "runas"; + startInfo.Arguments = "re-enabledevice " + args.InstanceId; + + try + { + Process child = Process.Start(startInfo); + if (!child.WaitForExit(30000)) + { + child.Kill(); + } + else + { + args.StatusCode = child.ExitCode; + } + child.Dispose(); + } + catch { } + } + + public void LaunchHidGuardHelper() + { + if (Global.hidguardInstalled) + { + LogDebug("HidGuardian in use. Launching HidGuardHelper."); + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exedirpath + "\\HidGuardHelper.exe"); + startInfo.Verb = "runas"; + startInfo.Arguments = Process.GetCurrentProcess().Id.ToString(); + startInfo.WorkingDirectory = Global.exedirpath; + try + { Process tempProc = Process.Start(startInfo); tempProc.Dispose(); } + catch { } + } + } + + private void TestQueueBus(Action temp) + { + lock (busEvtQueueLock) + { + busEvtQueue.Enqueue(temp); + } + + lock (busThrLck) + Monitor.Pulse(busThrLck); + } + + public void ChangeUDPStatus(bool state, bool openPort=true) + { + if (state && _udpServer == null) + { + udpChangeStatus = true; + TestQueueBus(() => + { + _udpServer = new UdpServer(GetPadDetailForIdx); + if (openPort) + { + // Change thread affinity of object to have normal priority + Task.Run(() => + { + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + }).Wait(); + } + + udpChangeStatus = false; + }); + } + else if (!state && _udpServer != null) + { + TestQueueBus(() => + { + udpChangeStatus = true; + _udpServer.Stop(); + _udpServer = null; + AppLogger.LogToGui("Closed UDP server", false); + udpChangeStatus = false; + }); + } + } + + public void ChangeMotionEventStatus(bool state) + { + IEnumerable devices = DS4Devices.getDS4Controllers(); + if (state) + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + } + else + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + } + } + + private bool udpChangeStatus = false; + public bool changingUDPPort = false; + public async void UseUDPPort() + { + changingUDPPort = true; + IEnumerable devices = DS4Devices.getDS4Controllers(); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + + await Task.Delay(100); + + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + + changingUDPPort = false; + } + + private void WarnExclusiveModeFailure(DS4Device device) + { + if (DS4Devices.isExclusiveMode && !device.isExclusive()) + { + string message = DS4WinWPF.Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.getMacAddress()) + " " + + DS4WinWPF.Properties.Resources.QuitOtherPrograms; + LogDebug(message, true); + AppLogger.LogToTray(message, true); + } + } + + private void startViGEm() + { + tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } }); + tempThread.Priority = ThreadPriority.AboveNormal; + tempThread.IsBackground = true; + tempThread.Start(); + while (tempThread.IsAlive) + { + Thread.SpinWait(500); + } + + tempThread = null; + } + + private void stopViGEm() + { + if (vigemTestClient != null) + { + vigemTestClient.Dispose(); + vigemTestClient = null; + } + } + + public void PluginOutDev(int index, DS4Device device) + { + OutContType contType = Global.OutContType[index]; + if (useDInputOnly[index]) + { + if (contType == OutContType.X360) + { + LogDebug("Plugging in X360 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.X360; + + //Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient); + Xbox360OutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, vigemTestClient) + as Xbox360OutDevice; + //outputDevices[index] = tempXbox; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) => + { + SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); + }; + tempXbox.cont.FeedbackReceived += p; + tempXbox.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempXbox, index, outputDevices); + //tempXbox.Connect(); + //LogDebug("X360 Controller #" + (index + 1) + " connected"); + } + else if (contType == OutContType.DS4) + { + LogDebug("Plugging in DS4 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.DS4; + //DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient); + DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient) + as DS4OutDevice; + //outputDevices[index] = tempDS4; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => + { + //bool useRumble = false; bool useLight = false; + byte largeMotor = args.LargeMotor; + byte smallMotor = args.SmallMotor; + SetDevRumble(device, largeMotor, smallMotor, devIndex); + //DS4Color color = new DS4Color(args.LightbarColor.Red, + // args.LightbarColor.Green, + // args.LightbarColor.Blue); + ///*Console.WriteLine("IN EVENT"); + //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", + // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToLongTimeString()); + // */ + //if (largeMotor != 0 || smallMotor != 0) + //{ + // useRumble = true; + //} + + //if (color.red != 0 || color.green != 0 || color.blue != 0) + //{ + // useLight = true; + //} + + //if (!useRumble && !useLight) + //{ + // //Console.WriteLine("Fallback"); + // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) + // { + // useRumble = true; + // } + // /*else if (device.LightBarColor.red != 0 || + // device.LightBarColor.green != 0 || + // device.LightBarColor.blue != 0) + // { + // useLight = true; + // } + // */ + //} + + //if (useRumble) + //{ + // //Console.WriteLine("Perform rumble"); + // SetDevRumble(device, largeMotor, smallMotor, devIndex); + //} + + //if (useLight) + //{ + // //Console.WriteLine("Change lightbar color"); + // DS4HapticState haptics = new DS4HapticState + // { + // LightBarColor = color, + // }; + // device.SetHapticState(ref haptics); + //} + + //Console.WriteLine(); + }; + tempDS4.cont.FeedbackReceived += p; + tempDS4.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempDS4, index, outputDevices); + //tempDS4.Connect(); + //LogDebug("DS4 Controller #" + (index + 1) + " connected"); + } + } + + useDInputOnly[index] = false; + } + + public void UnplugOutDev(int index, DS4Device device, bool immediate = false) + { + if (!useDInputOnly[index]) + { + //OutContType contType = Global.OutContType[index]; + string tempType = outputDevices[index].GetDeviceType(); + LogDebug("Unplugging " + tempType + " Controller for input #" + (index + 1), false); + OutputDevice dev = outputDevices[index]; + outputDevices[index] = null; + activeOutDevType[index] = OutContType.None; + outputslotMan.DeferredRemoval(dev, index, outputDevices, immediate); + //dev.Disconnect(); + //LogDebug(tempType + " Controller # " + (index + 1) + " unplugged"); + useDInputOnly[index] = true; + } + } + + public bool Start(bool showlog = true) + { + startViGEm(); + if (vigemTestClient != null) + //if (x360Bus.Open() && x360Bus.Start()) + { + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.Starting); + + LogDebug($"Connection to ViGEmBus {Global.vigembusVersion} established"); + + DS4Devices.isExclusiveMode = getUseExclusiveMode(); + //uiContext = tempui as SynchronizationContext; + if (showlog) + { + LogDebug(DS4WinWPF.Properties.Resources.SearchingController); + LogDebug(DS4Devices.isExclusiveMode ? DS4WinWPF.Properties.Resources.UsingExclusive : DS4WinWPF.Properties.Resources.UsingShared); + } + + if (isUsingUDPServer() && _udpServer == null) + { + ChangeUDPStatus(true, false); + while (udpChangeStatus == true) + { + Thread.SpinWait(500); + } + } + + try + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //int ind = 0; + DS4LightBar.defaultLight = false; + //foreach (DS4Device device in devices) + + //for (int i = 0, devCount = devices.Count(); i < devCount; i++) + int i = 0; + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext(); i++) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + + DS4Controllers[i] = device; + slotManager.AddController(device, i); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[i] = new Mouse(i, device); + + device.SetMouseProxy(touchPad[i]); + + if (!useTempProfile[i]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[i] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[i] = true; + } + else + { + ProfilePath[i] = OlderProfilePath[i]; + Global.linkedProfileCheck[i] = false; + } + + LoadProfile(i, false, this, false, false); + } + + device.LightBarColor = getMainColor(i); + + if (!getDInputOnly(i) && device.isSynced()) + { + //useDInputOnly[i] = false; + PluginOutDev(i, device); + + } + else + { + useDInputOnly[i] = true; + Global.activeOutDevType[i] = OutContType.None; + } + + int tempIdx = i; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + TouchPadOn(i, device); + CheckProfileOptions(i, device, true); + device.StartUpdate(); + //string filename = ProfilePath[ind]; + //ind++; + if (showlog) + { + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[i] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (i + 1).ToString()).Replace("*Profile name*", ProfilePath[i]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (i + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + } + + if (i >= 4) // out of Xinput devices! + break; + } + } + catch (Exception e) + { + LogDebug(e.Message, true); + AppLogger.LogToTray(e.Message, true); + } + + running = true; + + if (_udpServer != null) + { + //var UDP_SERVER_PORT = 26760; + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + } + } + else + { + string logMessage = string.Empty; + if (!vigemInstalled) + { + logMessage = "ViGEmBus is not installed"; + } + else + { + logMessage = "Could not connect to ViGEmBus. Please check the status of the System device in Device Manager and if Visual C++ 2017 Redistributable is installed."; + } + + LogDebug(logMessage); + AppLogger.LogToTray(logMessage); + } + + runHotPlug = true; + ServiceStarted?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + private void CheckQuickCharge(object sender, EventArgs e) + { + DS4Device device = sender as DS4Device; + if (device.ConnectionType == ConnectionType.BT && getQuickCharge() && + device.Charging) + { + device.DisconnectBT(); + } + } + + public void PrepareAbort() + { + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + tempDevice.PrepareAbort(); + } + } + } + + public bool Stop(bool showlog = true) + { + if (running) + { + running = false; + runHotPlug = false; + PreServiceStop?.Invoke(this, EventArgs.Empty); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingX360); + + LogDebug("Closing connection to ViGEmBus"); + + bool anyUnplugged = false; + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + if ((DCBTatStop && !tempDevice.isCharging()) || suspending) + { + if (tempDevice.getConnectionType() == ConnectionType.BT) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectBT(true); + } + else if (tempDevice.getConnectionType() == ConnectionType.SONYWA) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectDongle(true); + } + else + { + tempDevice.StopUpdate(); + } + } + else + { + DS4LightBar.forcelight[i] = false; + DS4LightBar.forcedFlash[i] = 0; + DS4LightBar.defaultLight = true; + DS4LightBar.updateLightBar(DS4Controllers[i], i); + tempDevice.IsRemoved = true; + tempDevice.StopUpdate(); + DS4Devices.RemoveDevice(tempDevice); + Thread.Sleep(50); + } + + CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. + OutputDevice tempout = outputDevices[i]; + if (tempout != null) + { + UnplugOutDev(i, tempDevice, true); + anyUnplugged = true; + } + + //outputDevices[i] = null; + //useDInputOnly[i] = true; + //Global.activeOutDevType[i] = OutContType.None; + DS4Controllers[i] = null; + touchPad[i] = null; + lag[i] = false; + inWarnMonitor[i] = false; + } + } + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingDS4); + + DS4Devices.stopControllers(); + slotManager.ClearControllerList(); + + if (_udpServer != null) + ChangeUDPStatus(false); + //_udpServer.Stop(); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppedDS4Windows); + + while (outputslotMan.RunningQueue) + { + Thread.SpinWait(500); + } + + if (anyUnplugged) + { + Thread.Sleep(OutputSlotManager.DELAY_TIME); + } + + stopViGEm(); + } + + runHotPlug = false; + ServiceStopped?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + public bool HotPlug() + { + if (running) + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //foreach (DS4Device device in devices) + //for (int i = 0, devlen = devices.Count(); i < devlen; i++) + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext();) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + + if (device.isDisconnectingStatus()) + continue; + + if (((Func)delegate + { + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] != null && + DS4Controllers[Index].getMacAddress() == device.getMacAddress()) + return true; + } + + return false; + })()) + { + continue; + } + + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] == null) + { + //LogDebug(DS4WinWPF.Properties.Resources.FoundController + device.getMacAddress() + " (" + device.getConnectionType() + ")"); + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + DS4Controllers[Index] = device; + slotManager.AddController(device, Index); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[Index] = new Mouse(Index, device); + + device.SetMouseProxy(touchPad[Index]); + + if (!useTempProfile[Index]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[Index] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[Index] = true; + } + else + { + ProfilePath[Index] = OlderProfilePath[Index]; + Global.linkedProfileCheck[Index] = false; + } + + LoadProfile(Index, false, this, false, false); + } + + device.LightBarColor = getMainColor(Index); + + int tempIdx = Index; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + if (!getDInputOnly(Index) && device.isSynced()) + { + //useDInputOnly[Index] = false; + PluginOutDev(Index, device); + } + else + { + useDInputOnly[Index] = true; + Global.activeOutDevType[Index] = OutContType.None; + } + + TouchPadOn(Index, device); + CheckProfileOptions(Index, device); + device.StartUpdate(); + + //string filename = Path.GetFileName(ProfilePath[Index]); + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + + HotplugController?.Invoke(this, device, Index); + + break; + } + } + } + } + + return true; + } + + private void CheckProfileOptions(int ind, DS4Device device, bool startUp=false) + { + device.setIdleTimeout(getIdleDisconnectTimeout(ind)); + device.setBTPollRate(getBTPollRate(ind)); + touchPad[ind].ResetTrackAccel(getTrackballFriction(ind)); + + if (!startUp) + { + CheckLauchProfileOption(ind, device); + } + } + + private void CheckLauchProfileOption(int ind, DS4Device device) + { + string programPath = LaunchProgram[ind]; + if (programPath != string.Empty) + { + System.Diagnostics.Process[] localAll = System.Diagnostics.Process.GetProcesses(); + bool procFound = false; + for (int procInd = 0, procsLen = localAll.Length; !procFound && procInd < procsLen; procInd++) + { + try + { + string temp = localAll[procInd].MainModule.FileName; + if (temp == programPath) + { + procFound = true; + } + } + // Ignore any process for which this information + // is not exposed + catch { } + } + + if (!procFound) + { + Task processTask = new Task(() => + { + Thread.Sleep(5000); + System.Diagnostics.Process tempProcess = new System.Diagnostics.Process(); + tempProcess.StartInfo.FileName = programPath; + tempProcess.StartInfo.WorkingDirectory = new FileInfo(programPath).Directory.ToString(); + //tempProcess.StartInfo.UseShellExecute = false; + try { tempProcess.Start(); } + catch { } + }); + + processTask.Start(); + } + } + } + + public void TouchPadOn(int ind, DS4Device device) + { + Mouse tPad = touchPad[ind]; + //ITouchpadBehaviour tPad = touchPad[ind]; + device.Touchpad.TouchButtonDown += tPad.touchButtonDown; + device.Touchpad.TouchButtonUp += tPad.touchButtonUp; + device.Touchpad.TouchesBegan += tPad.touchesBegan; + device.Touchpad.TouchesMoved += tPad.touchesMoved; + device.Touchpad.TouchesEnded += tPad.touchesEnded; + device.Touchpad.TouchUnchanged += tPad.touchUnchanged; + //device.Touchpad.PreTouchProcess += delegate { touchPad[ind].populatePriorButtonStates(); }; + device.Touchpad.PreTouchProcess += (sender, args) => { touchPad[ind].populatePriorButtonStates(); }; + device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; + //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + } + + public string getDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + string battery; + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Charged; + else + battery = DS4WinWPF.Properties.Resources.Charging.Replace("*number*", d.getBattery().ToString()); + } + else + { + battery = DS4WinWPF.Properties.Resources.Battery.Replace("*number*", d.getBattery().ToString()); + } + + return d.getMacAddress() + " (" + d.getConnectionType() + "), " + battery; + //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); + } + else + return string.Empty; + } + + public string getDS4MacAddress(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + return d.getMacAddress(); + } + else + return string.Empty; + } + + public string getShortDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return (d.getConnectionType() + " " + battery); + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + public string getDS4Battery(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return battery; + } + else + return DS4WinWPF.Properties.Resources.NA; + } + + public string getDS4Status(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + return d.getConnectionType() + ""; + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + protected void On_SerialChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + OnDeviceSerialChange(this, ind, device.getMacAddress()); + } + } + + protected void On_SyncChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + bool synced = device.isSynced(); + + if (!synced) + { + if (!useDInputOnly[ind]) + { + Global.activeOutDevType[ind] = OutContType.None; + UnplugOutDev(ind, device); + } + } + else + { + if (!getDInputOnly(ind)) + { + PluginOutDev(ind, device); + } + } + } + } + + //Called when DS4 is disconnected or timed out + protected virtual void On_DS4Removal(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4Controllers.Length; ind == -1 && i < arlength; i++) + { + if (DS4Controllers[i] != null && device.getMacAddress() == DS4Controllers[i].getMacAddress()) + ind = i; + } + + if (ind != -1) + { + bool removingStatus = false; + lock (device.removeLocker) + { + if (!device.IsRemoving) + { + removingStatus = true; + device.IsRemoving = true; + } + } + + if (removingStatus) + { + CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. + if (!useDInputOnly[ind]) + { + UnplugOutDev(ind, device); + } + + // Use Task to reset device synth state and commit it + Task.Run(() => + { + Mapping.Commit(ind); + }).Wait(); + + string removed = DS4WinWPF.Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind + 1).ToString()); + if (device.getBattery() <= 20 && + device.getConnectionType() == ConnectionType.BT && !device.isCharging()) + { + removed += ". " + DS4WinWPF.Properties.Resources.ChargeController; + } + + LogDebug(removed); + AppLogger.LogToTray(removed); + /*Stopwatch sw = new Stopwatch(); + sw.Start(); + while (sw.ElapsedMilliseconds < XINPUT_UNPLUG_SETTLE_TIME) + { + // Use SpinWait to keep control of current thread. Using Sleep could potentially + // cause other events to get run out of order + System.Threading.Thread.SpinWait(500); + } + sw.Stop(); + */ + + device.IsRemoved = true; + device.Synced = false; + DS4Controllers[ind] = null; + slotManager.RemoveController(device, ind); + touchPad[ind] = null; + lag[ind] = false; + inWarnMonitor[ind] = false; + useDInputOnly[ind] = true; + Global.activeOutDevType[ind] = OutContType.None; + /*uiContext?.Post(new SendOrPostCallback((state) => + { + OnControllerRemoved(this, ind); + }), null); + */ + //Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); + } + } + } + + public bool[] lag = new bool[4] { false, false, false, false }; + public bool[] inWarnMonitor = new bool[4] { false, false, false, false }; + private byte[] currentBattery = new byte[4] { 0, 0, 0, 0 }; + private bool[] charging = new bool[4] { false, false, false, false }; + private string[] tempStrings = new string[4] { string.Empty, string.Empty, string.Empty, string.Empty }; + + // Called every time a new input report has arrived + //protected virtual void On_Report(object sender, EventArgs e, int ind) + protected virtual void On_Report(DS4Device device, EventArgs e, int ind) + { + //DS4Device device = (DS4Device)sender; + + if (ind != -1) + { + if (getFlushHIDQueue(ind)) + device.FlushHID(); + + string devError = tempStrings[ind] = device.error; + if (!string.IsNullOrEmpty(devError)) + { + LogDebug(devError); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LogDebug(devError); + }), null); + */ + } + + if (inWarnMonitor[ind]) + { + int flashWhenLateAt = getFlashWhenLateAt(); + if (!lag[ind] && device.Latency >= flashWhenLateAt) + { + lag[ind] = true; + LagFlashWarning(ind, true); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, true); + }), null); + */ + } + else if (lag[ind] && device.Latency < flashWhenLateAt) + { + lag[ind] = false; + LagFlashWarning(ind, false); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, false); + }), null); + */ + } + } + else + { + if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) + { + inWarnMonitor[ind] = true; + } + } + + device.getCurrentState(CurrentState[ind]); + DS4State cState = CurrentState[ind]; + DS4State pState = device.getPreviousStateRef(); + //device.getPreviousState(PreviousState[ind]); + //DS4State pState = PreviousState[ind]; + + if (device.firstReport && device.IsAlive()) + { + device.firstReport = false; + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + OnDeviceStatusChanged(this, ind); + }), null); + */ + } + //else if (pState.Battery != cState.Battery || device.oldCharging != device.isCharging()) + //{ + // byte tempBattery = currentBattery[ind] = cState.Battery; + // bool tempCharging = charging[ind] = device.isCharging(); + // /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + // { + // OnBatteryStatusChange(this, ind, tempBattery, tempCharging); + // }), null); + // */ + //} + + if (getEnableTouchToggle(ind)) + CheckForTouchToggle(ind, cState, pState); + + cState = Mapping.SetCurveAndDeadzone(ind, cState, TempState[ind]); + + if (!recordingMacro && (useTempProfile[ind] || + containsCustomAction(ind) || containsCustomExtras(ind) || + getProfileActionCount(ind) > 0)) + { + Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); + cState = MappedState[ind]; + } + + if (!useDInputOnly[ind]) + { + outputDevices[ind]?.ConvertandSendReport(cState, ind); + //testNewReport(ref x360reports[ind], cState, ind); + //x360controls[ind]?.SendReport(x360reports[ind]); + + //x360Bus.Parse(cState, processingData[ind].Report, ind); + // We push the translated Xinput state, and simultaneously we + // pull back any possible rumble data coming from Xinput consumers. + /*if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) + { + byte Big = processingData[ind].Rumble[3]; + byte Small = processingData[ind].Rumble[4]; + + if (processingData[ind].Rumble[1] == 0x08) + { + SetDevRumble(device, Big, Small, ind); + } + } + */ + } + else + { + // UseDInputOnly profile may re-map sixaxis gyro sensor values as a VJoy joystick axis (steering wheel emulation mode using VJoy output device). Handle this option because VJoy output works even in USeDInputOnly mode. + // If steering wheel emulation uses LS/RS/R2/L2 output axies then the profile should NOT use UseDInputOnly option at all because those require a virtual output device. + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(ind); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: break; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + break; + } + } + + // Output any synthetic events. + Mapping.Commit(ind); + + // Update the GUI/whatever. + DS4LightBar.updateLightBar(device, ind); + } + } + + public void LagFlashWarning(int ind, bool on) + { + if (on) + { + lag[ind] = true; + LogDebug(DS4WinWPF.Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); + if (getFlashWhenLate()) + { + DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; + DS4LightBar.forcedColor[ind] = color; + DS4LightBar.forcedFlash[ind] = 2; + DS4LightBar.forcelight[ind] = true; + } + } + else + { + lag[ind] = false; + LogDebug(DS4WinWPF.Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); + DS4LightBar.forcelight[ind] = false; + DS4LightBar.forcedFlash[ind] = 0; + } + } + + public DS4Controls GetActiveInputControl(int ind) + { + DS4State cState = CurrentState[ind]; + DS4StateExposed eState = ExposedState[ind]; + Mouse tp = touchPad[ind]; + DS4Controls result = DS4Controls.None; + + if (DS4Controllers[ind] != null) + { + if (Mapping.getBoolButtonMapping(cState.Cross)) + result = DS4Controls.Cross; + else if (Mapping.getBoolButtonMapping(cState.Circle)) + result = DS4Controls.Circle; + else if (Mapping.getBoolButtonMapping(cState.Triangle)) + result = DS4Controls.Triangle; + else if (Mapping.getBoolButtonMapping(cState.Square)) + result = DS4Controls.Square; + else if (Mapping.getBoolButtonMapping(cState.L1)) + result = DS4Controls.L1; + else if (Mapping.getBoolTriggerMapping(cState.L2)) + result = DS4Controls.L2; + else if (Mapping.getBoolButtonMapping(cState.L3)) + result = DS4Controls.L3; + else if (Mapping.getBoolButtonMapping(cState.R1)) + result = DS4Controls.R1; + else if (Mapping.getBoolTriggerMapping(cState.R2)) + result = DS4Controls.R2; + else if (Mapping.getBoolButtonMapping(cState.R3)) + result = DS4Controls.R3; + else if (Mapping.getBoolButtonMapping(cState.DpadUp)) + result = DS4Controls.DpadUp; + else if (Mapping.getBoolButtonMapping(cState.DpadDown)) + result = DS4Controls.DpadDown; + else if (Mapping.getBoolButtonMapping(cState.DpadLeft)) + result = DS4Controls.DpadLeft; + else if (Mapping.getBoolButtonMapping(cState.DpadRight)) + result = DS4Controls.DpadRight; + else if (Mapping.getBoolButtonMapping(cState.Share)) + result = DS4Controls.Share; + else if (Mapping.getBoolButtonMapping(cState.Options)) + result = DS4Controls.Options; + else if (Mapping.getBoolButtonMapping(cState.PS)) + result = DS4Controls.PS; + else if (Mapping.getBoolAxisDirMapping(cState.LX, true)) + result = DS4Controls.LXPos; + else if (Mapping.getBoolAxisDirMapping(cState.LX, false)) + result = DS4Controls.LXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.LY, true)) + result = DS4Controls.LYPos; + else if (Mapping.getBoolAxisDirMapping(cState.LY, false)) + result = DS4Controls.LYNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RX, true)) + result = DS4Controls.RXPos; + else if (Mapping.getBoolAxisDirMapping(cState.RX, false)) + result = DS4Controls.RXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RY, true)) + result = DS4Controls.RYPos; + else if (Mapping.getBoolAxisDirMapping(cState.RY, false)) + result = DS4Controls.RYNeg; + else if (Mapping.getBoolTouchMapping(tp.leftDown)) + result = DS4Controls.TouchLeft; + else if (Mapping.getBoolTouchMapping(tp.rightDown)) + result = DS4Controls.TouchRight; + else if (Mapping.getBoolTouchMapping(tp.multiDown)) + result = DS4Controls.TouchMulti; + else if (Mapping.getBoolTouchMapping(tp.upperDown)) + result = DS4Controls.TouchUpper; + } + + return result; + } + + public bool[] touchreleased = new bool[4] { true, true, true, true }, + touchslid = new bool[4] { false, false, false, false }; + + protected virtual void CheckForTouchToggle(int deviceID, DS4State cState, DS4State pState) + { + if (!getUseTPforControls(deviceID) && cState.Touch1 && pState.PS) + { + if (GetTouchActive(deviceID) && touchreleased[deviceID]) + { + TouchActive[deviceID] = false; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + touchreleased[deviceID] = false; + } + else if (touchreleased[deviceID]) + { + TouchActive[deviceID] = true; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + touchreleased[deviceID] = false; + } + } + else + touchreleased[deviceID] = true; + } + + public virtual void StartTPOff(int deviceID) + { + if (deviceID < 4) + { + TouchActive[deviceID] = false; + } + } + + public virtual string TouchpadSlide(int ind) + { + DS4State cState = CurrentState[ind]; + string slidedir = "none"; + if (DS4Controllers[ind] != null && cState.Touch2 && + !(touchPad[ind].dragging || touchPad[ind].dragging2)) + { + if (touchPad[ind].slideright && !touchslid[ind]) + { + slidedir = "right"; + touchslid[ind] = true; + } + else if (touchPad[ind].slideleft && !touchslid[ind]) + { + slidedir = "left"; + touchslid[ind] = true; + } + else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) + { + slidedir = ""; + touchslid[ind] = false; + } + } + + return slidedir; + } + + public virtual void LogDebug(String Data, bool warning = false) + { + //Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); + if (Debug != null) + { + DebugEventArgs args = new DebugEventArgs(Data, warning); + OnDebug(this, args); + } + } + + public virtual void OnDebug(object sender, DebugEventArgs args) + { + if (Debug != null) + Debug(this, args); + } + + // sets the rumble adjusted with rumble boost. General use method + public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) + { + if (deviceNum < 4) + { + DS4Device device = DS4Controllers[deviceNum]; + if (device != null) + SetDevRumble(device, heavyMotor, lightMotor, deviceNum); + //device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + } + + // sets the rumble adjusted with rumble boost. Method more used for + // report handling. Avoid constant checking for a device. + public void SetDevRumble(DS4Device device, + byte heavyMotor, byte lightMotor, int deviceNum) + { + byte boost = getRumbleBoost(deviceNum); + uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; + if (lightBoosted > 255) + lightBoosted = 255; + uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; + if (heavyBoosted > 255) + heavyBoosted = 255; + + device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + + public DS4State getDS4State(int ind) + { + return CurrentState[ind]; + } + + public DS4State getDS4StateMapped(int ind) + { + return MappedState[ind]; + } + + public DS4State getDS4StateTemp(int ind) + { + return TempState[ind]; + } + } +} diff --git a/DS4Windows/DS4Control/ControllerSlotManager.cs b/DS4Windows/DS4Control/ControllerSlotManager.cs new file mode 100644 index 0000000000..df4cffbf83 --- /dev/null +++ b/DS4Windows/DS4Control/ControllerSlotManager.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace DS4Windows +{ + public class ControllerSlotManager + { + private List controllerColl; + public List ControllerColl { get => controllerColl; set => controllerColl = value; } + + private Dictionary controllerDict; + private Dictionary reverseControllerDict; + public Dictionary ControllerDict { get => controllerDict; } + public Dictionary ReverseControllerDict { get => reverseControllerDict; } + + public ControllerSlotManager() + { + controllerColl = new List(); + controllerDict = new Dictionary(); + reverseControllerDict = new Dictionary(); + } + + public void AddController(DS4Device device, int slotIdx) + { + controllerColl.Add(device); + controllerDict.Add(slotIdx, device); + reverseControllerDict.Add(device, slotIdx); + } + + public void RemoveController(DS4Device device, int slotIdx) + { + controllerColl.Remove(device); + controllerDict.Remove(slotIdx); + reverseControllerDict.Remove(device); + } + + public void ClearControllerList() + { + controllerColl.Clear(); + controllerDict.Clear(); + reverseControllerDict.Clear(); + } + } +} diff --git a/DS4Windows/DS4Control/DS4LightBar.cs b/DS4Windows/DS4Control/DS4LightBar.cs index 98483a38a7..c7fb7c005f 100644 --- a/DS4Windows/DS4Control/DS4LightBar.cs +++ b/DS4Windows/DS4Control/DS4LightBar.cs @@ -1,17 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Drawing; using static System.Math; using static DS4Windows.Global; +using System.Diagnostics; + namespace DS4Windows { public class DS4LightBar { private readonly static byte[/* Light On duration */, /* Light Off duration */] BatteryIndicatorDurations = { - { 0, 0 }, // 0 is for "charging" OR anything sufficiently-"charged" + { 28, 252 }, // on 10% of the time at 0 { 28, 252 }, { 56, 224 }, { 84, 196 }, @@ -19,195 +18,335 @@ public class DS4LightBar { 140, 140 }, { 168, 112 }, { 196, 84 }, - { 224, 56}, // on 80% of the time at 80, etc. - { 252, 28 } // on 90% of the time at 90 + { 224, 56 }, // on 80% of the time at 80, etc. + { 252, 28 }, // on 90% of the time at 90 + { 0, 0 } // use on 100%. 0 is for "charging" OR anything sufficiently-"charged" }; + static double[] counters = new double[4] { 0, 0, 0, 0 }; - public static double[] fadetimer = new double[4] { 0, 0, 0, 0 }; + public static Stopwatch[] fadewatches = new Stopwatch[4] + { new Stopwatch(), new Stopwatch(), new Stopwatch(), new Stopwatch() }; + static bool[] fadedirection = new bool[4] { false, false, false, false }; - static DateTime oldnow = DateTime.UtcNow; + static DateTime[] oldnow = new DateTime[4] + { DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow }; + public static bool[] forcelight = new bool[4] { false, false, false, false }; public static DS4Color[] forcedColor = new DS4Color[4]; public static byte[] forcedFlash = new byte[4]; - public static void updateLightBar(DS4Device device, int deviceNum, DS4State cState, DS4StateExposed eState, Mouse tp) + internal const int PULSE_FLASH_DURATION = 2000; + internal const double PULSE_FLASH_SEGMENTS = PULSE_FLASH_DURATION / 40; + internal const int PULSE_CHARGING_DURATION = 4000; + internal const double PULSE_CHARGING_SEGMENTS = (PULSE_CHARGING_DURATION / 40) - 2; + + public static void updateLightBar(DS4Device device, int deviceNum) { - DS4Color color; - if (!defualtLight && !forcelight[deviceNum]) + DS4Color color = new DS4Color(); + bool useForceLight = forcelight[deviceNum]; + LightbarSettingInfo lightbarSettingInfo = getLightbarSettingsInfo(deviceNum); + LightbarDS4WinInfo lightModeInfo = lightbarSettingInfo.ds4winSettings; + bool useLightRoutine = lightbarSettingInfo.mode == LightbarMode.DS4Win; + //bool useLightRoutine = false; + if (!defaultLight && !useForceLight && useLightRoutine) { - if (UseCustomLed[deviceNum]) + if (lightModeInfo.useCustomLed) { - if (LedAsBatteryIndicator[deviceNum]) + if (lightModeInfo.ledAsBattery) { - DS4Color fullColor = CustomColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, device.Battery); + ref DS4Color fullColor = ref lightModeInfo.m_CustomLed; // ref getCustomColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else - color = CustomColor[deviceNum]; + color = lightModeInfo.m_CustomLed; //getCustomColor(deviceNum); } else { - if (Rainbow[deviceNum] > 0) - {// Display rainbow + double rainbow = lightModeInfo.rainbow;// getRainbow(deviceNum); + if (rainbow > 0) + { + // Display rainbow DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion + if (now >= oldnow[deviceNum] + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion { - oldnow = now; - if (device.Charging) - counters[deviceNum] -= 1.5 * 3 / Rainbow[deviceNum]; + oldnow[deviceNum] = now; + if (device.isCharging()) + counters[deviceNum] -= 1.5 * 3 / rainbow; else - counters[deviceNum] += 1.5 * 3 / Rainbow[deviceNum]; + counters[deviceNum] += 1.5 * 3 / rainbow; } + if (counters[deviceNum] < 0) counters[deviceNum] = 180000; - if (counters[deviceNum] > 180000) + else if (counters[deviceNum] > 180000) counters[deviceNum] = 0; - if (LedAsBatteryIndicator[deviceNum]) - color = HuetoRGB((float)counters[deviceNum] % 360, (byte)(2.55 * device.Battery)); + + double maxSat = lightModeInfo.maxRainbowSat; // GetMaxSatRainbow(deviceNum); + if (lightModeInfo.ledAsBattery) + { + byte useSat = (byte)(maxSat == 1.0 ? + device.getBattery() * 2.55 : + device.getBattery() * 2.55 * maxSat); + color = HuetoRGB((float)counters[deviceNum] % 360, useSat); + } else - color = HuetoRGB((float)counters[deviceNum] % 360, 255); + color = HuetoRGB((float)counters[deviceNum] % 360, + (byte)(maxSat == 1.0 ? 255 : 255 * maxSat)); } - else if (LedAsBatteryIndicator[deviceNum]) + else if (lightModeInfo.ledAsBattery) { - //if (device.Charging == false || device.Battery >= 100) // when charged, don't show the charging animation - { - DS4Color fullColor = MainColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, (uint)device.Battery); - } + ref DS4Color fullColor = ref lightModeInfo.m_Led; //ref getMainColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else { - color = MainColor[deviceNum]; + color = getMainColor(deviceNum); } - } - if (device.Battery <= FlashAt[deviceNum] && !defualtLight && !device.Charging) + if (device.getBattery() <= lightModeInfo.flashAt && !defaultLight && !device.isCharging()) { - if (!(FlashColor[deviceNum].red == 0 && - FlashColor[deviceNum].green == 0 && - FlashColor[deviceNum].blue == 0)) - color = FlashColor[deviceNum]; - if (FlashType[deviceNum] == 1) + ref DS4Color flashColor = ref lightModeInfo.m_FlashLed; //ref getFlashColor(deviceNum); + if (!(flashColor.red == 0 && + flashColor.green == 0 && + flashColor.blue == 0)) + color = flashColor; + + if (lightModeInfo.flashType == 1) { - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 100) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += 1; + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= 1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = 100.0 * (elapsed / PULSE_FLASH_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = (0 - 100.0) * (elapsed / PULSE_FLASH_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color tempCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref tempCol, ratio); } } - if (IdleDisconnectTimeout[deviceNum] > 0 && LedAsBatteryIndicator[deviceNum] && (!device.Charging || device.Battery >= 100)) - {//Fade lightbar by idle time + int idleDisconnectTimeout = getIdleDisconnectTimeout(deviceNum); + if (idleDisconnectTimeout > 0 && lightModeInfo.ledAsBattery && + (!device.isCharging() || device.getBattery() >= 100)) + { + // Fade lightbar by idle time TimeSpan timeratio = new TimeSpan(DateTime.UtcNow.Ticks - device.lastActive.Ticks); double botratio = timeratio.TotalMilliseconds; - double topratio = TimeSpan.FromSeconds(IdleDisconnectTimeout[deviceNum]).TotalMilliseconds; - double ratio = ((botratio / topratio) * 100); - if (ratio >= 50 && ratio <= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), (uint)((ratio - 50) * 2)); - else if (ratio >= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), 100); + double topratio = TimeSpan.FromSeconds(idleDisconnectTimeout).TotalMilliseconds; + double ratio = 100.0 * (botratio / topratio), elapsed = ratio; + if (ratio >= 50.0 && ratio < 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, + (uint)(-100.0 * (elapsed = 0.02 * (ratio - 50.0)) * (elapsed - 2.0))); + } + else if (ratio >= 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, 100.0); + } + } - if (device.Charging && device.Battery < 100) - switch (ChargingType[deviceNum]) + + if (device.isCharging() && device.getBattery() < 100) + { + switch (lightModeInfo.chargingType) { case 1: - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 105) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += .1; + { + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= .1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = 100.0 * (elapsed / PULSE_CHARGING_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = (0 - 100.0) * (elapsed / PULSE_CHARGING_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, ratio); break; + } case 2: - counters[deviceNum] += .167; + { + counters[deviceNum] += 0.167; color = HuetoRGB((float)counters[deviceNum] % 360, 255); break; + } case 3: - color = ChargingColor[deviceNum]; - break; - default: + { + color = lightModeInfo.m_ChargingLed; //getChargingColor(deviceNum); break; + } + default: break; } + } } - else if (forcelight[deviceNum]) + else if (useForceLight) { color = forcedColor[deviceNum]; + useLightRoutine = true; } else if (shuttingdown) + { color = new DS4Color(0, 0, 0); - else + useLightRoutine = true; + } + else if (useLightRoutine) { - if (device.ConnectionType == ConnectionType.BT) + if (device.getConnectionType() == ConnectionType.BT) color = new DS4Color(32, 64, 64); else color = new DS4Color(0, 0, 0); } - bool distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); - if (distanceprofile && !defualtLight) - { //Thing I did for Distance - float rumble = device.LeftHeavySlowRumble / 2.55f; - byte max = Max(color.red, Max(color.green, color.blue)); - if (device.LeftHeavySlowRumble > 100) - color = getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), rumble); - else - color = getTransitionedColor(color, getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), 39.6078f), device.LeftHeavySlowRumble); - } - DS4HapticState haptics = new DS4HapticState - { - LightBarColor = color - }; - if (haptics.IsLightBarSet()) + + if (useLightRoutine) { - if (forcelight[deviceNum] && forcedFlash[deviceNum] > 0) + bool distanceprofile = DistanceProfiles[deviceNum] || tempprofileDistance[deviceNum]; + //distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); + if (distanceprofile && !defaultLight) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); - haptics.LightBarExplicitlyOff = true; + // Thing I did for Distance + float rumble = device.getLeftHeavySlowRumble() / 2.55f; + byte max = Max(color.red, Max(color.green, color.blue)); + if (device.getLeftHeavySlowRumble() > 100) + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + color = getTransitionedColor(ref maxCol, ref redCol, rumble); + } + else + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + DS4Color tempCol = getTransitionedColor(ref maxCol, + ref redCol, 39.6078f); + color = getTransitionedColor(ref color, ref tempCol, + device.getLeftHeavySlowRumble()); + } } - else if (device.Battery <= FlashAt[deviceNum] && FlashType[deviceNum] == 0 && !defualtLight && !device.Charging) + + DS4HapticState haptics = new DS4HapticState { - int level = device.Battery / 10; - //if (level >= 10) - //level = 0; // all values of ~0% or >~100% are rendered the same - haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; - haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; - } - else if (distanceprofile && device.LeftHeavySlowRumble > 155) //also part of Distance + LightBarColor = color + }; + + if (haptics.IsLightBarSet()) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.LeftHeavySlowRumble + 265)); - haptics.LightBarExplicitlyOff = true; + if (useForceLight && forcedFlash[deviceNum] > 0) + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); + haptics.LightBarExplicitlyOff = true; + } + else if (device.getBattery() <= lightModeInfo.flashAt && lightModeInfo.flashType == 0 && !defaultLight && !device.isCharging()) + { + int level = device.getBattery() / 10; + if (level >= 10) + level = 10; // all values of >~100% are rendered the same + + haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; + haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; + } + else if (distanceprofile && device.getLeftHeavySlowRumble() > 155) //also part of Distance + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.getLeftHeavySlowRumble() + 265)); + haptics.LightBarExplicitlyOff = true; + } + else + { + //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; + haptics.LightBarExplicitlyOff = true; + } } else { - //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; haptics.LightBarExplicitlyOff = true; } + + byte tempLightBarOnDuration = device.getLightBarOnDuration(); + if (tempLightBarOnDuration != haptics.LightBarFlashDurationOn && tempLightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + + device.SetHapticState(ref haptics); + //device.pushHapticState(ref haptics); } - else - { - haptics.LightBarExplicitlyOff = true; - } - if (device.LightBarOnDuration != haptics.LightBarFlashDurationOn && device.LightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - if (device.LightBarOnDuration == 1) //helps better reset the color - System.Threading.Thread.Sleep(5); - device.pushHapticState(haptics); } - public static bool defualtLight = false, shuttingdown = false; + public static bool defaultLight = false, shuttingdown = false; public static DS4Color HuetoRGB(float hue, byte sat) { diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs new file mode 100644 index 0000000000..33cbdb8caf --- /dev/null +++ b/DS4Windows/DS4Control/DS4OutDevice.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; +using Nefarius.ViGEm.Client.Targets; +using Nefarius.ViGEm.Client.Targets.DualShock4; + +namespace DS4Windows +{ + class DS4OutDevice : OutputDevice + { + public const string devtype = "DS4"; + + public DualShock4Controller cont; + private DualShock4Report report; + public DualShock4FeedbackReceivedEventHandler forceFeedbackCall; + + public DS4OutDevice(ViGEmClient client) + { + cont = new DualShock4Controller(client); + report = new DualShock4Report(); + } + + public override void ConvertandSendReport(DS4State state, int device) + { + DualShock4Buttons tempButtons = 0; + DualShock4DPadValues tempDPad = DualShock4DPadValues.None; + DualShock4SpecialButtons tempSpecial = 0; + + unchecked + { + if (state.Share) tempButtons |= DualShock4Buttons.Share; + if (state.L3) tempButtons |= DualShock4Buttons.ThumbLeft; + if (state.R3) tempButtons |= DualShock4Buttons.ThumbRight; + if (state.Options) tempButtons |= DualShock4Buttons.Options; + + if (state.DpadUp && state.DpadRight) tempDPad = DualShock4DPadValues.Northeast; + else if (state.DpadUp && state.DpadLeft) tempDPad = DualShock4DPadValues.Northwest; + else if (state.DpadUp) tempDPad = DualShock4DPadValues.North; + else if (state.DpadRight && state.DpadDown) tempDPad = DualShock4DPadValues.Southeast; + else if (state.DpadRight) tempDPad = DualShock4DPadValues.East; + else if (state.DpadDown && state.DpadLeft) tempDPad = DualShock4DPadValues.Southwest; + else if (state.DpadDown) tempDPad = DualShock4DPadValues.South; + else if (state.DpadLeft) tempDPad = DualShock4DPadValues.West; + + /*if (state.DpadUp) tempDPad = (state.DpadRight) ? DualShock4DPadValues.Northeast : DualShock4DPadValues.North; + if (state.DpadRight) tempDPad = (state.DpadDown) ? DualShock4DPadValues.Southeast : DualShock4DPadValues.East; + if (state.DpadDown) tempDPad = (state.DpadLeft) ? DualShock4DPadValues.Southwest : DualShock4DPadValues.South; + if (state.DpadLeft) tempDPad = (state.DpadUp) ? DualShock4DPadValues.Northwest : DualShock4DPadValues.West; + */ + + if (state.L1) tempButtons |= DualShock4Buttons.ShoulderLeft; + if (state.R1) tempButtons |= DualShock4Buttons.ShoulderRight; + //if (state.L2Btn) tempButtons |= DualShock4Buttons.TriggerLeft; + //if (state.R2Btn) tempButtons |= DualShock4Buttons.TriggerRight; + if (state.L2 > 0) tempButtons |= DualShock4Buttons.TriggerLeft; + if (state.R2 > 0) tempButtons |= DualShock4Buttons.TriggerRight; + + if (state.Triangle) tempButtons |= DualShock4Buttons.Triangle; + if (state.Circle) tempButtons |= DualShock4Buttons.Circle; + if (state.Cross) tempButtons |= DualShock4Buttons.Cross; + if (state.Square) tempButtons |= DualShock4Buttons.Square; + if (state.PS) tempSpecial |= DualShock4SpecialButtons.Ps; + if (state.TouchButton) tempSpecial |= DualShock4SpecialButtons.Touchpad; + //report.SetButtonsFull(tempButtons); + report.Buttons = (ushort)tempButtons; + report.SetDPad(tempDPad); + report.SpecialButtons = (byte)tempSpecial; + } + + + report.LeftTrigger = state.L2; + report.RightTrigger = state.R2; + + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LX: + report.LeftThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LY: + report.LeftThumbX = state.LX; + report.LeftThumbY = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RX: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RY: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = (byte)state.SASteeringWheelEmulationUnit; + break; + + case SASteeringWheelEmulationAxisType.L2R2: + report.LeftTrigger = report.RightTrigger = 0; + if (state.SASteeringWheelEmulationUnit >= 0) report.LeftTrigger = (Byte)state.SASteeringWheelEmulationUnit; + else report.RightTrigger = (Byte)state.SASteeringWheelEmulationUnit; + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + goto case SASteeringWheelEmulationAxisType.None; + + default: + // Should never come here but just in case use the NONE case as default handler.... + goto case SASteeringWheelEmulationAxisType.None; + } + + cont.SendReport(report); + } + + public override void Connect() => cont.Connect(); + public override void Disconnect() + { + if (forceFeedbackCall != null) + { + cont.FeedbackReceived -= forceFeedbackCall; + forceFeedbackCall = null; + } + + cont.Disconnect(); + cont.Dispose(); + cont = null; + } + public override string GetDeviceType() => devtype; + } +} diff --git a/DS4Windows/DS4Control/DS4StateFieldMapping.cs b/DS4Windows/DS4Control/DS4StateFieldMapping.cs new file mode 100644 index 0000000000..d1c12adf10 --- /dev/null +++ b/DS4Windows/DS4Control/DS4StateFieldMapping.cs @@ -0,0 +1,164 @@ + +namespace DS4Windows +{ + public class DS4StateFieldMapping + { + public enum ControlType: int { Unknown = 0, Button, AxisDir, Trigger, Touch, GyroDir, SwipeDir } + + public bool[] buttons = new bool[(int)DS4Controls.SwipeDown + 1]; + public byte[] axisdirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public byte[] triggers = new byte[(int)DS4Controls.SwipeDown + 1]; + public int[] gryodirs = new int[(int)DS4Controls.SwipeDown + 1]; + public byte[] swipedirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public bool[] swipedirbools = new bool[(int)DS4Controls.SwipeDown + 1]; + public bool touchButton = false; + + public static ControlType[] mappedType = new ControlType[38] { ControlType.Unknown, // DS4Controls.None + ControlType.AxisDir, // DS4Controls.LXNeg + ControlType.AxisDir, // DS4Controls.LXPos + ControlType.AxisDir, // DS4Controls.LYNeg + ControlType.AxisDir, // DS4Controls.LYPos + ControlType.AxisDir, // DS4Controls.RXNeg + ControlType.AxisDir, // DS4Controls.RXPos + ControlType.AxisDir, // DS4Controls.RYNeg + ControlType.AxisDir, // DS4Controls.RYPos + ControlType.Button, // DS4Controls.L1 + ControlType.Trigger, // DS4Controls.L2 + ControlType.Button, // DS4Controls.L3 + ControlType.Button, // DS4Controls.R1 + ControlType.Trigger, // DS4Controls.R2 + ControlType.Button, // DS4Controls.R3 + ControlType.Button, // DS4Controls.Square + ControlType.Button, // DS4Controls.Triangle + ControlType.Button, // DS4Controls.Circle + ControlType.Button, // DS4Controls.Cross + ControlType.Button, // DS4Controls.DpadUp + ControlType.Button, // DS4Controls.DpadRight + ControlType.Button, // DS4Controls.DpadDown + ControlType.Button, // DS4Controls.DpadLeft + ControlType.Button, // DS4Controls.PS + ControlType.Touch, // DS4Controls.TouchLeft + ControlType.Touch, // DS4Controls.TouchUpper + ControlType.Touch, // DS4Controls.TouchMulti + ControlType.Touch, // DS4Controls.TouchRight + ControlType.Button, // DS4Controls.Share + ControlType.Button, // DS4Controls.Options + ControlType.GyroDir, // DS4Controls.GyroXPos + ControlType.GyroDir, // DS4Controls.GyroXNeg + ControlType.GyroDir, // DS4Controls.GyroZPos + ControlType.GyroDir, // DS4Controls.GyroZNeg + ControlType.SwipeDir, // DS4Controls.SwipeLeft + ControlType.SwipeDir, // DS4Controls.SwipeRight + ControlType.SwipeDir, // DS4Controls.SwipeUp + ControlType.SwipeDir, // DS4Controls.SwipeDown + }; + + public DS4StateFieldMapping() + { + } + + public DS4StateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse=false) + { + populateFieldMapping(cState, exposeState, tp, priorMouse); + } + + public void populateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse = false) + { + unchecked + { + axisdirs[(int)DS4Controls.LXNeg] = cState.LX; + axisdirs[(int)DS4Controls.LXPos] = cState.LX; + axisdirs[(int)DS4Controls.LYNeg] = cState.LY; + axisdirs[(int)DS4Controls.LYPos] = cState.LY; + + axisdirs[(int)DS4Controls.RXNeg] = cState.RX; + axisdirs[(int)DS4Controls.RXPos] = cState.RX; + axisdirs[(int)DS4Controls.RYNeg] = cState.RY; + axisdirs[(int)DS4Controls.RYPos] = cState.RY; + + triggers[(int)DS4Controls.L2] = cState.L2; + triggers[(int)DS4Controls.R2] = cState.R2; + + buttons[(int)DS4Controls.L1] = cState.L1; + buttons[(int)DS4Controls.L3] = cState.L3; + buttons[(int)DS4Controls.R1] = cState.R1; + buttons[(int)DS4Controls.R3] = cState.R3; + + buttons[(int)DS4Controls.Cross] = cState.Cross; + buttons[(int)DS4Controls.Triangle] = cState.Triangle; + buttons[(int)DS4Controls.Circle] = cState.Circle; + buttons[(int)DS4Controls.Square] = cState.Square; + buttons[(int)DS4Controls.PS] = cState.PS; + buttons[(int)DS4Controls.Options] = cState.Options; + buttons[(int)DS4Controls.Share] = cState.Share; + + buttons[(int)DS4Controls.DpadUp] = cState.DpadUp; + buttons[(int)DS4Controls.DpadRight] = cState.DpadRight; + buttons[(int)DS4Controls.DpadDown] = cState.DpadDown; + buttons[(int)DS4Controls.DpadLeft] = cState.DpadLeft; + + buttons[(int)DS4Controls.TouchLeft] = tp != null ? (!priorMouse ? tp.leftDown : tp.priorLeftDown) : false; + buttons[(int)DS4Controls.TouchRight] = tp != null ? (!priorMouse ? tp.rightDown : tp.priorRightDown) : false; + buttons[(int)DS4Controls.TouchUpper] = tp != null ? (!priorMouse ? tp.upperDown : tp.priorUpperDown) : false; + buttons[(int)DS4Controls.TouchMulti] = tp != null ? (!priorMouse ? tp.multiDown : tp.priorMultiDown) : false; + + int sixAxisX = -exposeState.getOutputAccelX(); + gryodirs[(int)DS4Controls.GyroXPos] = sixAxisX > 0 ? sixAxisX : 0; + gryodirs[(int)DS4Controls.GyroXNeg] = sixAxisX < 0 ? sixAxisX : 0; + + int sixAxisZ = exposeState.getOutputAccelZ(); + gryodirs[(int)DS4Controls.GyroZPos] = sixAxisZ > 0 ? sixAxisZ : 0; + gryodirs[(int)DS4Controls.GyroZNeg] = sixAxisZ < 0 ? sixAxisZ : 0; + + swipedirs[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeftB : tp.priorSwipeLeftB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRightB : tp.priorSwipeRightB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUpB : tp.priorSwipeUpB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDownB : tp.priorSwipeDownB) : (byte)0; + + swipedirbools[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeft : tp.priorSwipeLeft) : false; + swipedirbools[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRight : tp.priorSwipeRight) : false; + swipedirbools[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUp : tp.priorSwipeUp) : false; + swipedirbools[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDown : tp.priorSwipeDown) : false; + touchButton = cState.TouchButton; + } + } + + public void populateState(DS4State state) + { + unchecked + { + state.LX = axisdirs[(int)DS4Controls.LXNeg]; + state.LX = axisdirs[(int)DS4Controls.LXPos]; + state.LY = axisdirs[(int)DS4Controls.LYNeg]; + state.LY = axisdirs[(int)DS4Controls.LYPos]; + + state.RX = axisdirs[(int)DS4Controls.RXNeg]; + state.RX = axisdirs[(int)DS4Controls.RXPos]; + state.RY = axisdirs[(int)DS4Controls.RYNeg]; + state.RY = axisdirs[(int)DS4Controls.RYPos]; + + state.L2 = triggers[(int)DS4Controls.L2]; + state.R2 = triggers[(int)DS4Controls.R2]; + + state.L1 = buttons[(int)DS4Controls.L1]; + state.L3 = buttons[(int)DS4Controls.L3]; + state.R1 = buttons[(int)DS4Controls.R1]; + state.R3 = buttons[(int)DS4Controls.R3]; + + state.Cross = buttons[(int)DS4Controls.Cross]; + state.Triangle = buttons[(int)DS4Controls.Triangle]; + state.Circle = buttons[(int)DS4Controls.Circle]; + state.Square = buttons[(int)DS4Controls.Square]; + state.PS = buttons[(int)DS4Controls.PS]; + state.Options = buttons[(int)DS4Controls.Options]; + state.Share = buttons[(int)DS4Controls.Share]; + + state.DpadUp = buttons[(int)DS4Controls.DpadUp]; + state.DpadRight = buttons[(int)DS4Controls.DpadRight]; + state.DpadDown = buttons[(int)DS4Controls.DpadDown]; + state.DpadLeft = buttons[(int)DS4Controls.DpadLeft]; + state.TouchButton = touchButton; + } + } + } +} diff --git a/DS4Windows/DS4Control/ITouchpadBehaviour.cs b/DS4Windows/DS4Control/ITouchpadBehaviour.cs index 24a5ee74ff..80a4343c44 100644 --- a/DS4Windows/DS4Control/ITouchpadBehaviour.cs +++ b/DS4Windows/DS4Control/ITouchpadBehaviour.cs @@ -1,18 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { interface ITouchpadBehaviour { - void touchesBegan(object sender, TouchpadEventArgs arg); - void touchesMoved(object sender, TouchpadEventArgs arg); - void touchButtonUp(object sender, TouchpadEventArgs arg); - void touchButtonDown(object sender, TouchpadEventArgs arg); - void touchesEnded(object sender, TouchpadEventArgs arg); - void sixaxisMoved(object sender, SixAxisEventArgs unused); - void touchUnchanged(object sender, EventArgs unused); + void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg); + void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs unused); + void touchUnchanged(DS4Touchpad sender, EventArgs unused); } } diff --git a/DS4Windows/DS4Control/InputMethods.cs b/DS4Windows/DS4Control/InputMethods.cs index 278f86e312..09f4c845f5 100644 --- a/DS4Windows/DS4Control/InputMethods.cs +++ b/DS4Windows/DS4Control/InputMethods.cs @@ -1,210 +1,209 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Runtime.InteropServices; +using System.Security; + namespace DS4Windows { + [SuppressUnmanagedCodeSecurity] class InputMethods { - private static INPUT[] sendInputs = new INPUT[2]; // will allow for keyboard + mouse/tablet input within one SendInput call, or two mouse events - private static object lockob = new object(); public static void MoveCursorBy(int x, int y) { - lock (lockob) + if (x != 0 || y != 0) { - if (x != 0 || y != 0) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = MOUSEEVENTF_MOVE; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = x; - sendInputs[0].Data.Mouse.Y = y; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_MOVE; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = x; + temp.Data.Mouse.Y = y; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } } public static void MouseWheel(int vertical, int horizontal) { - lock (lockob) + INPUT[] tempInput = new INPUT[2]; + uint inputs = 0; + ref INPUT temp = ref tempInput[inputs]; + if (vertical != 0) { - uint inputs = 0; - if (vertical != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_WHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)vertical; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - if (horizontal != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)horizontal; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - SendInput(inputs, sendInputs, (int)inputs * Marshal.SizeOf(sendInputs[0])); + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_WHEEL; + temp.Data.Mouse.MouseData = (uint)vertical; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } - } - public static void MouseEvent(uint mouseButton) - { - lock (lockob) + if (horizontal != 0) { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + temp = ref tempInput[inputs]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; + temp.Data.Mouse.MouseData = (uint)horizontal; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } + + SendInput(inputs, tempInput, (int)inputs * Marshal.SizeOf(tempInput[0])); } - public static void MouseEvent(uint mouseButton, int type) + public static void MouseEvent(uint mouseButton) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = (uint)type; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performLeftClick() + public static void MouseEvent(uint mouseButton, int type) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = (uint)type; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performRightClick() + public static void performSCKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performMiddleClick() + public static void performKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = 1; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performFourthClick() + public static void performSCKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_XBUTTONDOWN | MOUSEEVENTF_XBUTTONUP; - sendInputs[0].Data.Mouse.MouseData = 1; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performSCKeyPress(ushort key) + + public static void performKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags | KEYEVENTF_KEYUP; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performKeyPress(ushort key) + private static ushort scancodeFromVK(uint vkey) { - lock (lockob) + ushort scancode = 0; + if (vkey == VK_PAUSE) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = 1; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + // MapVirtualKey does not work with VK_PAUSE + scancode = 0x45; } - } - - public static void performSCKeyRelease(ushort key) - { - lock (lockob) + else { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + scancode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC); } - } - public static void performKeyRelease(ushort key) - { - lock (lockob) + switch (vkey) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_INSERT: + case VK_DELETE: + case VK_DIVIDE: + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_VOLUME_MUTE: + case VK_VOLUME_DOWN: + case VK_VOLUME_UP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_LAUNCH_MEDIA_SELECT: + case VK_BROWSER_HOME: + case VK_LAUNCH_MAIL: + case VK_LAUNCH_APP1: + case VK_LAUNCH_APP2: + case VK_APPS: + { + scancode |= (ushort)EXTENDED_FLAG; // set extended bit + break; + } } + + return scancode; } /// @@ -277,7 +276,14 @@ internal struct MOUSEINPUT MOUSEEVENTF_XBUTTONDOWN = 128, MOUSEEVENTF_XBUTTONUP = 256, KEYEVENTF_EXTENDEDKEY = 1, KEYEVENTF_KEYUP = 2, MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x1000, MOUSEEVENTF_MIDDLEWDOWN = 0x0020, MOUSEEVENTF_MIDDLEWUP = 0x0040, - KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004; + KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004, EXTENDED_FLAG = 0x100; + + internal const uint VK_PAUSE = 0x13, VK_LEFT = 0x25, VK_UP = 0x26, VK_RIGHT = 0x27, VK_DOWN = 0x28, + VK_PRIOR = 0x21, VK_NEXT = 0x22, VK_END = 0x23, VK_HOME = 0x24, VK_INSERT = 0x2D, VK_DELETE = 0x2E, VK_APPS = 0x5D, + VK_DIVIDE = 0x6F, VK_NUMLOCK = 0x90, VK_RCONTROL = 0xA3, VK_RMENU = 0xA5, VK_BROWSER_HOME = 0xAC, + VK_VOLUME_MUTE = 0xAD, VK_VOLUME_DOWN = 0xAE, VK_VOLUME_UP = 0xAF, + VK_MEDIA_NEXT_TRACK = 0xB0, VK_MEDIA_PREV_TRACK = 0xB1, VK_MEDIA_STOP = 0xB2, VK_MEDIA_PLAY_PAUSE = 0xB3, + VK_LAUNCH_MAIL = 0xB4, VK_LAUNCH_MEDIA_SELECT = 0xB5, VK_LAUNCH_APP1 = 0xB6, VK_LAUNCH_APP2 = 0xB7; [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputs); @@ -285,6 +291,7 @@ internal struct MOUSEINPUT private static extern ushort MapVirtualKey(uint uCode, uint uMapType); [DllImport("user32.dll", SetLastError = true)] static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + //Not used, just here public static void DownKeys(ushort key) { @@ -301,5 +308,5 @@ public static void ReleaseKeys(ushort key) keybd_event((byte)key, 0, (int)KEYEVENTF_KEYUP, 0); } } - } + diff --git a/DS4Windows/DS4Control/Log.cs b/DS4Windows/DS4Control/Log.cs index f581f77e1f..a2d8815d08 100644 --- a/DS4Windows/DS4Control/Log.cs +++ b/DS4Windows/DS4Control/Log.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { - public class Log + public class AppLogger { public static event EventHandler TrayIconLog; public static event EventHandler GuiLog; diff --git a/DS4Windows/DS4Control/MacroParser.cs b/DS4Windows/DS4Control/MacroParser.cs new file mode 100644 index 0000000000..0950eb7863 --- /dev/null +++ b/DS4Windows/DS4Control/MacroParser.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace DS4Windows +{ + public class MacroParser + { + private bool loaded; + private List macroSteps; + private int[] inputMacro; + private Dictionary keydown = new Dictionary(); + public static Dictionary macroInputNames = new Dictionary() + { + [256] = "Left Mouse Button", [257] = "Right Mouse Button", + [258] = "Middle Mouse Button", [259] = "4th Mouse Button", + [260] = "5th Mouse Button", [261] = "A Button", + [262] = "B Button", [263] = "X Button", + [264] = "Y Button", [265] = "Start", + [266] = "Back", [267] = "Up Button", + [268] = "Down Button", [269] = "Left Button", + [270] = "Right Button", [271] = "Guide", + [272] = "Left Bumper", [273] = "Right Bumper", + [274] = "Left Trigger", [275] = "Right Trigger", + [276] = "Left Stick", [277] = "Right Stick", + [278] = "LS Right", [279] = "LS Left", + [280] = "LS Down", [281] = "LS Up", + [282] = "RS Right", [283] = "RS Left", + [284] = "RS Down", [285] = "RS Up", + }; + + public List MacroSteps { get => macroSteps; } + + public MacroParser(int[] macro) + { + macroSteps = new List(); + inputMacro = macro; + } + + public void LoadMacro() + { + if (loaded) + { + return; + } + + keydown.Clear(); + for(int i = 0; i < inputMacro.Length; i++) + { + int value = inputMacro[i]; + MacroStep step = ParseStep(value); + macroSteps.Add(step); + } + + loaded = true; + } + + public List GetMacroStrings() + { + if (!loaded) + { + LoadMacro(); + } + + List result = new List(); + foreach(MacroStep step in macroSteps) + { + result.Add(step.Name); + } + + return result; + } + + private MacroStep ParseStep(int value) + { + string name = string.Empty; + MacroStep.StepType type = MacroStep.StepType.ActDown; + MacroStep.StepOutput outType = MacroStep.StepOutput.Key; + + if (value >= 1000000000) + { + outType = MacroStep.StepOutput.Lightbar; + if (value > 1000000000) + { + type = MacroStep.StepType.ActDown; + string lb = value.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + name = $"Lightbar Color: {r},{g},{b}"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Reset Lightbar"; + } + } + else if (value >= 1000000) + { + outType = MacroStep.StepOutput.Rumble; + if (value > 1000000) + { + type = MacroStep.StepType.ActDown; + string r = value.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + name = $"Rumble {heavy}, {light} ({Math.Round((heavy * .75f + light * .25f) / 2.55f, 1)}%)"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Stop Rumble"; + } + } + else if (value >= 300) // ints over 300 used to delay + { + type = MacroStep.StepType.Wait; + outType = MacroStep.StepOutput.None; + name = $"Wait {(value - 300).ToString()} ms"; + } + else + { + // anything above 255 is not a key value + outType = value <= 255 ? MacroStep.StepOutput.Key : MacroStep.StepOutput.Button; + keydown.TryGetValue(value, out bool isdown); + if (!isdown) + { + type = MacroStep.StepType.ActDown; + keydown.Add(value, true); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + else + { + type = MacroStep.StepType.ActUp; + keydown.Remove(value); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + } + + MacroStep step = new MacroStep(value, name, type, outType); + return step; + } + + public void Reset() + { + loaded = false; + } + } + + public class MacroStep + { + public enum StepType : uint + { + ActDown, + ActUp, + Wait, + } + + public enum StepOutput : uint + { + None, + Key, + Button, + Rumble, + Lightbar, + } + + private string name; + private int value; + private StepType actType; + private StepOutput outputType; + + public string Name + { + get => name; + set + { + name = value; + NameChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler NameChanged; + public int Value + { + get => value; + set + { + this.value = value; + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ValueChanged; + public StepType ActType { get => actType; } + public StepOutput OutputType { get => outputType; } + + public MacroStep(int value, string name, StepType act, StepOutput output) + { + this.value = value; + this.name = name; + actType = act; + outputType = output; + + ValueChanged += MacroStep_ValueChanged; + } + + private void MacroStep_ValueChanged(object sender, EventArgs e) + { + if (actType == StepType.Wait) + { + Name = $"Wait {value-300}ms"; + } + else if (outputType == StepOutput.Rumble) + { + int result = value; + result -= 1000000; + int curHeavy = result / 1000; + int curLight = result - (curHeavy * 1000); + Name = $"Rumble {curHeavy},{curLight}"; + } + else if (outputType == StepOutput.Lightbar) + { + int temp = value - 1000000000; + int r = temp / 1000000; + temp -= (r * 1000000); + int g = temp / 1000; + temp -= (g * 1000); + int b = temp; + Name = $"Lightbar Color: {r},{g},{b}"; + } + } + } +} diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index 24b06ed4d9..3c057c08c1 100644 --- a/DS4Windows/DS4Control/Mapping.cs +++ b/DS4Windows/DS4Control/Mapping.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; using System.Diagnostics; using static DS4Windows.Global; +using System.Drawing; // Point struct + namespace DS4Windows { public class Mapping @@ -33,13 +34,19 @@ public class KeyPresses } public Dictionary keyPresses = new Dictionary(); - public void SavePrevious(bool performClear) + public void SaveToPrevious(bool performClear) { previousClicks = currentClicks; if (performClear) currentClicks.leftCount = currentClicks.middleCount = currentClicks.rightCount = currentClicks.fourthCount = currentClicks.fifthCount = currentClicks.wUpCount = currentClicks.wDownCount = currentClicks.toggleCount = 0; - foreach (KeyPresses kp in keyPresses.Values) + + //foreach (KeyPresses kp in keyPresses.Values) + Dictionary.ValueCollection keyValues = keyPresses.Values; + for (var keyEnum = keyValues.GetEnumerator(); keyEnum.MoveNext();) + //for (int i = 0, kpCount = keyValues.Count; i < kpCount; i++) { + //KeyPresses kp = keyValues.ElementAt(i); + KeyPresses kp = keyEnum.Current; kp.previous = kp.current; if (performClear) { @@ -55,8 +62,128 @@ public class ActionState public bool[] dev = new bool[4]; } + struct ControlToXInput + { + public DS4Controls ds4input; + public DS4Controls xoutput; + + public ControlToXInput(DS4Controls input, DS4Controls output) + { + ds4input = input; xoutput = output; + } + } + + static Queue[] customMapQueue = new Queue[4] + { + new Queue(), new Queue(), + new Queue(), new Queue() + }; + + struct DS4Vector2 + { + public double x; + public double y; + + public DS4Vector2(double x, double y) + { + this.x = x; + this.y = y; + } + } + + class DS4SquareStick + { + public DS4Vector2 current; + public DS4Vector2 squared; + + public DS4SquareStick() + { + current = new DS4Vector2(0.0, 0.0); + squared = new DS4Vector2(0.0, 0.0); + } + + public void CircleToSquare(double roundness) + { + const double PiOverFour = Math.PI / 4.0; + + // Determine the theta angle + double angle = Math.Atan2(current.y, -current.x); + angle += Math.PI; + double cosAng = Math.Cos(angle); + // Scale according to which wall we're clamping to + // X+ wall + if (angle <= PiOverFour || angle > 7.0 * PiOverFour) + { + double tempVal = 1.0 / cosAng; + //Console.WriteLine("1 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y+ wall + else if (angle > PiOverFour && angle <= 3.0 * PiOverFour) + { + double tempVal = 1.0 / Math.Sin(angle); + //Console.WriteLine("2 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // X- wall + else if (angle > 3.0 * PiOverFour && angle <= 5.0 * PiOverFour) + { + double tempVal = -1.0 / cosAng; + //Console.WriteLine("3 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y- wall + else if (angle > 5.0 * PiOverFour && angle <= 7.0 * PiOverFour) + { + double tempVal = -1.0 / Math.Sin(angle); + //Console.WriteLine("4 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + else return; + + //double lengthOld = Math.Sqrt((x * x) + (y * y)); + double length = current.x / cosAng; + //Console.WriteLine("LENGTH TEST ({0}) ({1}) {2}", lengthOld, length, (lengthOld == length).ToString()); + double factor = Math.Pow(length, roundness); + //double ogX = current.x, ogY = current.y; + current.x += (squared.x - current.x) * factor; + current.y += (squared.y - current.y) * factor; + //Console.WriteLine("INPUT: {0} {1} | {2} {3} | {4} {5} | {6} {7}", + // ogX, ogY, current.x, current.y, squared.x, squared.y, length, factor); + } + } + + static DS4SquareStick[] outSqrStk = new DS4SquareStick[4] { new DS4SquareStick(), + new DS4SquareStick(), new DS4SquareStick(), new DS4SquareStick()}; + + public static byte[] gyroStickX = new byte[4] { 128, 128, 128, 128 }; + public static byte[] gyroStickY = new byte[4] { 128, 128, 128, 128 }; + + static ReaderWriterLockSlim syncStateLock = new ReaderWriterLockSlim(); + public static SyntheticState globalState = new SyntheticState(); - public static SyntheticState[] deviceState = { new SyntheticState(), new SyntheticState(), new SyntheticState(), new SyntheticState() }; + public static SyntheticState[] deviceState = new SyntheticState[4] + { new SyntheticState(), new SyntheticState(), new SyntheticState(), + new SyntheticState() }; + + public static DS4StateFieldMapping[] fieldMappings = new DS4StateFieldMapping[4] { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] outputFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] previousFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; // TODO When we disconnect, process a null/dead state to release any keys or buttons. public static DateTime oldnow = DateTime.UtcNow; @@ -64,232 +191,299 @@ public class ActionState private static int wheel = 0, keyshelddown = 0; //mapcustom - public static bool[] pressedonce = new bool[261], macrodone = new bool[34]; + public static bool[] pressedonce = new bool[261], macrodone = new bool[38]; static bool[] macroControl = new bool[25]; + static uint macroCount = 0; + static Dictionary[] macroTaskQueue = new Dictionary[4] { new Dictionary(), new Dictionary(), new Dictionary(), new Dictionary() }; //actions - public static int[] fadetimer = { 0, 0, 0, 0 }; - public static int[] prevFadetimer = { 0, 0, 0, 0 }; + public static int[] fadetimer = new int[4] { 0, 0, 0, 0 }; + public static int[] prevFadetimer = new int[4] { 0, 0, 0, 0 }; public static DS4Color[] lastColor = new DS4Color[4]; public static List actionDone = new List(); - //public static List[] actionDone = { new List(), new List(), new List(), new List() }; - //public static bool[,] actionDone = new bool[4, 50]; public static SpecialAction[] untriggeraction = new SpecialAction[4]; public static DateTime[] nowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; public static DateTime[] oldnowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - public static int[] untriggerindex = { -1, -1, -1, -1 }; - public static DateTime[] oldnowKeyAct = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - private static bool tappedOnce = false, firstTouch = false, secondtouchbegin = false; - private static DateTime pastTime, firstTap, TimeofEnd; + public static int[] untriggerindex = new int[4] { -1, -1, -1, -1 }; + public static DateTime[] oldnowKeyAct = new DateTime[4] { DateTime.MinValue, + DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; + + private static DS4Controls[] shiftTriggerMapping = new DS4Controls[26] { DS4Controls.None, DS4Controls.Cross, DS4Controls.Circle, DS4Controls.Square, + DS4Controls.Triangle, DS4Controls.Options, DS4Controls.Share, DS4Controls.DpadUp, DS4Controls.DpadDown, + DS4Controls.DpadLeft, DS4Controls.DpadRight, DS4Controls.PS, DS4Controls.L1, DS4Controls.R1, DS4Controls.L2, + DS4Controls.R2, DS4Controls.L3, DS4Controls.R3, DS4Controls.TouchLeft, DS4Controls.TouchUpper, DS4Controls.TouchMulti, + DS4Controls.TouchRight, DS4Controls.GyroZNeg, DS4Controls.GyroZPos, DS4Controls.GyroXPos, DS4Controls.GyroXNeg, + }; + + private static int[] ds4ControlMapping = new int[38] { 0, // DS4Control.None + 16, // DS4Controls.LXNeg + 20, // DS4Controls.LXPos + 17, // DS4Controls.LYNeg + 21, // DS4Controls.LYPos + 18, // DS4Controls.RXNeg + 22, // DS4Controls.RXPos + 19, // DS4Controls.RYNeg + 23, // DS4Controls.RYPos + 3, // DS4Controls.L1 + 24, // DS4Controls.L2 + 5, // DS4Controls.L3 + 4, // DS4Controls.R1 + 25, // DS4Controls.R2 + 6, // DS4Controls.R3 + 13, // DS4Controls.Square + 14, // DS4Controls.Triangle + 15, // DS4Controls.Circle + 12, // DS4Controls.Cross + 7, // DS4Controls.DpadUp + 10, // DS4Controls.DpadRight + 8, // DS4Controls.DpadDown + 9, // DS4Controls.DpadLeft + 11, // DS4Controls.PS + 27, // DS4Controls.TouchLeft + 29, // DS4Controls.TouchUpper + 26, // DS4Controls.TouchMulti + 28, // DS4Controls.TouchRight + 1, // DS4Controls.Share + 2, // DS4Controls.Options + 31, // DS4Controls.GyroXPos + 30, // DS4Controls.GyroXNeg + 33, // DS4Controls.GyroZPos + 32, // DS4Controls.GyroZNeg + 34, // DS4Controls.SwipeLeft + 35, // DS4Controls.SwipeRight + 36, // DS4Controls.SwipeUp + 37 // DS4Controls.SwipeDown + }; + + // Define here to save some time processing. + // It is enough to feel a difference during gameplay. + // 201907: Commented out these temp variables because those were not actually used anymore (value was assigned but it was never used anywhere) + //private static int[] rsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //private static int[] lsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //static bool tempBool = false; + //private static double[] tempDoubleArray = new double[4] { 0.0, 0.0, 0.0, 0.0 }; + //private static int[] tempIntArray = new int[4] { 0, 0, 0, 0 }; - //special macros + // Special macros static bool altTabDone = true; - static DateTime altTabNow = DateTime.UtcNow, oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); + static DateTime altTabNow = DateTime.UtcNow, + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); - //mouse + // Mouse public static int mcounter = 34; public static int mouseaccel = 0; public static int prevmouseaccel = 0; private static double horizontalRemainder = 0.0, verticalRemainder = 0.0; + public const int MOUSESPEEDFACTOR = 48; + private const double MOUSESTICKOFFSET = 0.54; public static void Commit(int device) { SyntheticState state = deviceState[device]; - lock (globalState) - { - globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; - globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; - globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; - globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; - globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; - globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; - globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; - globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; - globalState.currentClicks.toggle = state.currentClicks.toggle; - - if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - } - else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - } - - if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - - if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 100); - oldnow = DateTime.UtcNow; - wheel = 100; - } - else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) - wheel = 0; + syncStateLock.EnterWriteLock(); - if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -100); - oldnow = DateTime.UtcNow; - wheel = -100; - } - if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) - wheel = 0; + globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; + globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; + globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; + globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; + globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; + globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; + globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; + globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; + globalState.currentClicks.toggle = state.currentClicks.toggle; + + if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + } + else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + } + + if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + + if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 120); + oldnow = DateTime.UtcNow; + wheel = 120; + } + else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) + wheel = 0; + + if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -120); + oldnow = DateTime.UtcNow; + wheel = -120; } + if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) + wheel = 0; + } - if (wheel != 0) //Continue mouse wheel movement + if (wheel != 0) //Continue mouse wheel movement + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) - { - oldnow = now; - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); - } + oldnow = now; + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); } + } + + // Merge and synthesize all key presses/releases that are present in this device's mapping. + // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule + Dictionary.KeyCollection kvpKeys = state.keyPresses.Keys; + //foreach (KeyValuePair kvp in state.keyPresses) + //for (int i = 0, keyCount = kvpKeys.Count; i < keyCount; i++) + for (var keyEnum = kvpKeys.GetEnumerator(); keyEnum.MoveNext();) + { + //UInt16 kvpKey = kvpKeys.ElementAt(i); + UInt16 kvpKey = keyEnum.Current; + SyntheticState.KeyPresses kvpValue = state.keyPresses[kvpKey]; - // Merge and synthesize all key presses/releases that are present in this device's mapping. - // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule - foreach (KeyValuePair kvp in state.keyPresses) + SyntheticState.KeyPresses gkp; + if (globalState.keyPresses.TryGetValue(kvpKey, out gkp)) { - SyntheticState.KeyPresses gkp; - if (globalState.keyPresses.TryGetValue(kvp.Key, out gkp)) - { - gkp.current.vkCount += kvp.Value.current.vkCount - kvp.Value.previous.vkCount; - gkp.current.scanCodeCount += kvp.Value.current.scanCodeCount - kvp.Value.previous.scanCodeCount; - gkp.current.repeatCount += kvp.Value.current.repeatCount - kvp.Value.previous.repeatCount; - gkp.current.toggle = kvp.Value.current.toggle; - gkp.current.toggleCount += kvp.Value.current.toggleCount - kvp.Value.previous.toggleCount; - } + gkp.current.vkCount += kvpValue.current.vkCount - kvpValue.previous.vkCount; + gkp.current.scanCodeCount += kvpValue.current.scanCodeCount - kvpValue.previous.scanCodeCount; + gkp.current.repeatCount += kvpValue.current.repeatCount - kvpValue.previous.repeatCount; + gkp.current.toggle = kvpValue.current.toggle; + gkp.current.toggleCount += kvpValue.current.toggleCount - kvpValue.previous.toggleCount; + } + else + { + gkp = new SyntheticState.KeyPresses(); + gkp.current = kvpValue.current; + globalState.keyPresses[kvpKey] = gkp; + } + if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) + { + if (gkp.current.scanCodeCount != 0) + InputMethods.performSCKeyPress(kvpKey); else + InputMethods.performKeyPress(kvpKey); + } + else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC + InputMethods.performSCKeyRelease(kvpKey); + else + InputMethods.performKeyRelease(kvpKey); + } + else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + { + if (gkp.current.scanCodeCount != 0) { - gkp = new SyntheticState.KeyPresses(); - gkp.current = kvp.Value.current; - globalState.keyPresses[kvp.Key] = gkp; - } - if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) - { - if (gkp.current.scanCodeCount != 0) - InputMethods.performSCKeyPress(kvp.Key); - else - InputMethods.performKeyPress(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performSCKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + else { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - InputMethods.performSCKeyRelease(kvp.Key); - else - InputMethods.performKeyRelease(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + } + else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition + ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms + { + if (keyshelddown == kvpKey) { - if (gkp.current.scanCodeCount != 0) - { - oldnow = DateTime.UtcNow; - InputMethods.performSCKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; - } - else + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) { - oldnow = DateTime.UtcNow; - InputMethods.performKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; + oldnow = now; + pressagain = true; } - } - else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition - ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms - { - if (keyshelddown == kvp.Key) + if (pressagain && gkp.current.scanCodeCount != 0) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { oldnow = now; - pressagain = true; - } - if (pressagain && gkp.current.scanCodeCount != 0) - { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performSCKeyPress(kvp.Key); - } + InputMethods.performSCKeyPress(kvpKey); } - else if (pressagain) + } + else if (pressagain) + { + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performKeyPress(kvp.Key); - } + oldnow = now; + InputMethods.performKeyPress(kvpKey); } } } - if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + } + if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - { - InputMethods.performSCKeyRelease(kvp.Key); - pressagain = false; - } - else - { - InputMethods.performKeyRelease(kvp.Key); - pressagain = false; - } + InputMethods.performSCKeyRelease(kvpKey); + pressagain = false; + } + else + { + InputMethods.performKeyRelease(kvpKey); + pressagain = false; } } - globalState.SavePrevious(false); } - state.SavePrevious(true); + globalState.SaveToPrevious(false); + + syncStateLock.ExitWriteLock(); + state.SaveToPrevious(true); } + public enum Click { None, Left, Middle, Right, Fourth, Fifth, WUP, WDOWN }; public static void MapClick(int device, Click mouseClick) { @@ -316,48 +510,19 @@ public static void MapClick(int device, Click mouseClick) case Click.WDOWN: deviceState[device].currentClicks.wDownCount++; break; + default: break; } } public static int DS4ControltoInt(DS4Controls ctrl) { - switch (ctrl) - { - case DS4Controls.Share: return 1; - case DS4Controls.Options: return 2; - case DS4Controls.L1: return 3; - case DS4Controls.R1: return 4; - case DS4Controls.L3: return 5; - case DS4Controls.R3: return 6; - case DS4Controls.DpadUp: return 7; - case DS4Controls.DpadDown: return 8; - case DS4Controls.DpadLeft: return 9; - case DS4Controls.DpadRight: return 10; - case DS4Controls.PS: return 11; - case DS4Controls.Cross: return 12; - case DS4Controls.Square: return 13; - case DS4Controls.Triangle: return 14; - case DS4Controls.Circle: return 15; - case DS4Controls.LXNeg: return 16; - case DS4Controls.LYNeg: return 17; - case DS4Controls.RXNeg: return 18; - case DS4Controls.RYNeg: return 19; - case DS4Controls.LXPos: return 20; - case DS4Controls.LYPos: return 21; - case DS4Controls.RXPos: return 22; - case DS4Controls.RYPos: return 23; - case DS4Controls.L2: return 24; - case DS4Controls.R2: return 25; - case DS4Controls.TouchMulti: return 26; - case DS4Controls.TouchLeft: return 27; - case DS4Controls.TouchRight: return 28; - case DS4Controls.TouchUpper: return 29; - case DS4Controls.GyroXNeg: return 30; - case DS4Controls.GyroXPos: return 31; - case DS4Controls.GyroZNeg: return 32; - case DS4Controls.GyroZPos: return 33; - } - return 0; + int result = 0; + if (ctrl >= DS4Controls.None && ctrl <= DS4Controls.SwipeDown) + { + result = ds4ControlMapping[(int)ctrl]; + } + + return result; } static double TValue(double value1, double value2, double percent) @@ -365,37 +530,44 @@ static double TValue(double value1, double value2, double percent) percent /= 100f; return value1 * percent + value2 * (1 - percent); } - static double Clamp(double min, double value, double max) + + private static int ClampInt(int min, int value, int max) { - if (value > max) - return max; - else if (value < min) - return min; - else - return value; + return (value < min) ? min : (value > max) ? max : value; } - public static DS4State SetCurveAndDeadzone(int device, DS4State cState) + public static DS4State SetCurveAndDeadzone(int device, DS4State cState, DS4State dState) { - DS4State dState = new DS4State(cState); + double rotation = /*tempDoubleArray[device] =*/ getLSRotation(device); + if (rotation > 0.0 || rotation < 0.0) + cState.rotateLSCoordinates(rotation); + + double rotationRS = /*tempDoubleArray[device] =*/ getRSRotation(device); + if (rotationRS > 0.0 || rotationRS < 0.0) + cState.rotateRSCoordinates(rotationRS); + + cState.CopyTo(dState); + //DS4State dState = new DS4State(cState); int x; int y; int curve; - if (LSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int lsCurve = getLSCurve(device); + if (lsCurve > 0) { x = cState.LX; y = cState.LY; float max = x + y; double curvex; double curvey; - curve = LSCurve[device]; + curve = lsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) { curvex = (x > 127.5f ? Math.Min(x, (x / max) * multimax) : Math.Max(x, (x / max) * multimin)); curvey = (y > 127.5f ? Math.Min(y, (y / max) * multimax) : Math.Max(y, (y / max) * multimin)); - //btnLSTrack.Location = new Point((int)(dpix * curvex / 2.09 + lbLSTrack.Location.X), (int)(dpiy * curvey / 2.09 + lbLSTrack.Location.Y)); } else { @@ -410,17 +582,21 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.LX = (byte)Math.Round(curvex, 0); dState.LY = (byte)Math.Round(curvey, 0); } - if (RSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int rsCurve = getRSCurve(device); + if (rsCurve > 0) { x = cState.RX; y = cState.RY; float max = x + y; double curvex; double curvey; - curve = RSCurve[device]; + curve = rsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) @@ -441,1587 +617,3826 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.RX = (byte)Math.Round(curvex, 0); dState.RY = (byte)Math.Round(curvey, 0); } - double ls = Math.Sqrt(Math.Pow(cState.LX - 127.5f, 2) + Math.Pow(cState.LY - 127.5f, 2)); - //deadzones - if (LSDeadzone[device] > 0 && ls < LSDeadzone[device]) - { - dState.LX = 127; - dState.LY = 127; - } - else if (LSDeadzone[device] < 0 && ls > 127.5f + LSDeadzone[device]) - { - double r = Math.Atan2((dState.LY - 127.5f), (dState.LX - 127.5f)); - dState.LX = (byte)(Math.Cos(r) * (127.5f + LSDeadzone[device]) + 127.5f); - dState.LY = (byte)(Math.Sin(r) * (127.5f + LSDeadzone[device]) + 127.5f); - } - //Console.WriteLine - double rs = Math.Sqrt(Math.Pow(cState.RX - 127.5f, 2) + Math.Pow(cState.RY - 127.5f, 2)); - if (RSDeadzone[device] > 0 && rs < LSDeadzone[device]) - { - dState.RX = 127; - dState.RY = 127; - } - else if (RSDeadzone[device] < 0 && rs > 127.5f + RSDeadzone[device]) - { - double r = Math.Atan2((dState.RY - 127.5f), (dState.RX - 127.5f)); - dState.RX = (byte)(Math.Cos(r) * (127.5f + RSDeadzone[device]) + 127.5f); - dState.RY = (byte)(Math.Sin(r) * (127.5f + RSDeadzone[device]) + 127.5f); - } - if (L2Deadzone[device] > 0 && cState.L2 < L2Deadzone[device]) - dState.L2 = 0; - if (R2Deadzone[device] > 0 && cState.R2 < R2Deadzone[device]) - dState.R2 = 0; - if (LSSens[device] != 1) - { - dState.LX = (byte)Clamp(0, LSSens[device] * (dState.LX - 127) + 127, 255); - dState.LY = (byte)Clamp(0, LSSens[device] * (dState.LY - 127) + 127, 255); - } - if (RSSens[device] != 1) - { - dState.RX = (byte)Clamp(0, RSSens[device] * (dState.RX - 127) + 127, 255); - dState.RY = (byte)Clamp(0, RSSens[device] * (dState.RY - 127) + 127, 255); - } - if (L2Sens[device] != 1) - dState.L2 = (byte)Clamp(0, L2Sens[device] * dState.L2, 255); - if (R2Sens[device] != 1) - dState.R2 = (byte)Clamp(0, R2Sens[device] * dState.R2, 255); - return dState; - } - private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) - { - switch (trigger) - { - case 1: return getBoolMapping(device, DS4Controls.Cross, cState, eState, tp); - case 2: return getBoolMapping(device, DS4Controls.Circle, cState, eState, tp); - case 3: return getBoolMapping(device, DS4Controls.Square, cState, eState, tp); - case 4: return getBoolMapping(device, DS4Controls.Triangle, cState, eState, tp); - case 5: return getBoolMapping(device, DS4Controls.Options, cState, eState, tp); - case 6: return getBoolMapping(device, DS4Controls.Share, cState, eState, tp); - case 7: return getBoolMapping(device, DS4Controls.DpadUp, cState, eState, tp); - case 8: return getBoolMapping(device, DS4Controls.DpadDown, cState, eState, tp); - case 9: return getBoolMapping(device, DS4Controls.DpadLeft, cState, eState, tp); - case 10: return getBoolMapping(device, DS4Controls.DpadRight, cState, eState, tp); - case 11: return getBoolMapping(device, DS4Controls.PS, cState, eState, tp); - case 12: return getBoolMapping(device, DS4Controls.L1, cState, eState, tp); - case 13: return getBoolMapping(device, DS4Controls.R1, cState, eState, tp); - case 14: return getBoolMapping(device, DS4Controls.L2, cState, eState, tp); - case 15: return getBoolMapping(device, DS4Controls.R2, cState, eState, tp); - case 16: return getBoolMapping(device, DS4Controls.L3, cState, eState, tp); - case 17: return getBoolMapping(device, DS4Controls.R3, cState, eState, tp); - case 18: return getBoolMapping(device, DS4Controls.TouchLeft, cState, eState, tp); - case 19: return getBoolMapping(device, DS4Controls.TouchUpper, cState, eState, tp); - case 20: return getBoolMapping(device, DS4Controls.TouchMulti, cState, eState, tp); - case 21: return getBoolMapping(device, DS4Controls.TouchRight, cState, eState, tp); - case 22: return getBoolMapping(device, DS4Controls.GyroZNeg, cState, eState, tp); - case 23: return getBoolMapping(device, DS4Controls.GyroZPos, cState, eState, tp); - case 24: return getBoolMapping(device, DS4Controls.GyroXPos, cState, eState, tp); - case 25: return getBoolMapping(device, DS4Controls.GyroXNeg, cState, eState, tp); - case 26: return cState.Touch1; - default: return false; - } - } - private static X360Controls getX360ControlsByName(string key) - { - X360Controls x3c; - if (Enum.TryParse(key, true, out x3c)) - return x3c; - switch (key) + /*int lsDeadzone = getLSDeadzone(device); + int lsAntiDead = getLSAntiDeadzone(device); + int lsMaxZone = getLSMaxzone(device); + */ + StickDeadZoneInfo lsMod = GetLSDeadInfo(device); + int lsDeadzone = lsMod.deadZone; + int lsAntiDead = lsMod.antiDeadZone; + int lsMaxZone = lsMod.maxZone; + double lsMaxOutput = lsMod.maxOutput; + + if (lsDeadzone > 0 || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) { - case "Back": return X360Controls.Back; - case "Left Stick": return X360Controls.LS; - case "Right Stick": return X360Controls.RS; - case "Start": return X360Controls.Start; - case "Up Button": return X360Controls.DpadUp; - case "Right Button": return X360Controls.DpadRight; - case "Down Button": return X360Controls.DpadDown; - case "Left Button": return X360Controls.DpadLeft; + double lsSquared = Math.Pow(cState.LX - 128f, 2) + Math.Pow(cState.LY - 128f, 2); + double lsDeadzoneSquared = Math.Pow(lsDeadzone, 2); + if (lsDeadzone > 0 && lsSquared <= lsDeadzoneSquared) + { + dState.LX = 128; + dState.LY = 128; + } + else if ((lsDeadzone > 0 && lsSquared > lsDeadzoneSquared) || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) + { + double r = Math.Atan2(-(dState.LY - 128.0), (dState.LX - 128.0)); + double maxXValue = dState.LX >= 128.0 ? 127.0 : -128; + double maxYValue = dState.LY >= 128.0 ? 127.0 : -128; + double ratio = lsMaxZone / 100.0; + double maxOutRatio = lsMaxOutput / 100.0; - case "Left Bumper": return X360Controls.LB; - case "Right Bumper": return X360Controls.RB; - case "Y Button": return X360Controls.Y; - case "B Button": return X360Controls.B; - case "A Button": return X360Controls.A; - case "X Button": return X360Controls.X; + double maxZoneXNegValue = (ratio * -128) + 128; + double maxZoneXPosValue = (ratio * 127) + 128; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.LX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.LY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); - case "Guide": return X360Controls.Guide; - case "Left X-Axis-": return X360Controls.LXNeg; - case "Left Y-Axis-": return X360Controls.LYNeg; - case "Right X-Axis-": return X360Controls.RXNeg; - case "Right Y-Axis-": return X360Controls.RYNeg; + double tempLsXDead = 0.0, tempLsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (lsDeadzone > 0) + { + tempLsXDead = Math.Abs(Math.Cos(r)) * (lsDeadzone / 127.0) * maxXValue; + tempLsYDead = Math.Abs(Math.Sin(r)) * (lsDeadzone / 127.0) * maxYValue; - case "Left X-Axis+": return X360Controls.LXPos; - case "Left Y-Axis+": return X360Controls.LYPos; - case "Right X-Axis+": return X360Controls.RXPos; - case "Right Y-Axis+": return X360Controls.RYPos; - case "Left Trigger": return X360Controls.LT; - case "Right Trigger": return X360Controls.RT; + if (lsSquared > lsDeadzoneSquared) + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = ((currentX - 128.0 - tempLsXDead) / (maxZoneX - tempLsXDead)); + tempOutputY = ((currentY - 128.0 - tempLsYDead) / (maxZoneY - tempLsYDead)); + } + } + else + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; + } - case "Left Mouse Button": return X360Controls.LeftMouse; - case "Right Mouse Button": return X360Controls.RightMouse; - case "Middle Mouse Button": return X360Controls.MiddleMouse; - case "4th Mouse Button": return X360Controls.FourthMouse; - case "5th Mouse Button": return X360Controls.FifthMouse; - case "Mouse Wheel Up": return X360Controls.WUP; - case "Mouse Wheel Down": return X360Controls.WDOWN; - case "Mouse Up": return X360Controls.MouseUp; - case "Mouse Down": return X360Controls.MouseDown; - case "Mouse Left": return X360Controls.MouseLeft; - case "Mouse Right": return X360Controls.MouseRight; - case "Unbound": return X360Controls.Unbound; + if (lsMaxOutput != 100.0) + { + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempLsXAntiDeadPercent = 0.0, tempLsYAntiDeadPercent = 0.0; + if (lsAntiDead > 0) + { + tempLsXAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempLsYAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + if (tempOutputX > 0.0) + { + dState.LX = (byte)((((1.0 - tempLsXAntiDeadPercent) * tempOutputX + tempLsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.LX = 128; + } + + if (tempOutputY > 0.0) + { + dState.LY = (byte)((((1.0 - tempLsYAntiDeadPercent) * tempOutputY + tempLsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.LY = 128; + } + } } - return X360Controls.Unbound; - } - /// - /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. - /// - static bool[] held = new bool[4]; - static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - - MappedState.LX = 127; - MappedState.LY = 127; - MappedState.RX = 127; - MappedState.RY = 127; - int MouseDeltaX = 0; - int MouseDeltaY = 0; - - SyntheticState deviceState = Mapping.deviceState[device]; - if (GetActions().Count > 0 && (ProfileActions[device].Count > 0 || !string.IsNullOrEmpty(tempprofilename[device]))) - MapCustomAction(device, cState, MappedState, eState, tp, ctrl); - if (ctrl.DS4Controllers[device] == null) return; - - cState.CopyTo(MappedState); - - List Cross = new List(); - List Circle = new List(); - List Square = new List(); - List Triangle = new List(); - List Options = new List(); - List Share = new List(); - List DpadUp = new List(); - List DpadDown = new List(); - List DpadLeft = new List(); - List DpadRight = new List(); - List PS = new List(); - List L1 = new List(); - List R1 = new List(); - List L2 = new List(); - List R2 = new List(); - List L3 = new List(); - List R3 = new List(); - List LXN = new List(); - List LXP = new List(); - List LYN = new List(); - List LYP = new List(); - List RXN = new List(); - List RXP = new List(); - List RYN = new List(); - List RYP = new List(); - DS4Controls usingExtra = DS4Controls.None; - foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + /*int rsDeadzone = getRSDeadzone(device); + int rsAntiDead = getRSAntiDeadzone(device); + int rsMaxZone = getRSMaxzone(device); + */ + StickDeadZoneInfo rsMod = GetRSDeadInfo(device); + int rsDeadzone = rsMod.deadZone; + int rsAntiDead = rsMod.antiDeadZone; + int rsMaxZone = rsMod.maxZone; + double rsMaxOutput = rsMod.maxOutput; + + if (rsDeadzone > 0 || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - object action = null; - DS4ControlSettings.ActionType actionType = 0; - DS4KeyType keyType = DS4KeyType.None; - if (dcs.shiftAction != null && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp)) - { - action = dcs.shiftAction; - actionType = dcs.shiftActionType; - keyType = dcs.shiftKeyType; - } - else if (dcs.action != null) + double rsSquared = Math.Pow(cState.RX - 128.0, 2) + Math.Pow(cState.RY - 128.0, 2); + double rsDeadzoneSquared = Math.Pow(rsDeadzone, 2); + if (rsDeadzone > 0 && rsSquared <= rsDeadzoneSquared) { - action = dcs.action; - actionType = dcs.actionType; - keyType = dcs.keyType; + dState.RX = 128; + dState.RY = 128; } - if (action != null) + else if ((rsDeadzone > 0 && rsSquared > rsDeadzoneSquared) || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - if (actionType == DS4ControlSettings.ActionType.Macro) + double r = Math.Atan2(-(dState.RY - 128.0), (dState.RX - 128.0)); + double maxXValue = dState.RX >= 128.0 ? 127 : -128; + double maxYValue = dState.RY >= 128.0 ? 127 : -128; + double ratio = rsMaxZone / 100.0; + double maxOutRatio = rsMaxOutput / 100.0; + + double maxZoneXNegValue = (ratio * -128.0) + 128.0; + double maxZoneXPosValue = (ratio * 127.0) + 128.0; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.RX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.RY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); + + double tempRsXDead = 0.0, tempRsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (rsDeadzone > 0) { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - PlayMacro(device, macroControl, string.Join("/", (int[])action), dcs.control, keyType); - } - else if (!getBoolMapping(device, dcs.control, cState, eState, tp)) + tempRsXDead = Math.Abs(Math.Cos(r)) * (rsDeadzone / 127.0) * maxXValue; + tempRsYDead = Math.Abs(Math.Sin(r)) * (rsDeadzone / 127.0) * maxYValue; + + if (rsSquared > rsDeadzoneSquared) { - EndMacro(device, macroControl, string.Join("/", (int[])action), dcs.control); + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = ((currentX - 128.0 - tempRsXDead) / (maxZoneX - tempRsXDead)); + tempOutputY = ((currentY - 128.0 - tempRsYDead) / (maxZoneY - tempRsYDead)); } } - else if (actionType == DS4ControlSettings.ActionType.Key) + else { - ushort value = ushort.Parse(action.ToString()); - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - SyntheticState.KeyPresses kp; - if (!deviceState.keyPresses.TryGetValue(value, out kp)) - deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); - if (keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (!pressedonce[value]) - { - kp.current.toggle = !kp.current.toggle; - pressedonce[value] = true; - } - kp.current.toggleCount++; - } - kp.current.repeatCount++; - } - else - pressedonce[value] = false; + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; } - else if (actionType == DS4ControlSettings.ActionType.Button) + + if (rsMaxOutput != 100.0) { - int keyvalue = 0; - bool isAnalog = dcs.control.ToString().Contains("LX") || - dcs.control.ToString().Contains("RX") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("R2") || - dcs.control.ToString().Contains("L2") || - dcs.control.ToString().Contains("Gyro"); - switch (getX360ControlsByName(action.ToString())) - { - case X360Controls.A: Cross.Add(dcs.control); break; - case X360Controls.B: Circle.Add(dcs.control); break; - case X360Controls.X: Square.Add(dcs.control); break; - case X360Controls.Y: Triangle.Add(dcs.control); break; - case X360Controls.LB: L1.Add(dcs.control); break; - case X360Controls.LS: L3.Add(dcs.control); break; - case X360Controls.RB: R1.Add(dcs.control); break; - case X360Controls.RS: R3.Add(dcs.control); break; - case X360Controls.DpadUp: DpadUp.Add(dcs.control); break; - case X360Controls.DpadDown: DpadDown.Add(dcs.control); break; - case X360Controls.DpadLeft: DpadLeft.Add(dcs.control); break; - case X360Controls.DpadRight: DpadRight.Add(dcs.control); break; - case X360Controls.Start: Options.Add(dcs.control); break; - case X360Controls.Guide: PS.Add(dcs.control); break; - case X360Controls.Back: Share.Add(dcs.control); break; - case X360Controls.LXNeg: LXN.Add(dcs.control); break; - case X360Controls.LYNeg: LYN.Add(dcs.control); break; - case X360Controls.RXNeg: RXN.Add(dcs.control); break; - case X360Controls.RYNeg: RYN.Add(dcs.control); break; - case X360Controls.LXPos: LXP.Add(dcs.control); break; - case X360Controls.LYPos: LYP.Add(dcs.control); break; - case X360Controls.RXPos: RXP.Add(dcs.control); break; - case X360Controls.RYPos: RYP.Add(dcs.control); break; - case X360Controls.LT: L2.Add(dcs.control); break; - case X360Controls.RT: R2.Add(dcs.control); break; - case X360Controls.LeftMouse: - keyvalue = 256; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.leftCount++; - break; - case X360Controls.RightMouse: - keyvalue = 257; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.rightCount++; - break; - case X360Controls.MiddleMouse: - keyvalue = 258; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.middleCount++; - break; - case X360Controls.FourthMouse: - keyvalue = 259; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fourthCount++; - break; - case X360Controls.FifthMouse: - keyvalue = 260; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fifthCount++; - break; - case X360Controls.WUP: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); - else - deviceState.currentClicks.wUpCount++; - break; - case X360Controls.WDOWN: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); - else - deviceState.currentClicks.wDownCount++; - break; - case X360Controls.MouseUp: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 0); - MouseDeltaY = -Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseDown: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 1); - MouseDeltaY = Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseLeft: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 2); - MouseDeltaX = -Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - case X360Controls.MouseRight: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 3); - MouseDeltaX = Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - } - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - if (!pressedonce[keyvalue]) - { - deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; - pressedonce[keyvalue] = true; - } - deviceState.currentClicks.toggleCount++; - } - else - { - pressedonce[keyvalue] = false; - } - } - resetToDefaultValue(dcs.control, MappedState); // erase default mappings for things that are remapped + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempRsXAntiDeadPercent = 0.0, tempRsYAntiDeadPercent = 0.0; + if (rsAntiDead > 0) + { + tempRsXAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempRsYAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + + if (tempOutputX > 0.0) + { + dState.RX = (byte)((((1.0 - tempRsXAntiDeadPercent) * tempOutputX + tempRsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.RX = 128; + } + + if (tempOutputY > 0.0) + { + dState.RY = (byte)((((1.0 - tempRsYAntiDeadPercent) * tempOutputY + tempRsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.RY = 128; } } + } - if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + /*byte l2Deadzone = getL2Deadzone(device); + int l2AntiDeadzone = getL2AntiDeadzone(device); + int l2Maxzone = getL2Maxzone(device); + */ + + TriggerDeadZoneZInfo l2ModInfo = GetL2ModInfo(device); + byte l2Deadzone = l2ModInfo.deadZone; + int l2AntiDeadzone = l2ModInfo.antiDeadZone; + int l2Maxzone = l2ModInfo.maxZone; + double l2MaxOutput = l2ModInfo.maxOutput; + if (l2Deadzone > 0 || l2AntiDeadzone > 0 || l2Maxzone != 100 || l2MaxOutput != 100.0) + { + double tempL2Output = cState.L2 / 255.0; + double tempL2AntiDead = 0.0; + double ratio = l2Maxzone / 100.0; + double maxValue = 255.0 * ratio; + + if (l2Deadzone > 0) { - bool shiftE = dcs.shiftExtras != "0,0,0,0,0,0,0,0" && dcs.shiftExtras != "" && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp); - bool regE = dcs.extras != "0,0,0,0,0,0,0,0" && dcs.extras != ""; - if ((regE || shiftE) && getBoolMapping(device, dcs.control, cState, eState, tp)) + if (cState.L2 > l2Deadzone) { - usingExtra = dcs.control; - string p; - if (shiftE) - p = dcs.shiftExtras; - else - p = dcs.extras; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[device] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - ctrl.setRumble((byte)extras[0], (byte)extras[1], device); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[device] = color; - DS4LightBar.forcedFlash[device] = (byte)extras[6]; - DS4LightBar.forcelight[device] = true; - } - if (extras[7] == 1) - { - if (oldmouse[device] == -1) - oldmouse[device] = ButtonMouseSensitivity[device]; - ButtonMouseSensitivity[device] = extras[8]; - } - } - catch { } + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = (current - l2Deadzone) / (maxValue - l2Deadzone); } - else if ((regE || shiftE) && held[device]) + else { - DS4LightBar.forcelight[device] = false; - DS4LightBar.forcedFlash[device] = 0; - ButtonMouseSensitivity[device] = oldmouse[device]; - oldmouse[device] = -1; - ctrl.setRumble(0, 0, device); - held[device] = false; - usingExtra = DS4Controls.None; + tempL2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = current / maxValue; + } + + if (l2MaxOutput != 100.0) + { + double maxOutRatio = l2MaxOutput / 100.0; + tempL2Output = Math.Min(Math.Max(tempL2Output, 0.0), maxOutRatio); + } + + if (l2AntiDeadzone > 0) + { + tempL2AntiDead = l2AntiDeadzone * 0.01; + } + + if (tempL2Output > 0.0) + { + dState.L2 = (byte)(((1.0 - tempL2AntiDead) * tempL2Output + tempL2AntiDead) * 255.0); + } + else + { + dState.L2 = 0; + } + } + + /*byte r2Deadzone = getR2Deadzone(device); + int r2AntiDeadzone = getR2AntiDeadzone(device); + int r2Maxzone = getR2Maxzone(device); + */ + TriggerDeadZoneZInfo r2ModInfo = GetR2ModInfo(device); + byte r2Deadzone = r2ModInfo.deadZone; + int r2AntiDeadzone = r2ModInfo.antiDeadZone; + int r2Maxzone = r2ModInfo.maxZone; + double r2MaxOutput = r2ModInfo.maxOutput; + if (r2Deadzone > 0 || r2AntiDeadzone > 0 || r2Maxzone != 100 || r2MaxOutput != 100.0) + { + double tempR2Output = cState.R2 / 255.0; + double tempR2AntiDead = 0.0; + double ratio = r2Maxzone / 100.0; + double maxValue = 255 * ratio; + + if (r2Deadzone > 0) + { + if (cState.R2 > r2Deadzone) + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = (current - r2Deadzone) / (maxValue - r2Deadzone); + } + else + { + tempR2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = current / maxValue; + } + + if (r2MaxOutput != 100.0) + { + double maxOutRatio = r2MaxOutput / 100.0; + tempR2Output = Math.Min(Math.Max(tempR2Output, 0.0), maxOutRatio); + } + + if (r2AntiDeadzone > 0) + { + tempR2AntiDead = r2AntiDeadzone * 0.01; + } + + if (tempR2Output > 0.0) + { + dState.R2 = (byte)(((1.0 - tempR2AntiDead) * tempR2Output + tempR2AntiDead) * 255.0); + } + else + { + dState.R2 = 0; + } + } + + double lsSens = getLSSens(device); + if (lsSens != 1.0) + { + dState.LX = (byte)Global.Clamp(0, lsSens * (dState.LX - 128.0) + 128.0, 255); + dState.LY = (byte)Global.Clamp(0, lsSens * (dState.LY - 128.0) + 128.0, 255); + } + + double rsSens = getRSSens(device); + if (rsSens != 1.0) + { + dState.RX = (byte)Global.Clamp(0, rsSens * (dState.RX - 128.0) + 128.0, 255); + dState.RY = (byte)Global.Clamp(0, rsSens * (dState.RY - 128.0) + 128.0, 255); + } + + double l2Sens = getL2Sens(device); + if (l2Sens != 1.0) + dState.L2 = (byte)Global.Clamp(0, l2Sens * dState.L2, 255); + + double r2Sens = getR2Sens(device); + if (r2Sens != 1.0) + dState.R2 = (byte)Global.Clamp(0, r2Sens * dState.R2, 255); + + SquareStickInfo squStk = GetSquareStickInfo(device); + if (squStk.lsMode && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.lsRoundness); + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + dState.LX = (byte)(tempX * capX + 128.0); + dState.LY = (byte)(tempY * capY + 128.0); + } + + int lsOutCurveMode = getLsOutCurveMode(device); + if (lsOutCurveMode > 0 && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (lsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; + } + + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.LX = (byte)(outputX * capX + 128.0); + dState.LY = (byte)(outputY * capY + 128.0); + } + else if (lsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.LX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.LX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 6) + { + dState.LX = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LX]; + dState.LY = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LY]; + } + } + + if (squStk.rsMode && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.rsRoundness); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + dState.RX = (byte)(tempX * capX + 128.0); + dState.RY = (byte)(tempY * capY + 128.0); + } + + int rsOutCurveMode = getRsOutCurveMode(device); + if (rsOutCurveMode > 0 && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (rsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; } + + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.RX = (byte)(outputX * capX + 128.0); + dState.RY = (byte)(outputY * capY + 128.0); + } + else if (rsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.RX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.RX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 6) + { + dState.RX = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RX]; + dState.RY = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RY]; + } + } + + int l2OutCurveMode = getL2OutCurveMode(device); + if (l2OutCurveMode > 0 && dState.L2 != 0) + { + double temp = dState.L2 / 255.0; + if (l2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 2) + { + double output = temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 6) + { + dState.L2 = l2OutBezierCurveObj[device].arrayBezierLUT[dState.L2]; + } + } + + int r2OutCurveMode = getR2OutCurveMode(device); + if (r2OutCurveMode > 0 && dState.R2 != 0) + { + double temp = dState.R2 / 255.0; + if (r2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 2) + { + double output = temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 6) + { + dState.R2 = r2OutBezierCurveObj[device].arrayBezierLUT[dState.R2]; + } + } + + + bool sOff = /*tempBool =*/ isUsingSAforMouse(device); + if (sOff == false) + { + int SXD = (int)(128d * getSXDeadzone(device)); + int SZD = (int)(128d * getSZDeadzone(device)); + double SXMax = getSXMaxzone(device); + double SZMax = getSZMaxzone(device); + double sxAntiDead = getSXAntiDeadzone(device); + double szAntiDead = getSZAntiDeadzone(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + int result = 0; + + int gyroX = cState.Motion.accelX, gyroZ = cState.Motion.accelZ; + int absx = Math.Abs(gyroX), absz = Math.Abs(gyroZ); + + if (SXD > 0 || SXMax < 1.0 || sxAntiDead > 0) + { + int maxValue = (int)(SXMax * 128d); + if (absx > SXD) + { + double ratioX = absx < maxValue ? (absx - SXD) / (double)(maxValue - SXD) : 1.0; + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * ((1.0 - sxAntiDead) * ratioX + sxAntiDead)); + } + else + { + dState.Motion.outputAccelX = 0; + } + } + else + { + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * (absx / 128d)); + } + + if (SZD > 0 || SZMax < 1.0 || szAntiDead > 0) + { + int maxValue = (int)(SZMax * 128d); + if (absz > SZD) + { + double ratioZ = absz < maxValue ? (absz - SZD) / (double)(maxValue - SZD) : 1.0; + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * ((1.0 - szAntiDead) * ratioZ + szAntiDead)); + } + else + { + dState.Motion.outputAccelZ = 0; + } + } + else + { + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * (absz / 128d)); + } + + int sxOutCurveMode = getSXOutCurveMode(device); + if (sxOutCurveMode > 0) + { + double temp = dState.Motion.outputAccelX / 128.0; + double sign = Math.Sign(temp); + if (sxOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelX = (int)(output * sign * 128.0); + } + else if (sxOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelX = (int)(-1.0 * output * + sign * 128.0); + } + else if (sxOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelX = (int)(output * + sign * 128.0); + } + else if (sxOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelX); + dState.Motion.outputAccelX = sxOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelX), 128)] * signSA; + } + } + + int szOutCurveMode = getSZOutCurveMode(device); + if (szOutCurveMode > 0 && dState.Motion.outputAccelZ != 0) + { + double temp = dState.Motion.outputAccelZ / 128.0; + double sign = Math.Sign(temp); + if (szOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelZ = (int)(output * sign * 128.0); + } + else if (szOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelZ = (int)(-1.0 * output * + sign * 128.0); + } + else if (szOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelZ = (int)(output * + sign * 128.0); + } + else if (szOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelZ); + dState.Motion.outputAccelZ = szOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelZ), 128)] * signSA; + } + } + } + + return dState; + } + + /* TODO: Possibly remove usage of this version of the method */ + private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping(device, ds, cState, eState, tp); + } + + return result; + } + + private static bool ShiftTrigger2(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMapping) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else if (trigger < 26) + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping2(device, ds, cState, eState, tp, fieldMapping); + } + else if (trigger == 26) + { + result = cState.Touch1Finger; + } + + return result; + } + + private static X360Controls getX360ControlsByName(string key) + { + X360Controls x3c; + if (Enum.TryParse(key, true, out x3c)) + return x3c; + + switch (key) + { + case "Back": return X360Controls.Back; + case "Left Stick": return X360Controls.LS; + case "Right Stick": return X360Controls.RS; + case "Start": return X360Controls.Start; + case "Up Button": return X360Controls.DpadUp; + case "Right Button": return X360Controls.DpadRight; + case "Down Button": return X360Controls.DpadDown; + case "Left Button": return X360Controls.DpadLeft; + + case "Left Bumper": return X360Controls.LB; + case "Right Bumper": return X360Controls.RB; + case "Y Button": return X360Controls.Y; + case "B Button": return X360Controls.B; + case "A Button": return X360Controls.A; + case "X Button": return X360Controls.X; + + case "Guide": return X360Controls.Guide; + case "Left X-Axis-": return X360Controls.LXNeg; + case "Left Y-Axis-": return X360Controls.LYNeg; + case "Right X-Axis-": return X360Controls.RXNeg; + case "Right Y-Axis-": return X360Controls.RYNeg; + + case "Left X-Axis+": return X360Controls.LXPos; + case "Left Y-Axis+": return X360Controls.LYPos; + case "Right X-Axis+": return X360Controls.RXPos; + case "Right Y-Axis+": return X360Controls.RYPos; + case "Left Trigger": return X360Controls.LT; + case "Right Trigger": return X360Controls.RT; + + case "Left Mouse Button": return X360Controls.LeftMouse; + case "Right Mouse Button": return X360Controls.RightMouse; + case "Middle Mouse Button": return X360Controls.MiddleMouse; + case "4th Mouse Button": return X360Controls.FourthMouse; + case "5th Mouse Button": return X360Controls.FifthMouse; + case "Mouse Wheel Up": return X360Controls.WUP; + case "Mouse Wheel Down": return X360Controls.WDOWN; + case "Mouse Up": return X360Controls.MouseUp; + case "Mouse Down": return X360Controls.MouseDown; + case "Mouse Left": return X360Controls.MouseLeft; + case "Mouse Right": return X360Controls.MouseRight; + case "Unbound": return X360Controls.Unbound; + default: break; + } + + return X360Controls.Unbound; + } + + /// + /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. + /// + static bool[] held = new bool[4]; + static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; + public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, + Mouse tp, ControlService ctrl) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + double tempMouseDeltaX = 0.0; + double tempMouseDeltaY = 0.0; + int mouseDeltaX = 0; + int mouseDeltaY = 0; + + cState.calculateStickAngles(); + DS4StateFieldMapping fieldMapping = fieldMappings[device]; + fieldMapping.populateFieldMapping(cState, eState, tp); + DS4StateFieldMapping outputfieldMapping = outputFieldMappings[device]; + outputfieldMapping.populateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping fieldMapping = new DS4StateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping outputfieldMapping = new DS4StateFieldMapping(cState, eState, tp); + + SyntheticState deviceState = Mapping.deviceState[device]; + if (getProfileActionCount(device) > 0 || useTempProfile[device]) + MapCustomAction(device, cState, MappedState, eState, tp, ctrl, fieldMapping, outputfieldMapping); + //if (ctrl.DS4Controllers[device] == null) return; + + //cState.CopyTo(MappedState); + + //Dictionary tempControlDict = new Dictionary(); + //MultiValueDict tempControlDict = new MultiValueDict(); + DS4Controls usingExtra = DS4Controls.None; + List tempSettingsList = getDS4CSettings(device); + //foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + //for (int settingIndex = 0, arlen = tempSettingsList.Count; settingIndex < arlen; settingIndex++) + for (var settingEnum = tempSettingsList.GetEnumerator(); settingEnum.MoveNext();) + { + //DS4ControlSettings dcs = tempSettingsList[settingIndex]; + DS4ControlSettings dcs = settingEnum.Current; + object action = null; + DS4ControlSettings.ActionType actionType = 0; + DS4KeyType keyType = DS4KeyType.None; + if (dcs.shiftAction != null && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping)) + { + action = dcs.shiftAction; + actionType = dcs.shiftActionType; + keyType = dcs.shiftKeyType; + } + else if (dcs.action != null) + { + action = dcs.action; + actionType = dcs.actionType; + keyType = dcs.keyType; + } + + if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + { + bool shiftE = !string.IsNullOrEmpty(dcs.shiftExtras) && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping); + bool regE = !string.IsNullOrEmpty(dcs.extras); + if ((regE || shiftE) && getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + usingExtra = dcs.control; + string p; + if (shiftE) + p = dcs.shiftExtras; + else + p = dcs.extras; + + string[] extraS = p.Split(','); + int extrasSLen = extraS.Length; + int[] extras = new int[extrasSLen]; + for (int i = 0; i < extrasSLen; i++) + { + int b; + if (int.TryParse(extraS[i], out b)) + extras[i] = b; + } + + held[device] = true; + try + { + if (!(extras[0] == extras[1] && extras[1] == 0)) + ctrl.setRumble((byte)extras[0], (byte)extras[1], device); + + if (extras[2] == 1) + { + DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; + DS4LightBar.forcedColor[device] = color; + DS4LightBar.forcedFlash[device] = (byte)extras[6]; + DS4LightBar.forcelight[device] = true; + } + + if (extras[7] == 1) + { + if (oldmouse[device] == -1) + oldmouse[device] = ButtonMouseSensitivity[device]; + ButtonMouseSensitivity[device] = extras[8]; + } + } + catch { } + } + else if ((regE || shiftE) && held[device]) + { + DS4LightBar.forcelight[device] = false; + DS4LightBar.forcedFlash[device] = 0; + if (oldmouse[device] != -1) + { + ButtonMouseSensitivity[device] = oldmouse[device]; + oldmouse[device] = -1; + } + + ctrl.setRumble(0, 0, device); + held[device] = false; + usingExtra = DS4Controls.None; + } + } + + if (action != null) + { + if (actionType == DS4ControlSettings.ActionType.Macro) + { + bool active = getBoolMapping2(device, dcs.control, cState, eState, tp, fieldMapping); + if (active) + { + PlayMacro(device, macroControl, String.Empty, null, (int[])action, dcs.control, keyType); + } + else + { + EndMacro(device, macroControl, (int[])action, dcs.control); + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Key) + { + ushort value = Convert.ToUInt16(action); + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + SyntheticState.KeyPresses kp; + if (!deviceState.keyPresses.TryGetValue(value, out kp)) + deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); + + if (keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (!pressedonce[value]) + { + kp.current.toggle = !kp.current.toggle; + pressedonce[value] = true; + } + kp.current.toggleCount++; + } + kp.current.repeatCount++; + } + else + pressedonce[value] = false; + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Button) + { + int keyvalue = 0; + bool isAnalog = false; + + if (dcs.control >= DS4Controls.LXNeg && dcs.control <= DS4Controls.RYPos) + { + isAnalog = true; + } + else if (dcs.control == DS4Controls.L2 || dcs.control == DS4Controls.R2) + { + isAnalog = true; + } + else if (dcs.control >= DS4Controls.GyroXPos && dcs.control <= DS4Controls.GyroZNeg) + { + isAnalog = true; + } + + X360Controls xboxControl = X360Controls.None; + if (action is X360Controls) + { + xboxControl = (X360Controls)action; + } + else if (action is string) + { + xboxControl = getX360ControlsByName(action.ToString()); + } + + if (xboxControl >= X360Controls.LXNeg && xboxControl <= X360Controls.Start) + { + DS4Controls tempDS4Control = reverseX360ButtonMapping[(int)xboxControl]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, tempDS4Control)); + //tempControlDict.Add(dcs.control, tempDS4Control); + } + else if (xboxControl >= X360Controls.LeftMouse && xboxControl <= X360Controls.WDOWN) + { + switch (xboxControl) + { + case X360Controls.LeftMouse: + { + keyvalue = 256; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.leftCount++; + + break; + } + case X360Controls.RightMouse: + { + keyvalue = 257; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.rightCount++; + + break; + } + case X360Controls.MiddleMouse: + { + keyvalue = 258; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.middleCount++; + + break; + } + case X360Controls.FourthMouse: + { + keyvalue = 259; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fourthCount++; + + break; + } + case X360Controls.FifthMouse: + { + keyvalue = 260; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fifthCount++; + + break; + } + case X360Controls.WUP: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); + else + deviceState.currentClicks.wUpCount++; + } + + break; + } + case X360Controls.WDOWN: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); + else + deviceState.currentClicks.wDownCount++; + } + + break; + } + + default: break; + } + } + else if (xboxControl >= X360Controls.MouseUp && xboxControl <= X360Controls.MouseRight) + { + switch (xboxControl) + { + case X360Controls.MouseUp: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 0, ctrl); + tempMouseDeltaY = -Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseDown: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 1, ctrl); + tempMouseDeltaY = Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseLeft: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 2, ctrl); + tempMouseDeltaX = -Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + case X360Controls.MouseRight: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 3, ctrl); + tempMouseDeltaX = Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + + default: break; + } + } + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (!pressedonce[keyvalue]) + { + deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; + pressedonce[keyvalue] = true; + } + deviceState.currentClicks.toggleCount++; + } + else + { + pressedonce[keyvalue] = false; + } + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + } + else + { + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[(int)dcs.control]; + if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + //if (dcs.control > DS4Controls.None && dcs.control < DS4Controls.L1) + { + //int current = (int)dcs.control; + //outputfieldMapping.axisdirs[current] = fieldMapping.axisdirs[current]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, dcs.control)); + } + } + } + + Queue tempControl = customMapQueue[device]; + unchecked + { + for (int i = 0, len = tempControl.Count; i < len; i++) + //while(tempControl.Any()) + { + ControlToXInput tempMap = tempControl.Dequeue(); + int controlNum = (int)tempMap.ds4input; + int tempOutControl = (int)tempMap.xoutput; + if (tempMap.xoutput >= DS4Controls.LXNeg && tempMap.xoutput <= DS4Controls.RYPos) + { + const byte axisDead = 128; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[tempOutControl]; + bool alt = controlType == DS4StateFieldMapping.ControlType.AxisDir && tempOutControl % 2 == 0 ? true : false; + byte axisMapping = getXYAxisMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping, alt); + if (axisMapping != axisDead) + { + int controlRelation = tempOutControl % 2 == 0 ? tempOutControl - 1 : tempOutControl + 1; + outputfieldMapping.axisdirs[tempOutControl] = axisMapping; + outputfieldMapping.axisdirs[controlRelation] = axisMapping; + } + } + else + { + if (tempMap.xoutput == DS4Controls.L2 || tempMap.xoutput == DS4Controls.R2) + { + const byte axisZero = 0; + byte axisMapping = getByteMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (axisMapping != axisZero) + outputfieldMapping.triggers[tempOutControl] = axisMapping; + } + else + { + bool value = getBoolMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (value) + outputfieldMapping.buttons[tempOutControl] = value; + } + } + } + } + + outputfieldMapping.populateState(MappedState); + + if (macroCount > 0) + { + if (macroControl[00]) MappedState.Cross = true; + if (macroControl[01]) MappedState.Circle = true; + if (macroControl[02]) MappedState.Square = true; + if (macroControl[03]) MappedState.Triangle = true; + if (macroControl[04]) MappedState.Options = true; + if (macroControl[05]) MappedState.Share = true; + if (macroControl[06]) MappedState.DpadUp = true; + if (macroControl[07]) MappedState.DpadDown = true; + if (macroControl[08]) MappedState.DpadLeft = true; + if (macroControl[09]) MappedState.DpadRight = true; + if (macroControl[10]) MappedState.PS = true; + if (macroControl[11]) MappedState.L1 = true; + if (macroControl[12]) MappedState.R1 = true; + if (macroControl[13]) MappedState.L2 = 255; + if (macroControl[14]) MappedState.R2 = 255; + if (macroControl[15]) MappedState.L3 = true; + if (macroControl[16]) MappedState.R3 = true; + if (macroControl[17]) MappedState.LX = 255; + if (macroControl[18]) MappedState.LX = 0; + if (macroControl[19]) MappedState.LY = 255; + if (macroControl[20]) MappedState.LY = 0; + if (macroControl[21]) MappedState.RX = 255; + if (macroControl[22]) MappedState.RX = 0; + if (macroControl[23]) MappedState.RY = 255; + if (macroControl[24]) MappedState.RY = 0; + } + + if (GetSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None) + { + MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl); + } + + ref byte gyroTempX = ref gyroStickX[device]; + if (gyroTempX != 128) + { + if (MappedState.RX != 128) + MappedState.RX = Math.Abs(gyroTempX - 128) > Math.Abs(MappedState.RX - 128) ? + gyroTempX : MappedState.RX; + else + MappedState.RX = gyroTempX; + } + + ref byte gyroTempY = ref gyroStickY[device]; + if (gyroTempY != 128) + { + if (MappedState.RY != 128) + MappedState.RY = Math.Abs(gyroTempY - 128) > Math.Abs(MappedState.RY - 128) ? + gyroTempY : MappedState.RY; + else + MappedState.RY = gyroTempY; + } + + gyroTempX = gyroTempY = 128; + + calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY, + out mouseDeltaX, out mouseDeltaY); + if (mouseDeltaX != 0 || mouseDeltaY != 0) + { + InputMethods.MoveCursorBy(mouseDeltaX, mouseDeltaY); + } + } + + private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) + { + return shift ? false : GetDS4Action(device, dc, false) == null; + } + + private static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, + DS4StateExposed eState, Mouse tp, ControlService ctrl, DS4StateFieldMapping fieldMapping, DS4StateFieldMapping outputfieldMapping) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + try + { + int actionDoneCount = actionDone.Count; + int totalActionCount = GetActions().Count; + DS4StateFieldMapping previousFieldMapping = null; + List profileActions = getProfileActions(device); + //foreach (string actionname in profileActions) + for (int actionIndex = 0, profileListLen = profileActions.Count; + actionIndex < profileListLen; actionIndex++) + { + //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); + //SpecialAction action = GetAction(actionname); + //int index = GetActionIndexOf(actionname); + string actionname = profileActions[actionIndex]; + SpecialAction action = GetProfileAction(device, actionname); + int index = GetProfileActionIndexOf(device, actionname); + + if (actionDoneCount < index + 1) + { + actionDone.Add(new ActionState()); + actionDoneCount++; + } + else if (actionDoneCount > totalActionCount) + { + actionDone.RemoveAt(actionDoneCount - 1); + actionDoneCount--; + } + + if (action == null) + { + continue; + } + + double time = 0.0; + //If a key or button is assigned to the trigger, a key special action is used like + //a quick tap to use and hold to use the regular custom button/key + bool triggerToBeTapped = action.typeID == SpecialAction.ActionTypeId.None && action.trigger.Count == 1 && + GetDS4Action(device, action.trigger[0], false) == null; + if (!(action.typeID == SpecialAction.ActionTypeId.None || index < 0)) + { + bool triggeractivated = true; + if (action.delayTime > 0.0) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + time = action.delayTime; + nowAction[device] = DateTime.UtcNow; + if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) + triggeractivated = true; + } + else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) + oldnowAction[device] = DateTime.UtcNow; + } + else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + oldnowKeyAct[device] = DateTime.UtcNow; + } + } + else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + DateTime now = DateTime.UtcNow; + if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) + { + await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up + triggeractivated = true; + oldnowKeyAct[device] = DateTime.MinValue; + } + else if (!subtriggeractivated) + oldnowKeyAct[device] = DateTime.MinValue; + } + else + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + triggeractivated = false; + break; + } + } + + // If special action macro is set to run on key release then activate the trigger status only when the trigger key is released + if (action.typeID == SpecialAction.ActionTypeId.Macro && action.pressRelease && action.firstTouch) + triggeractivated = !triggeractivated; + } + + bool utriggeractivated = true; + int uTriggerCount = action.uTrigger.Count; + if (action.typeID == SpecialAction.ActionTypeId.Key && uTriggerCount > 0) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + if (action.pressRelease) utriggeractivated = !utriggeractivated; + } + + bool actionFound = false; + if (triggeractivated) + { + if (action.typeID == SpecialAction.ActionTypeId.Program) + { + actionFound = true; + + if (!actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + if (!string.IsNullOrEmpty(action.extra)) + Process.Start(action.details, action.extra); + else + Process.Start(action.details); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Profile) + { + actionFound = true; + + if (!actionDone[index].dev[device] && (!useTempProfile[device] || untriggeraction[device] == null || untriggeraction[device].typeID != SpecialAction.ActionTypeId.Profile) ) + { + actionDone[index].dev[device] = true; + // If Loadprofile special action doesn't have untrigger keys or automatic untrigger option is not set then don't set untrigger status. This way the new loaded profile allows yet another loadProfile action key event. + if (action.uTrigger.Count > 0 || action.automaticUntrigger) + { + untriggeraction[device] = action; + untriggerindex[device] = index; + + // If the existing profile is a temp profile then store its name, because automaticUntrigger needs to know where to go back (empty name goes back to default regular profile) + untriggeraction[device].prevProfileName = (useTempProfile[device] ? tempprofilename[device] : string.Empty); + } + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease(ushort.Parse(dcs.action.ToString())); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", action.details); + AppLogger.LogToGui(prolog, false); + LoadTempProfile(device, action.details, true, ctrl); + + if (action.uTrigger.Count == 0 && !action.automaticUntrigger) + { + // If the new profile has any actions with the same action key (controls) than this action (which doesn't have untrigger keys) then set status of those actions to wait for the release of the existing action key. + List profileActionsNext = getProfileActions(device); + for (int actionIndexNext = 0, profileListLenNext = profileActionsNext.Count; actionIndexNext < profileListLenNext; actionIndexNext++) + { + string actionnameNext = profileActionsNext[actionIndexNext]; + SpecialAction actionNext = GetProfileAction(device, actionnameNext); + int indexNext = GetProfileActionIndexOf(device, actionnameNext); + + if (actionNext.controls == action.controls) + actionDone[indexNext].dev[device] = true; + } + } + + return; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Macro) + { + actionFound = true; + if (!action.pressRelease) + { + // Macro run when trigger keys are pressed down (the default behaviour) + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, actionDone[index]); + } + else + { + if (!action.keyType.HasFlag(DS4KeyType.RepeatMacro)) + EndMacro(device, macroControl, action.macro, DS4Controls.None); + } + } + else + { + // Macro is run when trigger keys are released (optional behaviour of macro special action)) + if (action.firstTouch) + { + action.firstTouch = false; + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, null); + } + } + else + action.firstTouch = true; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (uTriggerCount == 0 || (uTriggerCount > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = index; + ushort key; + ushort.TryParse(action.details, out key); + if (uTriggerCount == 0) + { + SyntheticState.KeyPresses kp; + if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) + deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + kp.current.repeatCount++; + } + else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyPress(key); + else + InputMethods.performKeyPress(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + bool synced = /*tempBool =*/ d.isSynced(); + if (synced && !d.isCharging()) + { + ConnectionType deviceConn = d.getConnectionType(); + //bool exclusive = /*tempBool =*/ d.isExclusive(); + if (deviceConn == ConnectionType.BT) + { + d.DisconnectBT(); + ReleaseActionKeys(action, device); + return; + } + else if (deviceConn == ConnectionType.SONYWA) + { + action.pressRelease = true; + } + } + } + else if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + + string[] dets = action.details.Split('|'); + if (dets.Length == 1) + dets = action.details.Split(','); + if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) + { + AppLogger.LogToTray("Controller " + (device + 1) + ": " + + ctrl.getDS4Battery(device), true); + } + if (bool.Parse(dets[2])) + { + DS4Device d = ctrl.DS4Controllers[device]; + if (!actionDone[index].dev[device]) + { + lastColor[device] = d.LightBarColor; + DS4LightBar.forcelight[device] = true; + } + DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); + DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); + DS4Color trans = getTransitionedColor(ref empty, ref full, d.Battery); + if (fadetimer[device] < 100) + DS4LightBar.forcedColor[device] = getTransitionedColor(ref lastColor[device], ref trans, fadetimer[device] += 2); + } + actionDone[index].dev[device] = true; + } + else if (action.typeID == SpecialAction.ActionTypeId.SASteeringWheelEmulationCalibrate) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + // If controller is not already in SASteeringWheelCalibration state then enable it now. If calibration is active then complete it (commit calibration values) + if (d.WheelRecalibrateActiveState == 0 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 1; // Start calibration process + } + else if (d.WheelRecalibrateActiveState == 2 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 3; // Complete calibration process + } + + actionDone[index].dev[device] = true; + } + } + else + { + if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + if (actionDone[index].dev[device]) + { + fadetimer[device] = 0; + /*if (prevFadetimer[device] == fadetimer[device]) + { + prevFadetimer[device] = 0; + fadetimer[device] = 0; + } + else + prevFadetimer[device] = fadetimer[device];*/ + DS4LightBar.forcelight[device] = false; + actionDone[index].dev[device] = false; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT && action.pressRelease) + { + actionFound = true; + DS4Device d = ctrl.DS4Controllers[device]; + ConnectionType deviceConn = d.getConnectionType(); + if (deviceConn == ConnectionType.SONYWA && d.isSynced()) + { + if (d.isDS4Idle()) + { + d.DisconnectDongle(); + ReleaseActionKeys(action, device); + actionDone[index].dev[device] = false; + action.pressRelease = false; + } + } + } + else if (action.typeID != SpecialAction.ActionTypeId.Key && + action.typeID != SpecialAction.ActionTypeId.XboxGameDVR && + action.typeID != SpecialAction.ActionTypeId.MultiAction) + { + // Ignore + actionFound = true; + actionDone[index].dev[device] = false; + } + } + + if (!actionFound) + { + if (uTriggerCount > 0 && utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = -1; + ushort key; + ushort.TryParse(action.details, out key); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyRelease(key); + else + InputMethods.performKeyRelease(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.XboxGameDVR || action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + actionFound = true; + + bool tappedOnce = action.tappedOnce, firstTouch = action.firstTouch, + secondtouchbegin = action.secondtouchbegin; + //DateTime pastTime = action.pastTime, firstTap = action.firstTap, + // TimeofEnd = action.TimeofEnd; + + /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) + getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; + if (getCustomMacro(device, action.trigger[0]) != "0") + getCustomMacros(device).Remove(action.trigger[0]); + if (getCustomKey(device, action.trigger[0]) != 0) + getCustomMacros(device).Remove(action.trigger[0]);*/ + string[] dets = action.details.Split(','); + DS4Device d = ctrl.DS4Controllers[device]; + //cus + + DS4State tempPrevState = d.getPreviousStateRef(); + // Only create one instance of previous DS4StateFieldMapping in case more than one multi-action + // button is assigned + if (previousFieldMapping == null) + { + previousFieldMapping = previousFieldMappings[device]; + previousFieldMapping.populateFieldMapping(tempPrevState, eState, tp, true); + //previousFieldMapping = new DS4StateFieldMapping(tempPrevState, eState, tp, true); + } + + bool activeCur = getBoolSpecialActionMapping(device, action.trigger[0], cState, eState, tp, fieldMapping); + bool activePrev = getBoolSpecialActionMapping(device, action.trigger[0], tempPrevState, eState, tp, previousFieldMapping); + if (activeCur && !activePrev) + { + // pressed down + action.pastTime = DateTime.UtcNow; + if (action.pastTime <= (action.firstTap + TimeSpan.FromMilliseconds(150))) + { + action.tappedOnce = tappedOnce = false; + action.secondtouchbegin = secondtouchbegin = true; + //tappedOnce = false; + //secondtouchbegin = true; + } + else + action.firstTouch = firstTouch = true; + //firstTouch = true; + } + else if (!activeCur && activePrev) + { + // released + if (secondtouchbegin) + { + action.firstTouch = firstTouch = false; + action.secondtouchbegin = secondtouchbegin = false; + //firstTouch = false; + //secondtouchbegin = false; + } + else if (firstTouch) + { + action.firstTouch = firstTouch = false; + //firstTouch = false; + if (DateTime.UtcNow <= (action.pastTime + TimeSpan.FromMilliseconds(150)) && !tappedOnce) + { + action.tappedOnce = tappedOnce = true; + //tappedOnce = true; + action.firstTap = DateTime.UtcNow; + action.TimeofEnd = DateTime.UtcNow; + } + } + } + + int type = 0; + string macro = ""; + if (tappedOnce) //single tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[0]; + } + else if (int.TryParse(dets[0], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if ((DateTime.UtcNow - action.TimeofEnd) > TimeSpan.FromMilliseconds(150)) + { + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + tappedOnce = false; + action.tappedOnce = false; + } + //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) + } + else if (firstTouch && (DateTime.UtcNow - action.pastTime) > TimeSpan.FromMilliseconds(500)) //helddown + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[1]; + } + else if (int.TryParse(dets[1], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + firstTouch = false; + action.firstTouch = false; + } + else if (secondtouchbegin) //if double tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[2]; + } + else if (int.TryParse(dets[2], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + secondtouchbegin = false; + action.secondtouchbegin = false; + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + } + } + catch { return; } + + if (untriggeraction[device] != null) + { + SpecialAction action = untriggeraction[device]; + int index = untriggerindex[device]; + bool utriggeractivated; + + if (!action.automaticUntrigger) + { + // Untrigger keys defined and auto-untrigger (=unload) profile option is NOT set. Unload a temporary profile only when specified untrigger keys have been triggered. + utriggeractivated = true; + + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, uTrigLen = action.uTrigger.Count; i < uTrigLen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + } + else + { + // Untrigger as soon any of the defined regular trigger keys have been released. + utriggeractivated = false; + + for (int i = 0, trigLen = action.trigger.Count; i < trigLen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = true; + break; + } + } + } + + if (utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Profile) + { + if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same + action.controls != action.ucontrols) + { + if (useTempProfile[device]) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + actionDone[index].dev[device] = true; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string profileName = untriggeraction[device].prevProfileName; + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", (profileName == string.Empty ? ProfilePath[device] : profileName)); + AppLogger.LogToGui(prolog, false); + + untriggeraction[device] = null; + + if (profileName == string.Empty) + LoadProfile(device, false, ctrl); // Previous profile was a regular default profile of a controller + else + LoadTempProfile(device, profileName, true, ctrl); // Previous profile was a temporary profile, so re-load it as a temp profile + } + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + + private static void ReleaseActionKeys(SpecialAction action, int device) + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + } + + // Play macro as a background task. Optionally the new macro play waits for completion of a previous macro execution (synchronized macro special action). + // Macro steps are defined either as macrostr string value, macroLst list object or as macroArr integer array. Only one of these should have a valid macro definition when this method is called. + // If the macro definition is a macroStr string value then it will be converted as integer array on the fl. If steps are already defined as list or array of integers then there is no need to do type cast conversion. + private static void PlayMacro(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action = null, ActionState actionDoneState = null) + { + if (action != null && action.synchronized) + { + // Run special action macros in synchronized order (ie. FirstIn-FirstOut). The trigger control name string is the execution queue identifier (ie. each unique trigger combination has an own synchronization queue). + if (!macroTaskQueue[device].TryGetValue(action.controls, out Task prevTask)) + macroTaskQueue[device].Add(action.controls, (Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState))) ); + else + macroTaskQueue[device][action.controls] = prevTask.ContinueWith((x) => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + else + // Run macro as "fire and forget" background task. No need to wait for completion of any of the other macros. + // If the same trigger macro is re-launched while previous macro is still running then the order of parallel macros is not guaranteed. + Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + + // Play through a macro. The macro steps are defined either as string, List or Array object (always only one of those parameters is set to a valid value) + private static void PlayMacroTask(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action, ActionState actionDoneState) + { + if(!String.IsNullOrEmpty(macroStr)) + { + string[] skeys; + + skeys = macroStr.Split('/'); + macroArr = new int[skeys.Length]; + for (int i = 0; i < macroArr.Length; i++) + macroArr[i] = int.Parse(skeys[i]); + } + + // macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18") + if ( (macroLst != null && macroLst.Count >= 4 && ((macroLst[0] == 164 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 164) || (macroLst[0] == 18 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 18))) + || (macroArr != null && macroArr.Length>= 4 && ((macroArr[0] == 164 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 164) || (macroArr[0] == 18 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 18))) + ) + { + int wait; + if(macroLst != null) + wait = macroLst[macroLst.Count - 1]; + else + wait = macroArr[macroArr.Length - 1]; + + if (wait <= 300 || wait > ushort.MaxValue) + wait = 1000; + else + wait -= 300; + + AltTabSwapping(wait, device); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + } + else if(control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + { + int macroCodeValue; + bool[] keydown = new bool[286]; + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + + // Play macro codes and simulate key down/up events (note! The same key may go through several up and down events during the same macro). + // If the return value is TRUE then this method should do a asynchronized delay (the usual Thread.Sleep doesnt work here because it would block the main gamepad reading thread). + if (macroLst != null) + { + for (int i = 0; i < macroLst.Count; i++) + { + macroCodeValue = macroLst[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + else + { + for (int i = 0; i < macroArr.Length; i++) + { + macroCodeValue = macroArr[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + + // The macro is finished. If any of the keys is still in down state then release a key state (ie. simulate key up event) unless special action specified to keep the last state as it is left in a macro + if (action == null || !action.keepKeyState) + { + for (int i = 0, arlength = keydown.Length; i < arlength; i++) + { + if (keydown[i]) + PlayMacroCodeValue(device, macrocontrol, keyType, i, keydown); + } + } + + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + + // Commented out rumble reset. No need to zero out rumble after a macro because it may conflict with a game generated rumble events (ie. macro would stop a game generated rumble effect). + // If macro generates rumble effects then the macro can stop the rumble as a last step or wait for rumble watchdog timer to do it after few seconds. + //Program.rootHub.DS4Controllers[device].setRumble(0, 0); + + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + Task.Delay(50).Wait(); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + } + + // If a special action type of Macro has "Repeat while held" option and actionDoneState object is defined then reset the action back to "not done" status in order to re-fire it if the trigger key is still held down + if (actionDoneState != null && keyType.HasFlag(DS4KeyType.RepeatMacro)) + actionDoneState.dev[device] = false; + } + + private static bool PlayMacroCodeValue(int device, bool[] macrocontrol, DS4KeyType keyType, int macroCodeValue, bool[] keydown) + { + bool doDelayOnCaller = false; + if (macroCodeValue >= 261 && macroCodeValue <= 285) + { + // Gamepad button up or down macro event. macroCodeValue index value is the button identifier (codeValue-261 = idx in 0..24 range) + if (!keydown[macroCodeValue]) + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = true; + macroCount++; + } + else + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = false; + if (macroCount > 0) macroCount--; + } + } + else if (macroCodeValue < 300) + { + // Keyboard key or mouse button macro event + if (!keydown[macroCodeValue]) + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyPress((ushort)macroCodeValue); + else InputMethods.performKeyPress((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = true; + } + else + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyRelease((ushort)macroCodeValue); + else InputMethods.performKeyRelease((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = false; + } + } + else if (macroCodeValue >= 1000000000) + { + // Lightbar color event + if (macroCodeValue > 1000000000) + { + string lb = macroCodeValue.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + DS4LightBar.forcelight[device] = true; + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); + } + else + { + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + } + } + else if (macroCodeValue >= 1000000) + { + // Rumble event + DS4Device d = Program.rootHub.DS4Controllers[device]; + string r = macroCodeValue.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + d.setRumble(light, heavy); + } + else + { + // Delay specification. Indicate to caller that it should do a delay of macroCodeValue-300 msecs + doDelayOnCaller = true; + } + + return doDelayOnCaller; + } + + private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) + { + if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, List macro, DS4Controls control) + { + if(macro.Count >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, int[] macro, DS4Controls control) + { + if (macro.Length >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void AltTabSwapping(int wait, int device) + { + if (altTabDone) + { + altTabDone = false; + InputMethods.performKeyPress(18); + } + else + { + altTabNow = DateTime.UtcNow; + if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) + { + oldAltTabNow = altTabNow; + InputMethods.performKeyPress(9); + InputMethods.performKeyRelease(9); + } + } + } + + private static void AltTabSwappingRelease() + { + if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + { + altTabDone = true; + InputMethods.performKeyRelease(9); + InputMethods.performKeyRelease(18); + altTabNow = DateTime.UtcNow; + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + } + } + + private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, bool down) + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + { + oldnow = now; + InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 8.0f * (down ? -1 : 1)), 0); + } + } + + private static double getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, + DS4StateFieldMapping fieldMapping, int mnum, ControlService ctrl) + { + int controlnum = DS4ControltoInt(control); + + int deadzoneL = 0; + int deadzoneR = 0; + if (getLSDeadzone(device) == 0) + deadzoneL = 3; + if (getRSDeadzone(device) == 0) + deadzoneR = 3; + + double value = 0.0; + int speed = ButtonMouseSensitivity[device]; + double root = 1.002; + double divide = 10000d; + //DateTime now = mousenow[mnum]; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + //long timeElapsed = ctrl.DS4Controllers[device].getLastTimeElapsed(); + double timeElapsed = ctrl.DS4Controllers[device].lastTimeElapsedDouble; + //double mouseOffset = 0.025; + double tempMouseOffsetX = 0.0, tempMouseOffsetY = 0.0; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + bool active = fieldMapping.buttons[controlNum]; + value = (active ? Math.Pow(root + speed / divide, 100) - 1 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + if (cState.LX < 128 - deadzoneL) + { + double diff = -(cState.LX - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LX - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LXPos: + { + if (cState.LX > 128 + deadzoneL) + { + double diff = (cState.LX - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LX - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RXNeg: + { + if (cState.RX < 128 - deadzoneR) + { + double diff = -(cState.RX - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RX - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RXPos: + { + if (cState.RX > 128 + deadzoneR) + { + double diff = (cState.RX - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RX - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.LYNeg: + { + if (cState.LY < 128 - deadzoneL) + { + double diff = -(cState.LY - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LY - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LYPos: + { + if (cState.LY > 128 + deadzoneL) + { + double diff = (cState.LY - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LY - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RYNeg: + { + if (cState.RY < 128 - deadzoneR) + { + double diff = -(cState.RY - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RY - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RYPos: + { + if (cState.RY > 128 + deadzoneR) + { + double diff = (cState.RY - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RY - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + byte trigger = fieldMapping.triggers[controlNum]; + value = Math.Pow(root + speed / divide, trigger / 2d) - 1; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + //double SXD = getSXDeadzone(device); + //double SZD = getSZDeadzone(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX > 0 ? Math.Pow(root + speed / divide, gyroX) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX < 0 ? Math.Pow(root + speed / divide, -gyroX) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ > 0 ? Math.Pow(root + speed / divide, gyroZ) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ < 0 ? Math.Pow(root + speed / divide, -gyroZ) : 0); + break; + } + default: break; + } + } + + if (getMouseAccel(device)) + { + if (value > 0) + { + mcounter = 34; + mouseaccel++; + } + + if (mouseaccel == prevmouseaccel) + { + mcounter--; + } + + if (mcounter <= 0) + { + mouseaccel = 0; + mcounter = 34; + } + + value *= 1 + Math.Min(20000, (mouseaccel)) / 10000d; + prevmouseaccel = mouseaccel; + } + + return value; + } + + private static void calculateFinalMouseMovement(ref double rawMouseX, ref double rawMouseY, + out int mouseX, out int mouseY) + { + if ((rawMouseX > 0.0 && horizontalRemainder > 0.0) || (rawMouseX < 0.0 && horizontalRemainder < 0.0)) + { + rawMouseX += horizontalRemainder; + } + else + { + horizontalRemainder = 0.0; + } + + //double mouseXTemp = rawMouseX - (Math.IEEERemainder(rawMouseX * 1000.0, 1.0) / 1000.0); + double mouseXTemp = rawMouseX - (remainderCutoff(rawMouseX * 1000.0, 1.0) / 1000.0); + //double mouseXTemp = rawMouseX - (rawMouseX * 1000.0 - (1.0 * (int)(rawMouseX * 1000.0 / 1.0))); + mouseX = (int)mouseXTemp; + horizontalRemainder = mouseXTemp - mouseX; + //mouseX = (int)rawMouseX; + //horizontalRemainder = rawMouseX - mouseX; + + if ((rawMouseY > 0.0 && verticalRemainder > 0.0) || (rawMouseY < 0.0 && verticalRemainder < 0.0)) + { + rawMouseY += verticalRemainder; + } + else + { + verticalRemainder = 0.0; + } + + //double mouseYTemp = rawMouseY - (Math.IEEERemainder(rawMouseY * 1000.0, 1.0) / 1000.0); + double mouseYTemp = rawMouseY - (remainderCutoff(rawMouseY * 1000.0, 1.0) / 1000.0); + mouseY = (int)mouseYTemp; + verticalRemainder = mouseYTemp - mouseY; + //mouseY = (int)rawMouseY; + //verticalRemainder = rawMouseY - mouseY; + } + + private static double remainderCutoff(double dividend, double divisor) + { + return dividend - (divisor * (int)(dividend / divisor)); + } + + public static bool compare(byte b1, byte b2) + { + bool result = true; + if (Math.Abs(b1 - b2) > 10) + { + result = false; + } + + return result; + } + + private static byte getByteMapping2(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, + DS4StateFieldMapping fieldMap) + { + byte result = 0; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = (byte)(fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + default: result = (byte)(axisValue - 128.0f < 0 ? 0 : (axisValue - 128.0f) * 2.0078740157480315f); break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = (byte)(tp != null && fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = (byte)(tp != null ? fieldMap.swipedirs[controlNum] : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroZ * 2) : 0); + break; + } + default: break; + } + } + + return result; + } + + public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + byte result = 0; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = (byte)(cState.Cross ? 255 : 0); break; + case DS4Controls.Square: result = (byte)(cState.Square ? 255 : 0); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? 255 : 0); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = (byte)(cState.L1 ? 255 : 0); break; + case DS4Controls.L2: result = cState.L2; break; + case DS4Controls.L3: result = (byte)(cState.L3 ? 255 : 0); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? 255 : 0); break; + case DS4Controls.R2: result = cState.R2; break; + case DS4Controls.R3: result = (byte)(cState.R3 ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? 255 : 0); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? 255 : 0); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? 255 : 0); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(cState.LX - 128.0f >= 0 ? 0 : -(cState.LX - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(cState.LY - 128.0f >= 0 ? 0 : -(cState.LY - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(cState.RX - 128.0f >= 0 ? 0 : -(cState.RX - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(cState.RY - 128.0f >= 0 ? 0 : -(cState.RY - 128.0f) * 1.9921875f); break; + case DS4Controls.LXPos: result = (byte)(cState.LX - 128.0f < 0 ? 0 : (cState.LX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.LYPos: result = (byte)(cState.LY - 128.0f < 0 ? 0 : (cState.LY - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RXPos: result = (byte)(cState.RX - 128.0f < 0 ? 0 : (cState.RX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RYPos: result = (byte)(cState.RY - 128.0f < 0 ? 0 : (cState.RY - 128.0f) * 2.0078740157480315f); break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? 255 : 0); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? 255 : 0); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? 255 : 0); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (byte)(tp != null ? tp.swipeUpB : 0); break; + case DS4Controls.SwipeDown: result = (byte)(tp != null ? tp.swipeDownB : 0); break; + case DS4Controls.SwipeLeft: result = (byte)(tp != null ? tp.swipeLeftB : 0); break; + case DS4Controls.SwipeRight: result = (byte)(tp != null ? tp.swipeRightB : 0); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX > SXD * 10 ? Math.Min(255, sxsens * gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX < -SXD * 10 ? Math.Min(255, sxsens * -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ > SZD * 10 ? Math.Min(255, szsens * gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ < -SZD * 10 ? Math.Min(255, szsens * -gyroZ * 2) : 0); + break; + } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? 255 : 0); break; + case DS4Controls.Options: result = (byte)(cState.Options ? 255 : 0); break; + case DS4Controls.PS: result = (byte)(cState.PS ? 255 : 0); break; + default: break; + } + } + + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = cState.Cross; break; + case DS4Controls.Square: result = cState.Square; break; + case DS4Controls.Triangle: result = cState.Triangle; break; + case DS4Controls.Circle: result = cState.Circle; break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = cState.L1; break; + case DS4Controls.R1: result = cState.R1; break; + case DS4Controls.L2: result = cState.L2 > 100; break; + case DS4Controls.R2: result = cState.R2 > 100; break; + case DS4Controls.L3: result = cState.L3; break; + case DS4Controls.R3: result = cState.R3; break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = cState.DpadUp; break; + case DS4Controls.DpadDown: result = cState.DpadDown; break; + case DS4Controls.DpadLeft: result = cState.DpadLeft; break; + case DS4Controls.DpadRight: result = cState.DpadRight; break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + case DS4Controls.LXPos: result = cState.LX > 128 + 55; break; + case DS4Controls.LYPos: result = cState.LY > 128 + 55; break; + case DS4Controls.RXPos: result = cState.RX > 128 + 55; break; + case DS4Controls.RYPos: result = cState.RY > 128 + 55; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (tp != null ? tp.leftDown : false); break; + case DS4Controls.TouchRight: result = (tp != null ? tp.rightDown : false); break; + case DS4Controls.TouchMulti: result = (tp != null ? tp.multiDown : false); break; + case DS4Controls.TouchUpper: result = (tp != null ? tp.upperDown : false); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (tp != null && tp.swipeUp); break; + case DS4Controls.SwipeDown: result = (tp != null && tp.swipeDown); break; + case DS4Controls.SwipeLeft: result = (tp != null && tp.swipeLeft); break; + case DS4Controls.SwipeRight: result = (tp != null && tp.swipeRight); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: result = !sOff ? SXSens[device] * -eState.AccelX > 67 : false; break; + case DS4Controls.GyroXNeg: result = !sOff ? SXSens[device] * -eState.AccelX < -67 : false; break; + case DS4Controls.GyroZPos: result = !sOff ? SZSens[device] * eState.AccelZ > 67 : false; break; + case DS4Controls.GyroZNeg: result = !sOff ? SZSens[device] * eState.AccelZ < -67 : false; break; + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.PS: result = cState.PS; break; + case DS4Controls.Share: result = cState.Share; break; + case DS4Controls.Options: result = cState.Options; break; + default: break; + } + } + + return result; + } + + private static bool getBoolMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolSpecialActionMapping(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolActionMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool analog = false) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + double angle = cState.LSAngle; + result = cState.LX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.LYNeg: + { + double angle = cState.LSAngle; + result = cState.LY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.RXNeg: + { + double angle = cState.RSAngle; + result = cState.RX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.RYNeg: + { + double angle = cState.RSAngle; + result = cState.RY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.LXPos: + { + double angle = cState.LSAngle; + result = cState.LX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.LYPos: + { + double angle = cState.LSAngle; + result = cState.LY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + case DS4Controls.RXPos: + { + double angle = cState.RSAngle; + result = cState.RX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.RYPos: + { + double angle = cState.RSAngle; + result = cState.RY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + public static bool getBoolButtonMapping(bool stateButton) + { + return stateButton; + } + + public static bool getBoolAxisDirMapping(byte stateAxis, bool positive) + { + return positive ? stateAxis > 128 + 55 : stateAxis < 128 - 55; + } + + public static bool getBoolTriggerMapping(byte stateAxis) + { + return stateAxis > 100; + } + + public static bool getBoolTouchMapping(bool touchButton) + { + return touchButton; + } + + private static byte getXYAxisMapping2(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool alt = false) + { + const byte falseVal = 128; + byte result = 0; + byte trueVal = 0; + + if (alt) + trueVal = 255; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.LYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + default: if (!alt) result = axisValue > falseVal ? (byte)(255 - axisValue) : falseVal; else result = axisValue > falseVal ? axisValue : falseVal; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + if (alt) + { + result = (byte)(128.0f + fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + else + { + result = (byte)(128.0f - fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + if (alt) + { + result = (byte)(tp != null ? 128.0f + fieldMap.swipedirs[controlNum] / 2f : 0); } - } - - if (macroControl[00]) MappedState.Cross = true; - if (macroControl[01]) MappedState.Circle = true; - if (macroControl[02]) MappedState.Square = true; - if (macroControl[03]) MappedState.Triangle = true; - if (macroControl[04]) MappedState.Options = true; - if (macroControl[05]) MappedState.Share = true; - if (macroControl[06]) MappedState.DpadUp = true; - if (macroControl[07]) MappedState.DpadDown = true; - if (macroControl[08]) MappedState.DpadLeft = true; - if (macroControl[09]) MappedState.DpadRight = true; - if (macroControl[10]) MappedState.PS = true; - if (macroControl[11]) MappedState.L1 = true; - if (macroControl[12]) MappedState.R1 = true; - if (macroControl[13]) MappedState.L2 = 255; - if (macroControl[14]) MappedState.R2 = 255; - if (macroControl[15]) MappedState.L3 = true; - if (macroControl[16]) MappedState.R3 = true; - if (macroControl[17]) MappedState.LX = 255; - if (macroControl[18]) MappedState.LX = 0; - if (macroControl[19]) MappedState.LY = 255; - if (macroControl[20]) MappedState.LY = 0; - if (macroControl[21]) MappedState.RX = 255; - if (macroControl[22]) MappedState.RX = 0; - if (macroControl[23]) MappedState.RY = 255; - if (macroControl[24]) MappedState.RY = 0; - foreach (DS4Controls dc in Cross) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Cross = true; - foreach (DS4Controls dc in Circle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Circle = true; - foreach (DS4Controls dc in Square) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Square = true; - foreach (DS4Controls dc in Triangle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Triangle = true; - foreach (DS4Controls dc in L1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L1 = true; - foreach (DS4Controls dc in L2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.L2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in L3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L3 = true; - foreach (DS4Controls dc in R1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R1 = true; - foreach (DS4Controls dc in R2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.R2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in R3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R3 = true; - foreach (DS4Controls dc in DpadUp) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadUp = true; - foreach (DS4Controls dc in DpadRight) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadRight = true; - foreach (DS4Controls dc in DpadLeft) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadLeft = true; - foreach (DS4Controls dc in DpadDown) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadDown = true; - foreach (DS4Controls dc in Options) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Options = true; - foreach (DS4Controls dc in Share) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Share = true; - foreach (DS4Controls dc in PS) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.PS = true; - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXNeg.ToString()), device, cState, eState, tp), DS4Controls.LXNeg)) - LXN.Add(DS4Controls.LXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXPos.ToString()), device, cState, eState, tp), DS4Controls.LXPos)) - LXP.Add(DS4Controls.LXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYNeg.ToString()), device, cState, eState, tp), DS4Controls.LYNeg)) - LYN.Add(DS4Controls.LYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYPos.ToString()), device, cState, eState, tp), DS4Controls.LYPos)) - LYP.Add(DS4Controls.LYPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXNeg.ToString()), device, cState, eState, tp), DS4Controls.RXNeg)) - RXN.Add(DS4Controls.RXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXPos.ToString()), device, cState, eState, tp), DS4Controls.RXPos)) - RXP.Add(DS4Controls.RXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYNeg.ToString()), device, cState, eState, tp), DS4Controls.RYNeg)) - RYN.Add(DS4Controls.RYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYPos.ToString()), device, cState, eState, tp), DS4Controls.RYPos)) - RYP.Add(DS4Controls.RYPos); - - if (Math.Abs(MappedState.LX - 127) < 10) - if (LXN.Count > 0 || LXP.Count > 0) - { - foreach (DS4Controls dc in LXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp, true); - foreach (DS4Controls dc in LXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp); - } - //else - //MappedState.LX = cState.LX; - if (Math.Abs(MappedState.LY - 127) < 10) - if (LYN.Count > 0 || LYP.Count > 0) - { - foreach (DS4Controls dc in LYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in LYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - //MappedState.LY = cState.LY; - if (Math.Abs(MappedState.RX - 127) < 10) - if (RXN.Count > 0 || RXP.Count > 0) - { - foreach (DS4Controls dc in RXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - // MappedState.RX = cState.RX; - if (Math.Abs(MappedState.RY - 127) < 10) - if (RYN.Count > 0 || RYP.Count > 0) - { - foreach (DS4Controls dc in RYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - // else - // MappedState.RY = cState.RY; - InputMethods.MoveCursorBy(MouseDeltaX, MouseDeltaY); - } + else + { + result = (byte)(tp != null ? 128.0f - fieldMap.swipedirs[controlNum] / 2f : 0); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); - private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) - { - return shift ? false : GetDS4Action(device, dc.ToString(), false) == null; - } - public static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - try { - foreach (string actionname in ProfileActions[device]) + switch (control) { - //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); - SpecialAction action = GetAction(actionname); - int index = GetActionIndexOf(actionname); - if (actionDone.Count < index + 1) - actionDone.Add(new ActionState()); - else if (actionDone.Count > GetActions().Count()) - actionDone.RemoveAt(actionDone.Count - 1); - double time; - //If a key or button is assigned to the trigger, a key special action is used like - //a quick tap to use and hold to use the regular custom button/key - bool triggerToBeTapped = action.type == "Key" && action.trigger.Count == 1 && - GetDS4Action(device, action.trigger[0].ToString(), false) == null; - if (!(action.name == "null" || index < 0)) + case DS4Controls.GyroXPos: { - bool triggeractivated = true; - if (action.delayTime > 0) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - time = action.delayTime; - nowAction[device] = DateTime.UtcNow; - if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) - triggeractivated = true; - } - else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) - oldnowAction[device] = DateTime.UtcNow; - } - else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - oldnowKeyAct[device] = DateTime.UtcNow; - } - } - else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - DateTime now = DateTime.UtcNow; - if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) - { - await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up - triggeractivated = true; - oldnowKeyAct[device] = DateTime.MinValue; - } - else if (!subtriggeractivated) - oldnowKeyAct[device] = DateTime.MinValue; - } - else - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - triggeractivated = false; - break; - } - } - - bool utriggeractivated = true; - if (action.type == "Key" && action.uTrigger.Count > 0) - { - foreach (DS4Controls dc in action.uTrigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } - } - if (action.pressRelease) utriggeractivated = !utriggeractivated; - } - - if (triggeractivated && action.type == "Program") - { - if (!actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - if (!string.IsNullOrEmpty(action.extra)) - Process.Start(action.details, action.extra); - else - Process.Start(action.details); - } - } - else if (triggeractivated && action.type == "Profile") - { - if (!actionDone[index].dev[device] && string.IsNullOrEmpty(tempprofilename[device])) - { - actionDone[index].dev[device] = true; - untriggeraction[device] = action; - untriggerindex[device] = index; - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease(ushort.Parse(action.ToString())); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - LoadTempProfile(device, action.details, true, ctrl); - return; - } - } - else if (triggeractivated && action.type == "Macro") - { - if (!actionDone[index].dev[device]) - { - DS4KeyType keyType = action.keyType; - actionDone[index].dev[device] = true; - foreach (DS4Controls dc in action.trigger) - resetToDefaultValue(dc, MappedState); - PlayMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None, keyType); - } - else - EndMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None); - } - else if (triggeractivated && action.type == "Key") + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (action.uTrigger.Count == 0 || (action.uTrigger.Count > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = index; - ushort key; - ushort.TryParse(action.details, out key); - if (action.uTrigger.Count == 0) - { - SyntheticState.KeyPresses kp; - if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) - deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - kp.current.repeatCount++; - } - else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress(key); - else - InputMethods.performKeyPress(key); - } - } - else if (action.uTrigger.Count > 0 && utriggeractivated && action.type == "Key") - { - if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = -1; - ushort key; - ushort.TryParse(action.details, out key); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(key); - else - InputMethods.performKeyRelease(key); - } - } - else if (triggeractivated && action.type == "DisconnectBT") - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!d.Charging) - { - d.DisconnectBT(); - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - return; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - string[] dets = action.details.Split('|'); - if (dets.Length == 1) - dets = action.details.Split(','); - if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) - { - Log.LogToTray("Controller " + (device + 1) + ": " + - ctrl.getDS4Battery(device), true); - } - if (bool.Parse(dets[2])) - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!actionDone[index].dev[device]) - { - lastColor[device] = d.LightBarColor; - DS4LightBar.forcelight[device] = true; - } - DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); - DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); - DS4Color trans = getTransitionedColor(empty, full, d.Battery); - if (fadetimer[device] < 100) - DS4LightBar.forcedColor[device] = getTransitionedColor(lastColor[device], trans, fadetimer[device] += 2); - } - actionDone[index].dev[device] = true; + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else if (!triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroZPos: + { + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (actionDone[index].dev[device]) - { - fadetimer[device] = 0; - /*if (prevFadetimer[device] == fadetimer[device]) - { - prevFadetimer[device] = 0; - fadetimer[device] = 0; - } - else - prevFadetimer[device] = fadetimer[device];*/ - DS4LightBar.forcelight[device] = false; - actionDone[index].dev[device] = false; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (action.type == "XboxGameDVR" || action.type == "MultiAction") + else return falseVal; + break; + } + case DS4Controls.GyroZNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) - getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; - if (getCustomMacro(device, action.trigger[0]) != "0") - getCustomMacros(device).Remove(action.trigger[0]); - if (getCustomKey(device, action.trigger[0]) != 0) - getCustomMacros(device).Remove(action.trigger[0]);*/ - string[] dets = action.details.Split(','); - DS4Device d = ctrl.DS4Controllers[device]; - //cus - if (getBoolMapping(device, action.trigger[0], cState, eState, tp) && !getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//pressed down - pastTime = DateTime.UtcNow; - if (DateTime.UtcNow <= (firstTap + TimeSpan.FromMilliseconds(150))) - { - tappedOnce = false; - secondtouchbegin = true; - } - else - firstTouch = true; - } - else if (!getBoolMapping(device, action.trigger[0], cState, eState, tp) && getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//released - if (secondtouchbegin) - { - firstTouch = false; - secondtouchbegin = false; - } - else if (firstTouch) - { - firstTouch = false; - if (DateTime.UtcNow <= (pastTime + TimeSpan.FromMilliseconds(200)) && !tappedOnce) - { - tappedOnce = true; - firstTap = DateTime.UtcNow; - TimeofEnd = DateTime.UtcNow; - } - } - } - - int type = 0; - string macro = ""; - if (tappedOnce) //single tap - { - if (action.type == "MultiAction") - { - macro = dets[0]; - } - else if (int.TryParse(dets[0], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if ((DateTime.UtcNow - TimeofEnd) > TimeSpan.FromMilliseconds(150)) - { - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - tappedOnce = false; - } - //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) - } - else if (firstTouch && (DateTime.UtcNow - pastTime) > TimeSpan.FromMilliseconds(1000)) //helddown - { - if (action.type == "MultiAction") - { - macro = dets[1]; - } - else if (int.TryParse(dets[1], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - firstTouch = false; - } - else if (secondtouchbegin) //if double tap - { - if (action.type == "MultiAction") - { - macro = dets[2]; - } - else if (int.TryParse(dets[2], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - secondtouchbegin = false; - } + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else - actionDone[index].dev[device] = false; + else result = falseVal; + break; } + default: break; } } - catch { return; } - if (untriggeraction[device] != null) + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) + { + byte result = 0; + byte trueVal = 0; + byte falseVal = 127; + + if (alt) + trueVal = 255; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) { - SpecialAction action = untriggeraction[device]; - int index = untriggerindex[device]; - bool utriggeractivated = true; - foreach (DS4Controls dc in action.uTrigger) + switch (control) { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } + case DS4Controls.Cross: result = (byte)(cState.Cross ? trueVal : falseVal); break; + case DS4Controls.Square: result = (byte)(cState.Square ? trueVal : falseVal); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? trueVal : falseVal); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? trueVal : falseVal); break; + default: break; } - - if (utriggeractivated && action.type == "Profile") + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) { - if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same - action.controls != action.ucontrols) - if (!string.IsNullOrEmpty(tempprofilename[device])) - { - foreach (DS4Controls dc in action.uTrigger) - { - actionDone[index].dev[device] = true; - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - untriggeraction[device] = null; - LoadProfile(device, false, ctrl); - } + case DS4Controls.L1: result = (byte)(cState.L1 ? trueVal : falseVal); break; + case DS4Controls.L2: if (alt) result = (byte)(128.0f + cState.L2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.L2 / 2.0078740157480315f); break; + case DS4Controls.L3: result = (byte)(cState.L3 ? trueVal : falseVal); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? trueVal : falseVal); break; + case DS4Controls.R2: if (alt) result = (byte)(128.0f + cState.R2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.R2 / 2.0078740157480315f); break; + case DS4Controls.R3: result = (byte)(cState.R3 ? trueVal : falseVal); break; + default: break; } - else - actionDone[index].dev[device] = false; } - } - - private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) - { - if (macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) { - string[] skeys; - int wait = 1000; - if (!string.IsNullOrEmpty(macro)) + switch (control) { - skeys = macro.Split('/'); - ushort delay; - if (ushort.TryParse(skeys[skeys.Length - 1], out delay) && delay > 300) - wait = delay - 300; + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? trueVal : falseVal); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? trueVal : falseVal); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? trueVal : falseVal); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? trueVal : falseVal); break; + default: break; } - AltTabSwapping(wait, device); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; } - else + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) { - string[] skeys; - int[] keys; - if (!string.IsNullOrEmpty(macro)) + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = cState.LX; else result = (byte)(255 - cState.LX); break; + case DS4Controls.LYNeg: if (!alt) result = cState.LY; else result = (byte)(255 - cState.LY); break; + case DS4Controls.RXNeg: if (!alt) result = cState.RX; else result = (byte)(255 - cState.RX); break; + case DS4Controls.RYNeg: if (!alt) result = cState.RY; else result = (byte)(255 - cState.RY); break; + case DS4Controls.LXPos: if (!alt) result = (byte)(255 - cState.LX); else result = cState.LX; break; + case DS4Controls.LYPos: if (!alt) result = (byte)(255 - cState.LY); else result = cState.LY; break; + case DS4Controls.RXPos: if (!alt) result = (byte)(255 - cState.RX); else result = cState.RX; break; + case DS4Controls.RYPos: if (!alt) result = (byte)(255 - cState.RY); else result = cState.RY; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) { - skeys = macro.Split('/'); - keys = new int[skeys.Length]; + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? trueVal : falseVal); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? trueVal : falseVal); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? trueVal : falseVal); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? trueVal : falseVal); break; + default: break; } - else + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) { - skeys = new string[0]; - keys = new int[0]; + case DS4Controls.SwipeUp: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeUpB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeUpB / 2f : 0); break; + case DS4Controls.SwipeDown: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeDownB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeDownB / 2f : 0); break; + case DS4Controls.SwipeLeft: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeLeftB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeLeftB / 2f : 0); break; + case DS4Controls.SwipeRight: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeRightB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeRightB / 2f : 0); break; + default: break; } - for (int i = 0; i < keys.Length; i++) - keys[i] = int.Parse(skeys[i]); - bool[] keydown = new bool[286]; - if (control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + + switch (control) { - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; - foreach (int i in keys) + case DS4Controls.GyroXPos: { - if (i >= 1000000000) - { - string lb = i.ToString().Substring(1); - if (i > 1000000000) - { - byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); - byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); - byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); - DS4LightBar.forcelight[device] = true; - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); - } - else - { - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - } - } - else if (i >= 1000000) - { - DS4Device d = Program.rootHub.DS4Controllers[device]; - string r = i.ToString().Substring(1); - byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); - byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); - d.setRumble(light, heavy); - } - else if (i >= 300) //ints over 300 used to delay - await Task.Delay(i - 300); - else if (!keydown[i]) + if (!sOff && -eState.AccelX > SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (i == 261) macroControl[0] = true; - else if (i == 262) macroControl[1] = true; - else if (i == 263) macroControl[2] = true; - else if (i == 264) macroControl[3] = true; - else if (i == 265) macroControl[4] = true; - else if (i == 266) macroControl[5] = true; - else if (i == 267) macroControl[6] = true; - else if (i == 268) macroControl[7] = true; - else if (i == 269) macroControl[8] = true; - else if (i == 270) macroControl[9] = true; - else if (i == 271) macroControl[10] = true; - else if (i == 272) macroControl[11] = true; - else if (i == 273) macroControl[12] = true; - else if (i == 274) macroControl[13] = true; - else if (i == 275) macroControl[14] = true; - else if (i == 276) macroControl[15] = true; - else if (i == 277) macroControl[16] = true; - else if (i == 278) macroControl[17] = true; - else if (i == 279) macroControl[18] = true; - else if (i == 280) macroControl[19] = true; - else if (i == 281) macroControl[20] = true; - else if (i == 282) macroControl[21] = true; - else if (i == 283) macroControl[22] = true; - else if (i == 284) macroControl[23] = true; - else if (i == 285) macroControl[24] = true; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress((ushort)i); - else - InputMethods.performKeyPress((ushort)i); - keydown[i] = true; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * -eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * -eState.AccelX); } - else + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (!sOff && -eState.AccelX < -SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease((ushort)i); - else - InputMethods.performKeyRelease((ushort)i); - keydown[i] = false; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * eState.AccelX); } + else result = falseVal; + break; } - for (ushort i = 0; i < keydown.Length; i++) + case DS4Controls.GyroZPos: { - if (keydown[i]) - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(i); - else - InputMethods.performKeyRelease(i); + if (!sOff && eState.AccelZ > SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * eState.AccelZ); + } + else return falseVal; + break; } - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - Program.rootHub.DS4Controllers[device].setRumble(0, 0); - if (keyType.HasFlag(DS4KeyType.HoldMacro)) + case DS4Controls.GyroZNeg: { - await Task.Delay(50); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + if (!sOff && eState.AccelZ < -SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * -eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * -eState.AccelZ); + } + else result = falseVal; + break; } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? trueVal : falseVal); break; + case DS4Controls.Options: result = (byte)(cState.Options ? trueVal : falseVal); break; + case DS4Controls.PS: result = (byte)(cState.PS ? trueVal : falseVal); break; + default: break; } } - } - private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) - { - if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) - AltTabSwappingRelease(); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + return result; } - private static void AltTabSwapping(int wait, int device) + + private static void resetToDefaultValue2(DS4Controls control, DS4State cState, + DS4StateFieldMapping fieldMap) { - if (altTabDone) + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) { - altTabDone = false; - InputMethods.performKeyPress(18); + fieldMap.buttons[controlNum] = false; } - else + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) { - altTabNow = DateTime.UtcNow; - if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) - { - oldAltTabNow = altTabNow; - InputMethods.performKeyPress(9); - InputMethods.performKeyRelease(9); - } + fieldMap.axisdirs[controlNum] = 128; + int controlRelation = (controlNum % 2 == 0 ? controlNum - 1 : controlNum + 1); + fieldMap.axisdirs[controlRelation] = 128; + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + fieldMap.triggers[controlNum] = 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + fieldMap.buttons[controlNum] = false; } } - private static void AltTabSwappingRelease() + + // SA steering wheel emulation mapping + + private const int C_WHEEL_ANGLE_PRECISION = 10; // Precision of SA angle in 1/10 of degrees + + private static readonly DS4Color calibrationColor_0 = new DS4Color { red = 0xA0, green = 0x00, blue = 0x00 }; + private static readonly DS4Color calibrationColor_1 = new DS4Color { red = 0xFF, green = 0xFF, blue = 0x00 }; + private static readonly DS4Color calibrationColor_2 = new DS4Color { red = 0x00, green = 0x50, blue = 0x50 }; + private static readonly DS4Color calibrationColor_3 = new DS4Color { red = 0x00, green = 0xC0, blue = 0x00 }; + + private static DateTime latestDebugMsgTime; + private static string latestDebugData; + private static void LogToGuiSACalibrationDebugMsg(string data, bool forceOutput = false) { - if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + // Print debug calibration log messages only once per 2 secs to avoid flooding the log receiver + DateTime curTime = DateTime.Now; + if (forceOutput || ((TimeSpan)(curTime - latestDebugMsgTime)).TotalSeconds > 2) { - altTabDone = true; - InputMethods.performKeyRelease(9); - InputMethods.performKeyRelease(18); - altTabNow = DateTime.UtcNow; - oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + latestDebugMsgTime = curTime; + if (data != latestDebugData) + { + AppLogger.LogToGui(data, false); + latestDebugData = data; + } } } - private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool down) + // Return number of bits set in a value + protected static int CountNumOfSetBits(int bitValue) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + int count = 0; + while (bitValue != 0) { - oldnow = now; - InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 51f * (down ? -1 : 1)), 0); + count++; + bitValue &= (bitValue - 1); } + return count; } - private static int getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, int mnum) + // Calculate and return the angle of the controller as -180...0...+180 value. + private static Int32 CalculateControllerAngle(int gyroAccelX, int gyroAccelZ, DS4Device controller) { - int controlnum = DS4ControltoInt(control); - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - int deadzoneL = 3; - int deadzoneR = 3; - if (LSDeadzone[device] >= 3) - deadzoneL = 0; - if (RSDeadzone[device] >= 3) - deadzoneR = 0; - double value = 0; - int speed = ButtonMouseSensitivity[device] + 15; - double root = 1.002; - double divide = 10000d; - //DateTime now = mousenow[mnum]; - switch (control) + Int32 result; + + if (gyroAccelX == controller.wheelCenterPoint.X && Math.Abs(gyroAccelZ - controller.wheelCenterPoint.Y) <= 1) { - case DS4Controls.LXNeg: - if (cState.LX - 127.5f < -deadzoneL) - value = -(cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LXPos: - if (cState.LX - 127.5f > deadzoneL) - value = (cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXNeg: - if (cState.RX - 127.5f < -deadzoneR) - value = -(cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXPos: - if (cState.RX - 127.5f > deadzoneR) - value = (cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYNeg: - if (cState.LY - 127.5f < -deadzoneL) - value = -(cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYPos: - if (cState.LY - 127.5f > deadzoneL) - value = (cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYNeg: - if (cState.RY - 127.5f < -deadzoneR) - value = -(cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYPos: - if (cState.RY - 127.5f > deadzoneR) - value = (cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.Share: value = (cState.Share ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Options: value = (cState.Options ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L1: value = (cState.L1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R1: value = (cState.R1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L3: value = (cState.L3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R3: value = (cState.R3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadUp: value = (cState.DpadUp ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadDown: value = (cState.DpadDown ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadLeft: value = (cState.DpadLeft ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadRight: value = (cState.DpadRight ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.PS: value = (cState.PS ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Cross: value = (cState.Cross ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Square: value = (cState.Square ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Triangle: value = (cState.Triangle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Circle: value = (cState.Circle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L2: value = Math.Pow(root + speed / divide, cState.L2 / 2d) - 1; break; - case DS4Controls.R2: value = Math.Pow(root + speed / divide, cState.R2 / 2d) - 1; break; - case DS4Controls.GyroXPos: return (byte)(eState.GyroX > SXD * 10 ? - Math.Pow(root + speed / divide, eState.GyroX) : 0); - case DS4Controls.GyroXNeg: return (byte)(eState.GyroX < -SXD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroX) : 0); - case DS4Controls.GyroZPos: return (byte)(eState.GyroZ > SZD * 10 ? - Math.Pow(root + speed / divide, eState.GyroZ) : 0); - case DS4Controls.GyroZNeg: return (byte)(eState.GyroZ < -SZD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroZ) : 0); - } - bool LXChanged = (Math.Abs(127 - cState.LX) < deadzoneL); - bool LYChanged = (Math.Abs(127 - cState.LY) < deadzoneL); - bool RXChanged = (Math.Abs(127 - cState.RX) < deadzoneR); - bool RYChanged = (Math.Abs(127 - cState.RY) < deadzoneR); - bool contains = (control.ToString().Contains("LX") || - control.ToString().Contains("LY") || - control.ToString().Contains("RX") || - control.ToString().Contains("RY")); - if (MouseAccel[device]) + // When the current gyro position is "close enough" the wheel center point then no need to go through the hassle of calculating an angle + result = 0; + } + else { - if (value > 0) + // Calculate two vectors based on "circle center" (ie. circle represents the 360 degree wheel turn and wheelCenterPoint and currentPosition vectors both start from circle center). + // To improve accuracy both left and right turns use a decicated calibration "circle" because DS4 gyro and DoItYourselfWheelRig may return slightly different SA sensor values depending on the tilt direction (well, only one or two degree difference so nothing major). + Point vectorAB; + Point vectorCD; + + if (gyroAccelX >= controller.wheelCenterPoint.X) { - mcounter = 34; - mouseaccel++; + // "DS4 gyro wheel" tilted to right + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointRight.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointRight.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointRight.X, gyroAccelZ - controller.wheelCircleCenterPointRight.Y); } - if (mouseaccel == prevmouseaccel) + else { - mcounter--; + // "DS4 gyro wheel" tilted to left + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointLeft.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointLeft.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointLeft.X, gyroAccelZ - controller.wheelCircleCenterPointLeft.Y); } - if (mcounter <= 0) + + // Calculate dot product and magnitude of vectors (center vector and the current tilt vector) + double dotProduct = vectorAB.X * vectorCD.X + vectorAB.Y * vectorCD.Y; + double magAB = Math.Sqrt(vectorAB.X * vectorAB.X + vectorAB.Y * vectorAB.Y); + double magCD = Math.Sqrt(vectorCD.X * vectorCD.X + vectorCD.Y * vectorCD.Y); + + // Calculate angle between vectors and convert radian to degrees + if (magAB == 0 || magCD == 0) { - mouseaccel = 0; - mcounter = 34; + result = 0; } - value *= 1 + (double)Math.Min(20000, (mouseaccel)) / 10000d; - prevmouseaccel = mouseaccel; - } - int intValue; - if (mnum > 1) - { - if ((value > 0.0 && horizontalRemainder > 0.0) || (value < 0.0 && horizontalRemainder < 0.0)) - value += horizontalRemainder; - intValue = (int)value; - horizontalRemainder = value - intValue; - } - else - { - if ((value > 0.0 && verticalRemainder > 0.0) || (value < 0.0 && verticalRemainder < 0.0)) - value += verticalRemainder; - intValue = (int)value; - verticalRemainder = value - intValue; + else + { + double angle = Math.Acos(dotProduct / (magAB * magCD)); + result = Convert.ToInt32(Global.Clamp( + -180.0 * C_WHEEL_ANGLE_PRECISION, + Math.Round((angle * (180.0 / Math.PI)), 1) * C_WHEEL_ANGLE_PRECISION, + 180.0 * C_WHEEL_ANGLE_PRECISION) + ); + } + + // Left turn is -180..0 and right turn 0..180 degrees + if (gyroAccelX < controller.wheelCenterPoint.X) result = -result; } - return intValue; + + return result; } - public static bool compare(byte b1, byte b2) + // Calibrate sixaxis steering wheel emulation. Use DS4Windows configuration screen to start a calibration or press a special action key (if defined) + private static void SAWheelEmulationCalibration(int device, DS4StateExposed exposedState, ControlService ctrl, DS4State currentDeviceState, DS4Device controller) { - if (Math.Abs(b1 - b2) > 10) + int gyroAccelX, gyroAccelZ; + int result; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // State 0=Normal mode (ie. calibration process is not running), 1=Activating calibration, 2=Calibration process running, 3=Completing calibration, 4=Cancelling calibration + if (controller.WheelRecalibrateActiveState == 1) { - return false; + AppLogger.LogToGui($"Controller {1 + device} activated re-calibration of SA steering wheel emulation", false); + + controller.WheelRecalibrateActiveState = 2; + + controller.wheelPrevPhysicalAngle = 0; + controller.wheelPrevFullAngle = 0; + controller.wheelFullTurnCount = 0; + + // Clear existing calibration value and use current position as "center" point. + // This initial center value may be off-center because of shaking the controller while button was pressed. The value will be overriden with correct value once controller is stabilized and hold still few secs at the center point + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + controller.wheel90DegPointRight.X = gyroAccelX + 20; + controller.wheel90DegPointLeft.X = gyroAccelX - 20; + + // Clear bitmask for calibration points. All three calibration points need to be set before re-calibration process is valid + controller.wheelCalibratedAxisBitmask = DS4Device.WheelCalibrationPoint.None; + + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); } - return true; - } + else if (controller.WheelRecalibrateActiveState == 3) + { + AppLogger.LogToGui($"Controller {1 + device} completed the calibration of SA steering wheel emulation. center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y})", false); - public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? 255 : 0); - case DS4Controls.Options: return (byte)(cState.Options ? 255 : 0); - case DS4Controls.L1: return (byte)(cState.L1 ? 255 : 0); - case DS4Controls.R1: return (byte)(cState.R1 ? 255 : 0); - case DS4Controls.L3: return (byte)(cState.L3 ? 255 : 0); - case DS4Controls.R3: return (byte)(cState.R3 ? 255 : 0); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? 255 : 0); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? 255 : 0); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? 255 : 0); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? 255 : 0); - case DS4Controls.PS: return (byte)(cState.PS ? 255 : 0); - case DS4Controls.Cross: return (byte)(cState.Cross ? 255 : 0); - case DS4Controls.Square: return (byte)(cState.Square ? 255 : 0); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? 255 : 0); - case DS4Controls.Circle: return (byte)(cState.Circle ? 255 : 0); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? 255 : 0); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? 255 : 0); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? 255 : 0); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? 255 : 0); - case DS4Controls.LXNeg: return (byte)(cState.LX - 127.5f > 0 ? 0 : -(cState.LX - 127.5f) * 2); - case DS4Controls.LYNeg: return (byte)(cState.LY - 127.5f > 0 ? 0 : -(cState.LY - 127.5f) * 2); - case DS4Controls.RXNeg: return (byte)(cState.RX - 127.5f > 0 ? 0 : -(cState.RX - 127.5f) * 2); - case DS4Controls.RYNeg: return (byte)(cState.RY - 127.5f > 0 ? 0 : -(cState.RY - 127.5f) * 2); - case DS4Controls.LXPos: return (byte)(cState.LX - 127.5f < 0 ? 0 : (cState.LX - 127.5f) * 2); - case DS4Controls.LYPos: return (byte)(cState.LY - 127.5f < 0 ? 0 : (cState.LY - 127.5f) * 2); - case DS4Controls.RXPos: return (byte)(cState.RX - 127.5f < 0 ? 0 : (cState.RX - 127.5f) * 2); - case DS4Controls.RYPos: return (byte)(cState.RY - 127.5f < 0 ? 0 : (cState.RY - 127.5f) * 2); - case DS4Controls.L2: return cState.L2; - case DS4Controls.R2: return cState.R2; - case DS4Controls.GyroXPos: return (byte)(!sOff && SXSens[device] * eState.GyroX > SXD * 10 ? Math.Min(255, SXSens[device] * eState.GyroX * 2) : 0); - case DS4Controls.GyroXNeg: return (byte)(!sOff && SXSens[device] * eState.GyroX < -SXD * 10 ? Math.Min(255, SXSens[device] * -eState.GyroX * 2) : 0); - case DS4Controls.GyroZPos: return (byte)(!sOff && SZSens[device] * eState.GyroZ > SZD * 10 ? Math.Min(255, SZSens[device] * eState.GyroZ * 2) : 0); - case DS4Controls.GyroZNeg: return (byte)(!sOff && SZSens[device] * eState.GyroZ < -SZD * 10 ? Math.Min(255, SZSens[device] * -eState.GyroZ * 2) : 0); - case DS4Controls.SwipeUp: return (byte)(tp != null ? tp.swipeUpB : 0); - case DS4Controls.SwipeDown: return (byte)(tp != null ? tp.swipeDownB: 0); - case DS4Controls.SwipeLeft: return (byte)(tp != null ? tp.swipeLeftB: 0); - case DS4Controls.SwipeRight: return (byte)(tp != null ? tp.swipeRightB : 0); - } - return 0; - } + // If any of the calibration points (center, left 90deg, right 90deg) are missing then reset back to default calibration values + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.All) == DS4Device.WheelCalibrationPoint.All)) + Global.SaveControllerConfigs(controller); + else + controller.wheelCenterPoint.X = controller.wheelCenterPoint.Y = 0; - public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return cState.Share; - case DS4Controls.Options: return cState.Options; - case DS4Controls.L1: return cState.L1; - case DS4Controls.R1: return cState.R1; - case DS4Controls.L3: return cState.L3; - case DS4Controls.R3: return cState.R3; - case DS4Controls.DpadUp: return cState.DpadUp; - case DS4Controls.DpadDown: return cState.DpadDown; - case DS4Controls.DpadLeft: return cState.DpadLeft; - case DS4Controls.DpadRight: return cState.DpadRight; - case DS4Controls.PS: return cState.PS; - case DS4Controls.Cross: return cState.Cross; - case DS4Controls.Square: return cState.Square; - case DS4Controls.Triangle: return cState.Triangle; - case DS4Controls.Circle: return cState.Circle; - case DS4Controls.TouchLeft: return (tp != null ? tp.leftDown : false); - case DS4Controls.TouchRight: return (tp != null ? tp.rightDown : false); - case DS4Controls.TouchMulti: return (tp != null ? tp.multiDown : false); - case DS4Controls.TouchUpper: return (tp != null ? tp.upperDown : false); - case DS4Controls.LXNeg: return cState.LX < 127 - 55; - case DS4Controls.LYNeg: return cState.LY < 127 - 55; - case DS4Controls.RXNeg: return cState.RX < 127 - 55; - case DS4Controls.RYNeg: return cState.RY < 127 - 55; - case DS4Controls.LXPos: return cState.LX > 127 + 55; - case DS4Controls.LYPos: return cState.LY > 127 + 55; - case DS4Controls.RXPos: return cState.RX > 127 + 55; - case DS4Controls.RYPos: return cState.RY > 127 + 55; - case DS4Controls.L2: return cState.L2 > 100; - case DS4Controls.R2: return cState.R2 > 100; - case DS4Controls.GyroXPos: return !sOff ? SXSens[device] * eState.GyroX > 67 : false; - case DS4Controls.GyroXNeg: return !sOff ? SXSens[device] * eState.GyroX < -67 : false; - case DS4Controls.GyroZPos: return !sOff ? SZSens[device] * eState.GyroZ > 67 : false; - case DS4Controls.GyroZNeg: return !sOff ? SZSens[device] * eState.GyroZ < -67 : false; - case DS4Controls.SwipeUp: return (tp != null && tp.swipeUp); - case DS4Controls.SwipeDown: return (tp != null && tp.swipeDown); - case DS4Controls.SwipeLeft: return (tp != null && tp.swipeLeft); - case DS4Controls.SwipeRight: return (tp != null && tp.swipeRight); - } - return false; - } + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + else if (controller.WheelRecalibrateActiveState == 4) + { + AppLogger.LogToGui($"Controller {1 + device} cancelled the calibration of SA steering wheel emulation.", false); - public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) - { - byte trueVal = 0; - byte falseVal = 127; - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - if (alt) - trueVal = 255; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? trueVal : falseVal); - case DS4Controls.Options: return (byte)(cState.Options ? trueVal : falseVal); - case DS4Controls.L1: return (byte)(cState.L1 ? trueVal : falseVal); - case DS4Controls.R1: return (byte)(cState.R1 ? trueVal : falseVal); - case DS4Controls.L3: return (byte)(cState.L3 ? trueVal : falseVal); - case DS4Controls.R3: return (byte)(cState.R3 ? trueVal : falseVal); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? trueVal : falseVal); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? trueVal : falseVal); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? trueVal : falseVal); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? trueVal : falseVal); - case DS4Controls.PS: return (byte)(cState.PS ? trueVal : falseVal); - case DS4Controls.Cross: return (byte)(cState.Cross ? trueVal : falseVal); - case DS4Controls.Square: return (byte)(cState.Square ? trueVal : falseVal); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? trueVal : falseVal); - case DS4Controls.Circle: return (byte)(cState.Circle ? trueVal : falseVal); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? trueVal : falseVal); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? trueVal : falseVal); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? trueVal : falseVal); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? trueVal : falseVal); - case DS4Controls.L2: if (alt) return (byte)(127.5f + cState.L2 / 2f); else return (byte)(127.5f - cState.L2 / 2f); - case DS4Controls.R2: if (alt) return (byte)(127.5f + cState.R2 / 2f); else return (byte)(127.5f - cState.R2 / 2f); - case DS4Controls.SwipeUp: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeUpB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeUpB / 2f : 0); - case DS4Controls.SwipeDown: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeDownB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeDownB / 2f : 0); - case DS4Controls.SwipeLeft: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeLeftB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeLeftB / 2f : 0); - case DS4Controls.SwipeRight: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeRightB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeRightB / 2f : 0); - case DS4Controls.GyroXPos: if (!sOff && eState.GyroX > SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * eState.GyroX); - else return falseVal; - case DS4Controls.GyroXNeg: if (!sOff && eState.GyroX < -SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * -eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * -eState.GyroX); - else return falseVal; - case DS4Controls.GyroZPos: if (!sOff && eState.GyroZ > SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * eState.GyroZ); - else return falseVal; - case DS4Controls.GyroZNeg: if (!sOff && eState.GyroZ < -SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * -eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * -eState.GyroZ); - else return falseVal; - } - if (!alt) + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + if (controller.WheelRecalibrateActiveState > 0) { - switch (control) + // Cross "X" key pressed. Set calibration point when the key is released and controller hold steady for a few seconds + if (currentDeviceState.Cross == true) controller.wheelPrevRecalibrateTime = DateTime.Now; + + // Make sure controller is hold steady (velocity of gyro axis) to avoid misaligments and set calibration few secs after the "X" key was released + if (Math.Abs(currentDeviceState.Motion.angVelPitch) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelYaw) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelRoll) < 0.5 + && ((TimeSpan)(DateTime.Now - controller.wheelPrevRecalibrateTime)).TotalSeconds > 1) { - case DS4Controls.LXNeg: return cState.LX; - case DS4Controls.LYNeg: return cState.LY; - case DS4Controls.RXNeg: return cState.RX; - case DS4Controls.RYNeg: return cState.RY; - case DS4Controls.LXPos: return (byte)(255 - cState.LX); - case DS4Controls.LYPos: return (byte)(255 - cState.LY); - case DS4Controls.RXPos: return (byte)(255 - cState.RX); - case DS4Controls.RYPos: return (byte)(255 - cState.RY); + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); + + if (controller.wheelCalibratedAxisBitmask == DS4Device.WheelCalibrationPoint.None) + { + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Center; + } + else if (controller.wheel90DegPointRight.X < gyroAccelX) + { + controller.wheel90DegPointRight.X = gyroAccelX; + controller.wheel90DegPointRight.Y = gyroAccelZ; + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Right90; + } + else if (controller.wheel90DegPointLeft.X > gyroAccelX) + { + controller.wheel90DegPointLeft.X = gyroAccelX; + controller.wheel90DegPointLeft.Y = gyroAccelZ; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Left90; + } } + + // Show lightbar color feedback how the calibration process is proceeding. + // red / yellow / blue / green = No calibration anchors/one anchor/two anchors/all three anchors calibrated when color turns to green (center, 90DegLeft, 90DegRight). + int bitsSet = CountNumOfSetBits((int)controller.wheelCalibratedAxisBitmask); + if (bitsSet >= 3) DS4LightBar.forcedColor[device] = calibrationColor_3; + else if (bitsSet == 2) DS4LightBar.forcedColor[device] = calibrationColor_2; + else if (bitsSet == 1) DS4LightBar.forcedColor[device] = calibrationColor_1; + else DS4LightBar.forcedColor[device] = calibrationColor_0; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Force lightbar flashing when controller is currently at calibration point (user can verify the calibration before accepting it by looking at flashing lightbar) + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Center) != 0 && Math.Abs(result) <= 1 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && result <= -89 * C_WHEEL_ANGLE_PRECISION && result >= -91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Right90) != 0 && result >= 89 * C_WHEEL_ANGLE_PRECISION && result <= 91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && Math.Abs(result) >= 179 * C_WHEEL_ANGLE_PRECISION)) + DS4LightBar.forcedFlash[device] = 2; + else + DS4LightBar.forcedFlash[device] = 0; + + DS4LightBar.forcelight[device] = true; + + LogToGuiSACalibrationDebugMsg($"Calibration values ({gyroAccelX}, {gyroAccelZ}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)}\n"); } else { - switch (control) - { - case DS4Controls.LXNeg: return (byte)(255 - cState.LX); - case DS4Controls.LYNeg: return (byte)(255 - cState.LY); - case DS4Controls.RXNeg: return (byte)(255 - cState.RX); - case DS4Controls.RYNeg: return (byte)(255 - cState.RY); - case DS4Controls.LXPos: return cState.LX; - case DS4Controls.LYPos: return cState.LY; - case DS4Controls.RXPos: return cState.RX; - case DS4Controls.RYPos: return cState.RY; - } + // Re-calibration completed or cancelled. Set lightbar color back to normal color + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = Global.getMainColor(device); + DS4LightBar.forcelight[device] = false; + DS4LightBar.updateLightBar(controller, device); } - return 0; } - //Returns false for any bool, - //if control is one of the xy axis returns 127 - //if its a trigger returns 0 - public static void resetToDefaultValue(DS4Controls control, DS4State cState) + protected static Int32 Scale360degreeGyroAxis(int device, DS4StateExposed exposedState, ControlService ctrl) { - switch (control) - { - case DS4Controls.Share: cState.Share = false; break; - case DS4Controls.Options: cState.Options = false; break; - case DS4Controls.L1: cState.L1 = false; break; - case DS4Controls.R1: cState.R1 = false; break; - case DS4Controls.L3: cState.L3 = false; break; - case DS4Controls.R3: cState.R3 = false; break; - case DS4Controls.DpadUp: cState.DpadUp = false; break; - case DS4Controls.DpadDown: cState.DpadDown = false; break; - case DS4Controls.DpadLeft: cState.DpadLeft = false; break; - case DS4Controls.DpadRight: cState.DpadRight = false; break; - case DS4Controls.PS: cState.PS = false; break; - case DS4Controls.Cross: cState.Cross = false; break; - case DS4Controls.Square: cState.Square = false; break; - case DS4Controls.Triangle: cState.Triangle = false; break; - case DS4Controls.Circle: cState.Circle = false; break; - case DS4Controls.LXNeg: cState.LX = 127; break; - case DS4Controls.LYNeg: cState.LY = 127; break; - case DS4Controls.RXNeg: cState.RX = 127; break; - case DS4Controls.RYNeg: cState.RY = 127; break; - case DS4Controls.LXPos: cState.LX = 127; break; - case DS4Controls.LYPos: cState.LY = 127; break; - case DS4Controls.RXPos: cState.RX = 127; break; - case DS4Controls.RYPos: cState.RY = 127; break; - case DS4Controls.L2: cState.L2 = 0; break; - case DS4Controls.R2: cState.R2 = 0; break; + unchecked + { + DS4Device controller; + DS4State currentDeviceState; + + int gyroAccelX, gyroAccelZ; + int result; + + controller = ctrl.DS4Controllers[device]; + if (controller == null) return 0; + + currentDeviceState = controller.getCurrentStateRef(); + + // If calibration is active then do the calibration process instead of the normal "angle calculation" + if (controller.WheelRecalibrateActiveState > 0) + { + SAWheelEmulationCalibration(device, exposedState, ctrl, currentDeviceState, controller); + + // Return center wheel position while SA wheel emuation is being calibrated + return 0; + } + + // Do nothing if connection is active but the actual DS4 controller is still missing or not yet synchronized + if (!controller.Synced) + return 0; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // If calibration values are missing then use "educated guesses" about good starting values + if (controller.wheelCenterPoint.IsEmpty) + { + if (!Global.LoadControllerConfigs(controller)) + { + AppLogger.LogToGui($"Controller {1 + device} sixaxis steering wheel calibration data missing. It is recommended to run steering wheel calibration process by pressing SASteeringWheelEmulationCalibration special action key. Using estimated values until the controller is calibrated at least once.", false); + + // Use current controller position as "center point". Assume DS4Windows was started while controller was hold in center position (yes, dangerous assumption but can't do much until controller is calibrated) + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheel90DegPointRight.X = controller.wheelCenterPoint.X + 113; + controller.wheel90DegPointRight.Y = controller.wheelCenterPoint.Y + 110; + + controller.wheel90DegPointLeft.X = controller.wheelCenterPoint.X - 127; + controller.wheel90DegPointLeft.Y = controller.wheel90DegPointRight.Y; + } + + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + AppLogger.LogToGui($"Controller {1 + device} steering wheel emulation calibration values. Center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y}) Range={Global.GetSASteeringWheelEmulationRange(device)}", false); + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + + int maxRangeRight = Global.GetSASteeringWheelEmulationRange(device) / 2 * C_WHEEL_ANGLE_PRECISION; + int maxRangeLeft = -maxRangeRight; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Apply deadzone (SA X-deadzone value). This code assumes that 20deg is the max deadzone anyone ever might wanna use (in practice effective deadzone + // is probably just few degrees by using SXDeadZone values 0.01...0.05) + double sxDead = getSXDeadzone(device); + if (sxDead > 0) + { + int sxDeadInt = Convert.ToInt32(20.0 * C_WHEEL_ANGLE_PRECISION * sxDead); + if (Math.Abs(result) <= sxDeadInt) + { + result = 0; + } + else + { + // Smooth steering angle based on deadzone range instead of just clipping the deadzone gap + result -= (result < 0 ? -sxDeadInt : sxDeadInt); + } + } + + // If wrapped around from +180 to -180 side (or vice versa) then SA steering wheel keeps on turning beyond 360 degrees (if range is >360). + // Keep track of how many times the steering wheel has been turned beyond the full 360 circle and clip the result to max range. + int wheelFullTurnCount = controller.wheelFullTurnCount; + if (controller.wheelPrevPhysicalAngle < 0 && result > 0) + { + if ((result - controller.wheelPrevPhysicalAngle) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount--; + else + result = maxRangeLeft; + } + } + else if (controller.wheelPrevPhysicalAngle > 0 && result < 0) + { + if ((controller.wheelPrevPhysicalAngle - result) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount++; + else + result = maxRangeRight; + } + } + controller.wheelPrevPhysicalAngle = result; + + if (wheelFullTurnCount != 0) + { + // Adjust value of result (steering wheel angle) based on num of full 360 turn counts + result += (wheelFullTurnCount * 180 * C_WHEEL_ANGLE_PRECISION * 2); + } + + // If the new angle is more than 180 degrees further away then this is probably bogus value (controller shaking too much and gyro and velocity sensors went crazy). + // Accept the new angle only when the new angle is within a "stability threshold", otherwise use the previous full angle value and wait for controller to be stabilized. + if (Math.Abs(result - controller.wheelPrevFullAngle) <= 180 * C_WHEEL_ANGLE_PRECISION) + { + controller.wheelPrevFullAngle = result; + controller.wheelFullTurnCount = wheelFullTurnCount; + } + else + { + result = controller.wheelPrevFullAngle; + } + + result = Mapping.ClampInt(maxRangeLeft, result, maxRangeRight); + + // Debug log output of SA sensor values + //LogToGuiSACalibrationDebugMsg($"DBG gyro=({gyroAccelX}, {gyroAccelZ}) output=({exposedState.OutputAccelX}, {exposedState.OutputAccelZ}) PitRolYaw=({currentDeviceState.Motion.gyroPitch}, {currentDeviceState.Motion.gyroRoll}, {currentDeviceState.Motion.gyroYaw}) VelPitRolYaw=({currentDeviceState.Motion.angVelPitch}, {currentDeviceState.Motion.angVelRoll}, {currentDeviceState.Motion.angVelYaw}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)} fullTurns={controller.wheelFullTurnCount}", false); + + // Apply anti-deadzone (SA X-antideadzone value) + double sxAntiDead = getSXAntiDeadzone(device); + + int outputAxisMax, outputAxisMin, outputAxisZero; + if ( Global.OutContType[device] == OutContType.DS4 ) + { + // DS4 analog stick axis supports only 0...255 output value range (not the best one for steering wheel usage) + outputAxisMax = 255; + outputAxisMin = 0; + outputAxisZero = 128; + } + else + { + // x360 (xinput) analog stick axis supports -32768...32767 output value range (more than enough for steering wheel usage) + outputAxisMax = 32767; + outputAxisMin = -32768; + outputAxisZero = 0; + } + + switch (Global.GetSASteeringWheelEmulationAxis(device)) + { + case SASteeringWheelEmulationAxisType.LX: + case SASteeringWheelEmulationAxisType.LY: + case SASteeringWheelEmulationAxisType.RX: + case SASteeringWheelEmulationAxisType.RY: + // DS4 thumbstick axis output (-32768..32767 raw value range) + //return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768); + if (result == 0) return outputAxisZero; + + if (sxAntiDead > 0) + { + sxAntiDead *= (outputAxisMax - outputAxisZero); + if (result < 0) return (((result - maxRangeLeft) * (outputAxisZero - Convert.ToInt32(sxAntiDead) - (outputAxisMin))) / (0 - maxRangeLeft)) + (outputAxisMin); + else return (((result - 0) * (outputAxisMax - (outputAxisZero + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (outputAxisZero + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (outputAxisMax - (outputAxisMin))) / (maxRangeRight - maxRangeLeft)) + (outputAxisMin); + } + + case SASteeringWheelEmulationAxisType.L2R2: + // DS4 Trigger axis output. L2+R2 triggers share the same axis in x360 xInput/DInput controller, + // so L2+R2 steering output supports only 360 turn range (-255..255 raw value range in the shared trigger axis) + if (result == 0) return 0; + + result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION))); + if (result < 0) result = -181 - result; + + if (sxAntiDead > 0) + { + sxAntiDead *= 255; + if (result < 0) return (((result - (-180)) * (-Convert.ToInt32(sxAntiDead) - (-255))) / (0 - (-180))) + (-255); + else return (((result - (0)) * (255 - (Convert.ToInt32(sxAntiDead)))) / (180 - (0))) + (Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255); + } + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2X: + case SASteeringWheelEmulationAxisType.VJoy2Y: + case SASteeringWheelEmulationAxisType.VJoy2Z: + // SASteeringWheelEmulationAxisType.VJoy1X/VJoy1Y/VJoy1Z/VJoy2X/VJoy2Y/VJoy2Z VJoy axis output (0..32767 raw value range by default) + if (result == 0) return 16384; + + if (sxAntiDead > 0) + { + sxAntiDead *= 16384; + if (result < 0) return (((result - maxRangeLeft) * (16384 - Convert.ToInt32(sxAntiDead) - (-0))) / (0 - maxRangeLeft)) + (-0); + else return (((result - 0) * (32767 - (16384 + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (16384 + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0); + } + + default: + // Should never come here, but C# case statement syntax requires DEFAULT handler + return 0; + } } } + } } diff --git a/DS4Windows/DS4Control/Mouse.cs b/DS4Windows/DS4Control/Mouse.cs index bd90856253..f939d11587 100644 --- a/DS4Windows/DS4Control/Mouse.cs +++ b/DS4Windows/DS4Control/Mouse.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Runtime.InteropServices; +using System.Drawing; namespace DS4Windows { @@ -17,12 +14,35 @@ public class Mouse : ITouchpadBehaviour private readonly MouseWheel wheel; private bool tappedOnce = false, secondtouchbegin = false; public bool swipeLeft, swipeRight, swipeUp, swipeDown; + public bool priorSwipeLeft, priorSwipeRight, priorSwipeUp, priorSwipeDown; public byte swipeLeftB, swipeRightB, swipeUpB, swipeDownB, swipedB; + public byte priorSwipeLeftB, priorSwipeRightB, priorSwipeUpB, priorSwipeDownB, priorSwipedB; public bool slideleft, slideright; + public bool priorSlideLeft, priorSlideright; // touch area stuff public bool leftDown, rightDown, upperDown, multiDown; + public bool priorLeftDown, priorRightDown, priorUpperDown, priorMultiDown; protected DS4Controls pushed = DS4Controls.None; protected Mapping.Click clicked = Mapping.Click.None; + public int CursorGyroDead { get => cursor.GyroCursorDeadZone; set => cursor.GyroCursorDeadZone = value; } + + internal const int TRACKBALL_INIT_FICTION = 10; + internal const int TRACKBALL_MASS = 45; + internal const double TRACKBALL_RADIUS = 0.0245; + + private double TRACKBALL_INERTIA = 2.0 * (TRACKBALL_MASS * TRACKBALL_RADIUS * TRACKBALL_RADIUS) / 5.0; + private double TRACKBALL_SCALE = 0.004; + private const int TRACKBALL_BUFFER_LEN = 8; + private double[] trackballXBuffer = new double[TRACKBALL_BUFFER_LEN]; + private double[] trackballYBuffer = new double[TRACKBALL_BUFFER_LEN]; + private int trackballBufferTail = 0; + private int trackballBufferHead = 0; + private double trackballAccel = 0.0; + private double trackballXVel = 0.0; + private double trackballYVel = 0.0; + private bool trackballActive = false; + private double trackballDXRemain = 0.0; + private double trackballDYRemain = 0.0; public Mouse(int deviceID, DS4Device d) { @@ -30,25 +50,279 @@ public Mouse(int deviceID, DS4Device d) dev = d; cursor = new MouseCursor(deviceNum); wheel = new MouseWheel(deviceNum); + trackballAccel = TRACKBALL_RADIUS * TRACKBALL_INIT_FICTION / TRACKBALL_INERTIA; + firstTouch = new Touch(0, 0, 0, null); + } + + public void ResetTrackAccel(double friction) + { + trackballAccel = TRACKBALL_RADIUS * friction / TRACKBALL_INERTIA; + } + + public Point GetLastMouseMovementXY() { + return cursor.GetLastMouseMovementXY(); } - public virtual void sixaxisMoved(object sender, SixAxisEventArgs arg) + public void ResetToggleGyroM() { - if (Global.UseSAforMouse[deviceNum] && Global.GyroSensitivity[deviceNum] > 0) + currentToggleGyroM = false; + previousTriggerActivated = false; + triggeractivated = false; + } + + bool triggeractivated = false; + bool previousTriggerActivated = false; + bool useReverseRatchet = false; + bool toggleGyroMouse = true; + public bool ToggleGyroMouse { get => toggleGyroMouse; + set { toggleGyroMouse = value; ResetToggleGyroM(); } } + bool currentToggleGyroM = false; + + public virtual void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs arg) + { + GyroOutMode outMode = Global.GetGyroOutMode(deviceNum); + if (outMode == GyroOutMode.Mouse && Global.getGyroSensitivity(deviceNum) > 0) { - bool triggeractivated = true; + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.getGyroTriggerTurns(deviceNum); int i = 0; - string[] ss = Global.SATriggers[deviceNum].Split(','); + string[] ss = Global.getSATriggers(deviceNum).Split(','); + bool andCond = Global.getSATriggerCond(deviceNum); + triggeractivated = andCond ? true : false; if (!string.IsNullOrEmpty(ss[0])) - foreach (string s in ss) - if (!(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { triggeractivated = false; - if (triggeractivated) + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) cursor.sixaxisMoved(arg); - dev.getCurrentState(s); + else if (!useReverseRatchet && !triggeractivated) + cursor.sixaxisMoved(arg); + else + cursor.mouseRemainderReset(); + + } + else if (outMode == GyroOutMode.MouseJoystick) + { + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.GetGyroMouseStickTriggerTurns(deviceNum); + int i = 0; + string[] ss = Global.GetSAMouseStickTriggers(deviceNum).Split(','); + bool andCond = Global.GetSAMouseStickTriggerCond(deviceNum); + triggeractivated = andCond ? true : false; + if (!string.IsNullOrEmpty(ss[0])) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + triggeractivated = false; + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) + SixMouseStick(arg); + else if (!useReverseRatchet && !triggeractivated) + SixMouseStick(arg); + else + SixMouseReset(arg); } } + private const int SMOOTH_BUFFER_LEN = 3; + private int[] xSmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int[] ySmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + + private void SixMouseReset(SixAxisEventArgs args) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0; + ySmoothBuffer[iIndex] = 0; + smoothBufferTail = iIndex + 1; + } + + private void SixMouseStick(SixAxisEventArgs arg) + { + int deltaX = 0, deltaY = 0; + deltaX = Global.getGyroMouseStickHorizontalAxis(0) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //int inputX = deltaX, inputY = deltaY; + int maxDirX = deltaX >= 0 ? 127 : -128; + int maxDirY = deltaY >= 0 ? 127 : -128; + + GyroMouseStickInfo msinfo = Global.GetGyroMouseStickInfo(deviceNum); + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + int deadzoneX = (int)Math.Abs(normX * msinfo.deadZone); + int deadzoneY = (int)Math.Abs(normY * msinfo.deadZone); + + int maxValX = signX * msinfo.maxZone; + int maxValY = signY * msinfo.maxZone; + + double xratio = 0.0, yratio = 0.0; + double antiX = msinfo.antiDeadX * normX; + double antiY = msinfo.antiDeadY * normY; + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + deltaX = (deltaX < 0 && deltaX < maxValX) ? maxValX : + (deltaX > 0 && deltaX > maxValX) ? maxValX : deltaX; + //if (deltaX != maxValX) deltaX -= deltaX % (signX * GyroMouseFuzz); + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + //if (deltaY != maxValY) deltaY -= deltaY % (signY * GyroMouseFuzz); + } + else + { + deltaY = 0; + } + + if (msinfo.useSmoothing) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = deltaX; + ySmoothBuffer[iIndex] = deltaY; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= msinfo.smoothWeight; + } + + x_out /= finalWeight; + deltaX = (int)x_out; + y_out /= finalWeight; + deltaY = (int)y_out; + + maxValX = deltaX < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxValY = deltaY < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxDirX = deltaX >= 0 ? 127 : -128; + maxDirY = deltaY >= 0 ? 127 : -128; + } + + if (msinfo.vertScale != 100) + { + double verticalScale = msinfo.vertScale * 0.01; + deltaY = (int)(deltaY * verticalScale); + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + } + + if (deltaX != 0) xratio = deltaX / (double)maxValX; + if (deltaY != 0) yratio = deltaY / (double)maxValY; + + double xNorm = 0.0, yNorm = 0.0; + if (xratio != 0.0) + { + xNorm = (1.0 - antiX) * xratio + antiX; + } + + if (yratio != 0.0) + { + yNorm = (1.0 - antiY) * yratio + antiY; + } + + if (msinfo.inverted != 0) + { + if ((msinfo.inverted & 1) == 1) + { + // Invert max dir value + maxDirX = deltaX >= 0 ? -128 : 127; + } + + if ((msinfo.inverted & 2) == 2) + { + // Invert max dir value + maxDirY = deltaY >= 0 ? -128 : 127; + } + } + + byte axisXOut = (byte)(xNorm * maxDirX + 128.0); + byte axisYOut = (byte)(yNorm * maxDirY + 128.0); + Mapping.gyroStickX[deviceNum] = axisXOut; + Mapping.gyroStickY[deviceNum] = axisYOut; + } + private bool getDS4ControlsByName(int key) { switch (key) @@ -59,30 +333,68 @@ private bool getDS4ControlsByName(int key) case 2: return s.Square; case 3: return s.Triangle; case 4: return s.L1; - case 5: return s.L2 > 127; + case 5: return s.L2 > 128; case 6: return s.R1; - case 7: return s.R2 > 127; + case 7: return s.R2 > 128; case 8: return s.DpadUp; case 9: return s.DpadDown; case 10: return s.DpadLeft; case 11: return s.DpadRight; case 12: return s.L3; case 13: return s.R3; - case 14: return s.Touch1; - case 15: return s.Touch2; + case 14: return s.Touch1Finger; + case 15: return s.Touch2Fingers; case 16: return s.Options; case 17: return s.Share; case 18: return s.PS; + default: break; } + return false; } - public virtual void touchesMoved(object sender, TouchpadEventArgs arg) + private bool tempBool = false; + public virtual void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg) { - if (!Global.UseTPforControls[deviceNum]) + s = dev.getCurrentStateRef(); + + if (Global.getUseTPforControls(deviceNum) == false) { - cursor.touchesMoved(arg, dragging || dragging2); - wheel.touchesMoved(arg, dragging || dragging2); + if (Global.GetTouchActive(deviceNum)) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = (arg.touches[0].deltaX * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballYBuffer[iIndex] = (arg.touches[0].deltaY * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + + cursor.touchesMoved(arg, dragging || dragging2, tempBool); + wheel.touchesMoved(arg, dragging || dragging2); + } + else + { + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = 0; + trackballYBuffer[iIndex] = 0; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + } } else { @@ -93,54 +405,78 @@ public virtual void touchesMoved(object sender, TouchpadEventArgs arg) if (arg.touches[0].hwY - firstTouch.hwY > 300) swipeDown = true; if (arg.touches[0].hwY - firstTouch.hwY < -300) swipeUp = true; } + swipeUpB = (byte)Math.Min(255, Math.Max(0, (firstTouch.hwY - arg.touches[0].hwY) * 1.5f)); swipeDownB = (byte)Math.Min(255, Math.Max(0, (arg.touches[0].hwY - firstTouch.hwY) * 1.5f)); swipeLeftB = (byte)Math.Min(255, Math.Max(0, firstTouch.hwX - arg.touches[0].hwX)); swipeRightB = (byte)Math.Min(255, Math.Max(0, arg.touches[0].hwX - firstTouch.hwX)); } + if (Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 50 && arg.touches.Length == 2) + { if (arg.touches[0].hwX - firstTouch.hwX > 200 && !slideleft) slideright = true; else if (firstTouch.hwX - arg.touches[0].hwX > 200 && !slideright) slideleft = true; - dev.getCurrentState(s); + } + synthesizeMouseButtons(); } - public virtual void touchesBegan(object sender, TouchpadEventArgs arg) + + public virtual void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg) { if (!Global.UseTPforControls[deviceNum]) { + Array.Clear(trackballXBuffer, 0, TRACKBALL_BUFFER_LEN); + Array.Clear(trackballYBuffer, 0, TRACKBALL_BUFFER_LEN); + trackballXVel = 0.0; + trackballYVel = 0.0; + trackballActive = false; + trackballBufferTail = 0; + trackballBufferHead = 0; + trackballDXRemain = 0.0; + trackballDYRemain = 0.0; + cursor.touchesBegan(arg); wheel.touchesBegan(arg); } + pastTime = arg.timeStamp; - firstTouch = arg.touches[0]; - if (Global.DoubleTap[deviceNum]) + firstTouch.populate(arg.touches[0].hwX, arg.touches[0].hwY, arg.touches[0].touchID, + arg.touches[0].previousTouch); + + if (Global.getDoubleTap(deviceNum)) { DateTime test = arg.timeStamp; if (test <= (firstTap + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 1.5)) && !arg.touchButtonPressed) secondtouchbegin = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } - public virtual void touchesEnded(object sender, TouchpadEventArgs arg) + + public virtual void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg) { + s = dev.getCurrentStateRef(); slideright = slideleft = false; swipeUp = swipeDown = swipeLeft = swipeRight = false; swipeUpB = swipeDownB = swipeLeftB = swipeRightB = 0; - if (Global.TapSensitivity[deviceNum] != 0 && !Global.UseTPforControls[deviceNum]) + byte tapSensitivity = Global.getTapSensitivity(deviceNum); + if (tapSensitivity != 0 && !Global.getUseTPforControls(deviceNum)) { - if (secondtouchbegin) { tappedOnce = false; secondtouchbegin = false; } + DateTime test = arg.timeStamp; - if (test <= (pastTime + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 2)) && !arg.touchButtonPressed && !tappedOnce) + if (test <= (pastTime + TimeSpan.FromMilliseconds((double)tapSensitivity * 2)) && !arg.touchButtonPressed && !tappedOnce) + { if (Math.Abs(firstTouch.hwX - arg.touches[0].hwX) < 10 && Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 10) - if (Global.DoubleTap[deviceNum]) + { + if (Global.getDoubleTap(deviceNum)) { tappedOnce = true; firstTap = arg.timeStamp; @@ -148,8 +484,97 @@ public virtual void touchesEnded(object sender, TouchpadEventArgs arg) } else Mapping.MapClick(deviceNum, Mapping.Click.Left); //this way no delay if disabled + } + } + } + else + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + if (!trackballActive) + { + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = -1; + for (int i = 0; i < TRACKBALL_BUFFER_LEN && idx != trackballBufferHead; i++) + { + idx = (trackballBufferTail - i - 1 + TRACKBALL_BUFFER_LEN) % TRACKBALL_BUFFER_LEN; + x_out += trackballXBuffer[idx] * currentWeight; + y_out += trackballYBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= 1.0; + } + + x_out /= finalWeight; + trackballXVel = x_out; + y_out /= finalWeight; + trackballYVel = y_out; + + trackballActive = true; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } } - dev.getCurrentState(s); + synthesizeMouseButtons(); } @@ -163,30 +588,98 @@ private bool isRight(Touch t) return t.hwX >= 1920 * 2 / 5; } - public virtual void touchUnchanged(object sender, EventArgs unused) + public virtual void touchUnchanged(DS4Touchpad sender, EventArgs unused) { - dev.getCurrentState(s); - //if (s.Touch1 || s.Touch2 || s.TouchButton) - synthesizeMouseButtons(); + s = dev.getCurrentStateRef(); + + if (trackballActive) + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } + + if (s.Touch1Finger || s.TouchButton) + synthesizeMouseButtons(); } - private DS4State remapped = new DS4State(); public bool dragging, dragging2; + private void synthesizeMouseButtons() { - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft.ToString(), false) == null && leftDown) + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft, false) == null && leftDown) { Mapping.MapClick(deviceNum, Mapping.Click.Left); dragging2 = true; } else + { dragging2 = false; - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper.ToString(), false) == null && upperDown) + } + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper, false) == null && upperDown) Mapping.MapClick(deviceNum, Mapping.Click.Middle); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight.ToString(), false) == null && rightDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight, false) == null && rightDown) Mapping.MapClick(deviceNum, Mapping.Click.Left); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti.ToString(), false) == null && multiDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti, false) == null && multiDown) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (!Global.UseTPforControls[deviceNum]) { if (tappedOnce) @@ -205,23 +698,22 @@ private void synthesizeMouseButtons() dragging = true; } else + { dragging = false; + } } - s = remapped; - //remapped.CopyTo(s); } - public virtual void touchButtonUp(object sender, TouchpadEventArgs arg) + public virtual void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg) { pushed = DS4Controls.None; upperDown = leftDown = rightDown = multiDown = false; - dev.setRumble(0, 0); - dev.getCurrentState(s); + s = dev.getCurrentStateRef(); if (s.Touch1 || s.Touch2) synthesizeMouseButtons(); } - public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) + public virtual void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg) { if (arg.touches == null) upperDown = true; @@ -231,15 +723,30 @@ public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) { if ((Global.LowerRCOn[deviceNum] && arg.touches[0].hwX > (1920 * 3) / 4 && arg.touches[0].hwY > (960 * 3) / 4)) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (isLeft(arg.touches[0])) leftDown = true; else if (isRight(arg.touches[0])) rightDown = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } + public void populatePriorButtonStates() + { + priorUpperDown = upperDown; + priorLeftDown = leftDown; + priorRightDown = rightDown; + priorMultiDown = multiDown; + + priorSwipeLeft = swipeLeft; priorSwipeRight = swipeRight; + priorSwipeUp = swipeUp; priorSwipeDown = swipeDown; + priorSwipeLeftB = swipeLeftB; priorSwipeRightB = swipeRightB; priorSwipeUpB = swipeUpB; + priorSwipeDownB = swipeDownB; priorSwipedB = swipedB; + } + public DS4State getDS4State() { return s; diff --git a/DS4Windows/DS4Control/MouseCursor.cs b/DS4Windows/DS4Control/MouseCursor.cs index 0a5870d3b7..d11ea56865 100644 --- a/DS4Windows/DS4Control/MouseCursor.cs +++ b/DS4Windows/DS4Control/MouseCursor.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - +using System.Drawing; namespace DS4Windows { @@ -20,37 +17,187 @@ public MouseCursor(int deviceNum) /** Indicate x/y direction for doing jitter compensation, etc. */ public enum Direction { Negative, Neutral, Positive } // Track direction vector separately and very trivially for now. - private Direction horizontalDirection = Direction.Neutral, verticalDirection = Direction.Neutral; + private Direction horizontalDirection = Direction.Neutral, + verticalDirection = Direction.Neutral; private Direction hDirection = Direction.Neutral, vDirection = Direction.Neutral; + private const double GYRO_MOUSE_COEFFICIENT = 0.0095; + public const int GYRO_MOUSE_DEADZONE = 10; + private const double GYRO_MOUSE_OFFSET = 0.1463; + private const double GYRO_SMOOTH_MOUSE_OFFSET = 0.14698; + private const double TOUCHPAD_MOUSE_OFFSET = 0.015; + + private const int SMOOTH_BUFFER_LEN = 3; + private double[] xSmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private double[] ySmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + private int gyroCursorDeadZone = GYRO_MOUSE_DEADZONE; + public int GyroCursorDeadZone { get => gyroCursorDeadZone; set => gyroCursorDeadZone = value; } + + Point lastMouseMovementXY = new Point(0, 0); + + double coefficient = 0.0; + double verticalScale = 0.0; + bool gyroSmooth = false; + + int tempInt = 0; + double tempDouble = 0.0; + bool tempBool = false; + public virtual void sixaxisMoved(SixAxisEventArgs arg) { int deltaX = 0, deltaY = 0; - deltaX = -arg.sixAxis.accelX; - deltaY = -arg.sixAxis.accelY; - //Console.WriteLine(arg.sixAxis.deltaX); - - double coefficient = Global.GyroSensitivity[deviceNumber] / 100f; - //Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - xMotion += hRemainder; - int xAction = (int)xMotion; - hRemainder += xMotion - xAction; - hRemainder -= (int)hRemainder; - double yMotion = coefficient * deltaY; - yMotion += vRemainder; - int yAction = (int)yMotion; - vRemainder += yMotion - yAction; - vRemainder -= (int)vRemainder; - if (Global.GyroInvert[deviceNumber] == 2 || Global.GyroInvert[deviceNumber] == 3) + deltaX = Global.getGyroMouseHorizontalAxis(deviceNumber) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //tempDouble = arg.sixAxis.elapsed * 0.001 * 200.0; // Base default speed on 5 ms + tempDouble = arg.sixAxis.elapsed * 200.0; // Base default speed on 5 ms + + gyroSmooth = Global.getGyroSmoothing(deviceNumber); + double gyroSmoothWeight = 0.0; + + coefficient = (Global.getGyroSensitivity(deviceNumber) * 0.01) * GYRO_MOUSE_COEFFICIENT; + double offset = GYRO_MOUSE_OFFSET; + if (gyroSmooth) + { + gyroSmoothWeight = Global.getGyroSmoothingWeight(deviceNumber); + if (gyroSmoothWeight > 0.0) + { + offset = GYRO_SMOOTH_MOUSE_OFFSET; + } + } + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + if (deltaX == 0 || (hRemainder > 0 != deltaX > 0)) + { + hRemainder = 0.0; + } + + if (deltaY == 0 || (vRemainder > 0 != deltaY > 0)) + { + vRemainder = 0.0; + } + + int deadzoneX = (int)Math.Abs(normX * gyroCursorDeadZone); + int deadzoneY = (int)Math.Abs(normY * gyroCursorDeadZone); + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + } + else + { + deltaY = 0; + } + + double xMotion = deltaX != 0 ? coefficient * (deltaX * tempDouble) + + (normX * (offset * signX)) : 0; + + int xAction = 0; + if (xMotion != 0.0) + { + xMotion += hRemainder; + } + else + { + hRemainder = 0.0; + } + + verticalScale = Global.getGyroSensVerticalScale(deviceNumber) * 0.01; + double yMotion = deltaY != 0 ? (coefficient * verticalScale) * (deltaY * tempDouble) + + (normY * (offset * signY)) : 0; + + int yAction = 0; + if (yMotion != 0.0) + { + yMotion += vRemainder; + } + else + { + vRemainder = 0.0; + } + + if (gyroSmooth) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = xMotion; + ySmoothBuffer[iIndex] = yMotion; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= gyroSmoothWeight; + } + + x_out /= finalWeight; + xMotion = x_out; + y_out /= finalWeight; + yMotion = y_out; + } + + hRemainder = vRemainder = 0.0; + if (xMotion != 0.0) + { + xAction = (int)xMotion; + hRemainder = xMotion - xAction; + } + + if (yMotion != 0.0) + { + yAction = (int)yMotion; + vRemainder = yMotion - yAction; + } + + int gyroInvert = Global.getGyroInvert(deviceNumber); + if ((gyroInvert & 0x02) == 2) xAction *= -1; - if (Global.GyroInvert[deviceNumber] == 1 || Global.GyroInvert[deviceNumber] == 3) + + if ((gyroInvert & 0x01) == 1) yAction *= -1; - if (yAction != 0 || xAction != 0) - InputMethods.MoveCursorBy(xAction, yAction); + if (!DS4Lightgun.SET_R2_AS_LIGHTGUN) { + if (yAction != 0 || xAction != 0) + InputMethods.MoveCursorBy(xAction, yAction); + } hDirection = xMotion > 0.0 ? Direction.Positive : xMotion < 0.0 ? Direction.Negative : Direction.Neutral; vDirection = yMotion > 0.0 ? Direction.Positive : yMotion < 0.0 ? Direction.Negative : Direction.Neutral; + + lastMouseMovementXY = new Point(xAction, yAction); + } + + public Point GetLastMouseMovementXY() { + return lastMouseMovementXY; + } + + public void mouseRemainderReset() + { + hRemainder = vRemainder = 0.0; + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0.0; + ySmoothBuffer[iIndex] = 0.0; + smoothBufferTail = iIndex + 1; } public void touchesBegan(TouchpadEventArgs arg) @@ -59,15 +206,18 @@ public void touchesBegan(TouchpadEventArgs arg) { horizontalRemainder = verticalRemainder = 0.0; horizontalDirection = verticalDirection = Direction.Neutral; + } } private byte lastTouchID; - public void touchesMoved(TouchpadEventArgs arg, bool dragging) + public void touchesMoved(TouchpadEventArgs arg, bool dragging, bool disableInvert = false) { - if ((!dragging && arg.touches.Length != 1) || (dragging && arg.touches.Length < 1)) + int touchesLen = arg.touches.Length; + if ((!dragging && touchesLen != 1) || (dragging && touchesLen < 1)) return; - int deltaX, deltaY; + + int deltaX = 0, deltaY = 0; if (arg.touches[0].touchID != lastTouchID) { deltaX = deltaY = 0; @@ -75,11 +225,9 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) horizontalDirection = verticalDirection = Direction.Neutral; lastTouchID = arg.touches[0].touchID; } - else if (Global.TouchpadJitterCompensation[deviceNumber]) + else { - // Often the DS4's internal jitter compensation kicks in and starts hiding changes, ironically creating jitter... - - if (dragging && arg.touches.Length > 1) + if (dragging && touchesLen > 1) { deltaX = arg.touches[1].deltaX; deltaY = arg.touches[1].deltaY; @@ -89,86 +237,75 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) deltaX = arg.touches[0].deltaX; deltaY = arg.touches[0].deltaY; } - // allow only very fine, slow motions, when changing direction, even from neutral - // TODO maybe just consume it completely? - if (deltaX <= -1) - { - if (horizontalDirection != Direction.Negative) - { - deltaX = -1; - horizontalRemainder = 0.0; - } - } - else if (deltaX >= 1) - { - if (horizontalDirection != Direction.Positive) - { - deltaX = 1; - horizontalRemainder = 0.0; - } - } - - if (deltaY <= -1) - { - if (verticalDirection != Direction.Negative) - { - deltaY = -1; - verticalRemainder = 0.0; - } - } - else if (deltaY >= 1) - { - if (verticalDirection != Direction.Positive) - { - deltaY = 1; - verticalRemainder = 0.0; - } - } } - else + + TouchMoveCursor(deltaX, deltaY, disableInvert); + } + + public void TouchMoveCursor(int dx, int dy, bool disableInvert = false) + { + double tempAngle = Math.Atan2(-dy, dx); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(dx); + int signY = Math.Sign(dy); + double coefficient = Global.getTouchSensitivity(deviceNumber) * 0.01; + bool jitterCompenstation = Global.getTouchpadJitterCompensation(deviceNumber); + + double xMotion = dx != 0 ? + coefficient * dx + (normX * (TOUCHPAD_MOUSE_OFFSET * signX)) : 0.0; + + double yMotion = dy != 0 ? + coefficient * dy + (normY * (TOUCHPAD_MOUSE_OFFSET * signY)) : 0.0; + + if (jitterCompenstation) { - if (dragging && arg.touches.Length > 1) + double absX = Math.Abs(xMotion); + if (absX <= normX * 0.15) { - deltaX = arg.touches[1].deltaX; - deltaY = arg.touches[1].deltaY; + xMotion = signX * Math.Pow(absX / 0.15f, 1.408) * 0.15; } - else + + double absY = Math.Abs(yMotion); + if (absY <= normY * 0.15) { - deltaX = arg.touches[0].deltaX; - deltaY = arg.touches[0].deltaY; + yMotion = signY * Math.Pow(absY / 0.15f, 1.408) * 0.15; } } - double coefficient = Global.TouchSensitivity[deviceNumber] / 100.0; // Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - if (xMotion > 0.0) + if (xMotion > 0.0 && horizontalRemainder > 0.0) { - if (horizontalRemainder > 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } - else if (xMotion < 0.0) + else if (xMotion < 0.0 && horizontalRemainder < 0.0) { - if (horizontalRemainder < 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } int xAction = (int)xMotion; horizontalRemainder = xMotion - xAction; - double yMotion = coefficient * deltaY; - if (yMotion > 0.0) + if (yMotion > 0.0 && verticalRemainder > 0.0) { - if (verticalRemainder > 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } - else if (yMotion < 0.0) + else if (yMotion < 0.0 && verticalRemainder < 0.0) { - if (verticalRemainder < 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } int yAction = (int)yMotion; verticalRemainder = yMotion - yAction; + if (disableInvert == false) + { + int touchpadInvert = tempInt = Global.getTouchpadInvert(deviceNumber); + if ((touchpadInvert & 0x02) == 2) + xAction *= -1; + + if ((touchpadInvert & 0x01) == 1) + yAction *= -1; + } + if (yAction != 0 || xAction != 0) InputMethods.MoveCursorBy(xAction, yAction); diff --git a/DS4Windows/DS4Control/MouseWheel.cs b/DS4Windows/DS4Control/MouseWheel.cs index d237731bca..e17ba13aef 100644 --- a/DS4Windows/DS4Control/MouseWheel.cs +++ b/DS4Windows/DS4Control/MouseWheel.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace DS4Windows { @@ -27,6 +23,7 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) { if (arg.touches.Length != 2 || dragging) return; + Touch lastT0 = arg.touches[0].previousTouch; Touch lastT1 = arg.touches[1].previousTouch; Touch T0 = arg.touches[0]; diff --git a/DS4Windows/DS4Control/OutputDevice.cs b/DS4Windows/DS4Control/OutputDevice.cs new file mode 100644 index 0000000000..3e46f526f2 --- /dev/null +++ b/DS4Windows/DS4Control/OutputDevice.cs @@ -0,0 +1,12 @@ +using System; + +namespace DS4Windows +{ + public abstract class OutputDevice + { + public abstract void ConvertandSendReport(DS4State state, int device); + public abstract void Connect(); + public abstract void Disconnect(); + public abstract string GetDeviceType(); + } +} diff --git a/DS4Windows/DS4Control/OutputSlotManager.cs b/DS4Windows/DS4Control/OutputSlotManager.cs new file mode 100644 index 0000000000..429a143e88 --- /dev/null +++ b/DS4Windows/DS4Control/OutputSlotManager.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class OutputSlotManager + { + public const int DELAY_TIME = 500; // measured in ms + + private Dictionary deviceDict = new Dictionary(); + private Dictionary revDeviceDict = new Dictionary(); + private OutputDevice[] outputDevices = new OutputDevice[4]; + private Queue actions = new Queue(); + private object actionLock = new object(); + private bool runningQueue; + + public bool RunningQueue { get => runningQueue; } + + public OutputDevice AllocateController(OutContType contType, ViGEmClient client) + { + OutputDevice outputDevice = null; + switch(contType) + { + case OutContType.X360: + outputDevice = new Xbox360OutDevice(client); + break; + case OutContType.DS4: + outputDevice = new DS4OutDevice(client); + break; + case OutContType.None: + default: + break; + } + + return outputDevice; + } + + private int FindSlot() + { + int result = -1; + for (int i = 0; i < outputDevices.Length && result == -1; i++) + { + OutputDevice tempdev = outputDevices[i]; + if (tempdev == null) + { + result = i; + } + } + + return result; + } + + private void LaunchEvents() + { + bool hasItems = false; + Action act = null; + lock (actionLock) + { + hasItems = actions.Count > 0; + } + + while (hasItems) + { + lock (actionLock) + { + act = actions.Dequeue(); + } + + act.Invoke(); + + lock (actionLock) + { + hasItems = actions.Count > 0; + } + } + } + + private void PrepareEventTask() + { + if (!runningQueue) + { + runningQueue = true; + Task.Run(() => + { + LaunchEvents(); + runningQueue = false; + }); + } + } + + public void DeferredPlugin(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs) + { + Action tempAction = new Action(() => + { + int slot = FindSlot(); + if (slot != -1) + { + outputDevice.Connect(); + outputDevices[slot] = outputDevice; + deviceDict.Add(slot, outputDevice); + revDeviceDict.Add(outputDevice, slot); + Task.Delay(DELAY_TIME).Wait(); + outdevs[inIdx] = outputDevice; + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + + public void DeferredRemoval(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs, bool immediate = false) + { + Action tempAction = new Action(() => + { + if (revDeviceDict.ContainsKey(outputDevice)) + { + int slot = revDeviceDict[outputDevice]; + outputDevices[slot] = null; + deviceDict.Remove(slot); + revDeviceDict.Remove(outputDevice); + outputDevice.Disconnect(); + outdevs[inIdx] = null; + if (!immediate) + { + Task.Delay(DELAY_TIME).Wait(); + } + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + } +} diff --git a/DS4Windows/DS4Control/ProfilePropGroups.cs b/DS4Windows/DS4Control/ProfilePropGroups.cs new file mode 100644 index 0000000000..91eee96b09 --- /dev/null +++ b/DS4Windows/DS4Control/ProfilePropGroups.cs @@ -0,0 +1,98 @@ +using System; + +namespace DS4Windows +{ + public class SquareStickInfo + { + public bool lsMode; + public bool rsMode; + public double lsRoundness = 5.0; + public double rsRoundness = 5.0; + } + + public class StickDeadZoneInfo + { + public int deadZone; + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class TriggerDeadZoneZInfo + { + public byte deadZone; // Trigger deadzone is expressed in axis units + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class GyroMouseInfo + { + + } + + public class GyroMouseStickInfo + { + public int deadZone; + public int maxZone; + public double antiDeadX; + public double antiDeadY; + public int vertScale; + // Flags representing invert axis choices + public uint inverted; + public bool useSmoothing; + public double smoothWeight; + } + + public enum LightbarMode : uint + { + None, + DS4Win, + Passthru, + } + + public class LightbarDS4WinInfo + { + public bool useCustomLed; + public bool ledAsBattery; + public DS4Color m_CustomLed = new DS4Color(0, 0, 255); + public DS4Color m_Led; + public DS4Color m_LowLed; + public DS4Color m_ChargingLed; + public DS4Color m_FlashLed; + public double rainbow; + public double maxRainbowSat = 1.0; + public int flashAt; // Battery % when flashing occurs. <0 means disabled + public byte flashType; + public int chargingType; + } + + public class LightbarSettingInfo + { + public LightbarMode mode = LightbarMode.DS4Win; + public LightbarDS4WinInfo ds4winSettings = new LightbarDS4WinInfo(); + public LightbarMode Mode + { + get => mode; + set + { + if (mode == value) return; + mode = value; + ChangedMode?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ChangedMode; + + public LightbarSettingInfo() + { + /*ChangedMode += (sender, e) => + { + if (mode != LightbarMode.DS4Win) + { + ds4winSettings = null; + } + }; + */ + } + } +} \ No newline at end of file diff --git a/DS4Windows/DS4Control/Program.cs b/DS4Windows/DS4Control/Program.cs new file mode 100644 index 0000000000..c78e7615a8 --- /dev/null +++ b/DS4Windows/DS4Control/Program.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DS4Windows +{ + public class Program + { + public static ControlService rootHub; + } +} diff --git a/DS4Windows/DS4Control/ScpDevice.Designer.cs b/DS4Windows/DS4Control/ScpDevice.Designer.cs deleted file mode 100644 index f1f8f1ed01..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpDevice - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpDevice.cs b/DS4Windows/DS4Control/ScpDevice.cs deleted file mode 100644 index ae276170c7..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.cs +++ /dev/null @@ -1,707 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Windows.Forms; - -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -namespace DS4Windows -{ - public partial class ScpDevice : Component - { - public virtual Boolean IsActive - { - get { return m_IsActive; } - } - - public virtual String Path - { - get { return m_Path; } - } - - - public ScpDevice() - { - InitializeComponent(); - } - - public ScpDevice(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - public ScpDevice(String Class) - { - InitializeComponent(); - - this.m_Class = new Guid(Class); - } - - - public virtual Boolean Open(Int32 Instance = 0) - { - String DevicePath = String.Empty; - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (Find(m_Class, ref DevicePath, Instance)) - { - Open(DevicePath); - } - - return m_IsActive; - } - - public virtual Boolean Open(String DevicePath) - { - m_Path = DevicePath.ToUpper(); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (GetDeviceHandle(m_Path)) - { - if (WinUsb_Initialize(m_FileHandle, ref m_WinUsbHandle)) - { - if (InitializeDevice()) - { - m_IsActive = true; - } - else - { - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - } - else - { - m_FileHandle.Close(); - } - } - - return m_IsActive; - } - - public virtual Boolean Start() - { - return m_IsActive; - } - - public virtual Boolean Stop() - { - m_IsActive = false; - - if (!(m_WinUsbHandle == (IntPtr) INVALID_HANDLE_VALUE)) - { - WinUsb_AbortPipe(m_WinUsbHandle, m_IntIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkOut); - - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - - if (m_FileHandle != null && !m_FileHandle.IsInvalid && !m_FileHandle.IsClosed) - { - m_FileHandle.Close(); - m_FileHandle = null; - } - - return true; - } - - public virtual Boolean Close() - { - return Stop(); - } - - - public virtual Boolean ReadIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_IntIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean ReadBulkPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_BulkIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_IntOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteBulkPipe(Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_BulkOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - - public virtual Boolean SendTransfer(Byte RequestType, Byte Request, UInt16 Value, Byte[] Buffer, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - WINUSB_SETUP_PACKET Setup = new WINUSB_SETUP_PACKET(); - - Setup.RequestType = RequestType; - Setup.Request = Request; - Setup.Value = Value; - Setup.Index = 0; - Setup.Length = (UInt16) Buffer.Length; - - return WinUsb_ControlTransfer(m_WinUsbHandle, Setup, Buffer, Buffer.Length, ref Transfered, IntPtr.Zero); - } - - - #region Constant and Structure Definitions - public const Int32 SERVICE_CONTROL_STOP = 0x00000001; - public const Int32 SERVICE_CONTROL_SHUTDOWN = 0x00000005; - public const Int32 SERVICE_CONTROL_DEVICEEVENT = 0x0000000B; - public const Int32 SERVICE_CONTROL_POWEREVENT = 0x0000000D; - - public const Int32 DBT_DEVICEARRIVAL = 0x8000; - public const Int32 DBT_DEVICEQUERYREMOVE = 0x8001; - public const Int32 DBT_DEVICEREMOVECOMPLETE = 0x8004; - public const Int32 DBT_DEVTYP_DEVICEINTERFACE = 0x0005; - public const Int32 DBT_DEVTYP_HANDLE = 0x0006; - - public const Int32 PBT_APMRESUMEAUTOMATIC = 0x0012; - public const Int32 PBT_APMSUSPEND = 0x0004; - - public const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0x0000; - public const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 0x0001; - public const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x0004; - - public const Int32 WM_DEVICECHANGE = 0x0219; - - public const Int32 DIGCF_PRESENT = 0x0002; - public const Int32 DIGCF_DEVICEINTERFACE = 0x0010; - - public delegate Int32 ServiceControlHandlerEx(Int32 Control, Int32 Type, IntPtr Data, IntPtr Context); - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_DEVICEINTERFACE - { - internal Int32 dbcc_size; - internal Int32 dbcc_devicetype; - internal Int32 dbcc_reserved; - internal Guid dbcc_classguid; - internal Int16 dbcc_name; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public class DEV_BROADCAST_DEVICEINTERFACE_M - { - public Int32 dbcc_size; - public Int32 dbcc_devicetype; - public Int32 dbcc_reserved; - - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] - public Byte[] dbcc_classguid; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] - public Char[] dbcc_name; - } - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_HDR - { - public Int32 dbch_size; - public Int32 dbch_devicetype; - public Int32 dbch_reserved; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_DEVICE_INTERFACE_DATA - { - internal Int32 cbSize; - internal Guid InterfaceClassGuid; - internal Int32 Flags; - internal IntPtr Reserved; - } - - protected const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80; - protected const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; - protected const UInt32 FILE_SHARE_READ = 1; - protected const UInt32 FILE_SHARE_WRITE = 2; - protected const UInt32 GENERIC_READ = 0x80000000; - protected const UInt32 GENERIC_WRITE = 0x40000000; - protected const Int32 INVALID_HANDLE_VALUE = -1; - protected const UInt32 OPEN_EXISTING = 3; - protected const UInt32 DEVICE_SPEED = 1; - protected const Byte USB_ENDPOINT_DIRECTION_MASK = 0x80; - - protected enum POLICY_TYPE - { - SHORT_PACKET_TERMINATE = 1, - AUTO_CLEAR_STALL = 2, - PIPE_TRANSFER_TIMEOUT = 3, - IGNORE_SHORT_PACKETS = 4, - ALLOW_PARTIAL_READS = 5, - AUTO_FLUSH = 6, - RAW_IO = 7, - } - - protected enum USBD_PIPE_TYPE - { - UsbdPipeTypeControl = 0, - UsbdPipeTypeIsochronous = 1, - UsbdPipeTypeBulk = 2, - UsbdPipeTypeInterrupt = 3, - } - - protected enum USB_DEVICE_SPEED - { - UsbLowSpeed = 1, - UsbFullSpeed = 2, - UsbHighSpeed = 3, - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_CONFIGURATION_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal UInt16 wTotalLength; - internal Byte bNumInterfaces; - internal Byte bConfigurationValue; - internal Byte iConfiguration; - internal Byte bmAttributes; - internal Byte MaxPower; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_INTERFACE_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal Byte bInterfaceNumber; - internal Byte bAlternateSetting; - internal Byte bNumEndpoints; - internal Byte bInterfaceClass; - internal Byte bInterfaceSubClass; - internal Byte bInterfaceProtocol; - internal Byte iInterface; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct WINUSB_PIPE_INFORMATION - { - internal USBD_PIPE_TYPE PipeType; - internal Byte PipeId; - internal UInt16 MaximumPacketSize; - internal Byte Interval; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - protected struct WINUSB_SETUP_PACKET - { - internal Byte RequestType; - internal Byte Request; - internal UInt16 Value; - internal UInt16 Index; - internal UInt16 Length; - } - - protected const Int32 DIF_PROPERTYCHANGE = 0x12; - protected const Int32 DICS_ENABLE = 1; - protected const Int32 DICS_DISABLE = 2; - protected const Int32 DICS_PROPCHANGE = 3; - protected const Int32 DICS_FLAG_GLOBAL = 1; - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_CLASSINSTALL_HEADER - { - internal Int32 cbSize; - internal Int32 InstallFunction; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_PROPCHANGE_PARAMS - { - internal SP_CLASSINSTALL_HEADER ClassInstallHeader; - internal Int32 StateChange; - internal Int32 Scope; - internal Int32 HwProfile; - } - #endregion - - #region Protected Data Members - protected Guid m_Class = Guid.Empty; - protected String m_Path = String.Empty; - - protected SafeFileHandle m_FileHandle = null; - protected IntPtr m_WinUsbHandle = IntPtr.Zero; - - protected Byte m_IntIn = 0xFF; - protected Byte m_IntOut = 0xFF; - protected Byte m_BulkIn = 0xFF; - protected Byte m_BulkOut = 0xFF; - - protected Boolean m_IsActive = false; - #endregion - - #region Static Helper Methods - public enum Notified { Ignore = 0x0000, Arrival = 0x8000, QueryRemove = 0x8001, Removal = 0x8004 }; - - public static Boolean RegisterNotify(IntPtr Form, Guid Class, ref IntPtr Handle, Boolean Window = true) - { - IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; - - try - { - DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); - Int32 Size = Marshal.SizeOf(devBroadcastDeviceInterface); - - devBroadcastDeviceInterface.dbcc_size = Size; - devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - devBroadcastDeviceInterface.dbcc_reserved = 0; - devBroadcastDeviceInterface.dbcc_classguid = Class; - - devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(Size); - Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); - - Handle = RegisterDeviceNotification(Form, devBroadcastDeviceInterfaceBuffer, Window ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE); - - Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); - - return Handle != IntPtr.Zero; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) - { - Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); - } - } - } - - public static Boolean UnregisterNotify(IntPtr Handle) - { - try - { - return UnregisterDeviceNotification(Handle); - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - #endregion - - #region Protected Methods - protected virtual Boolean Find(Guid Target, ref String Path, Int32 Instance = 0) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref Target, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref Target, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4: detailDataBuffer.ToInt64() + 4); - - Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (memberIndex == Instance) return true; - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceInstance(ref String Instance) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref m_Class, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4 : detailDataBuffer.ToInt64() + 4); - - String Current = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (Current == Path) - { - Int32 nBytes = 256; - IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); - - CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0); - Instance = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper(); - - Marshal.FreeHGlobal(ptrInstanceBuf); - return true; - } - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceHandle(String Path) - { - m_FileHandle = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); - - return !m_FileHandle.IsInvalid; - } - - protected virtual Boolean UsbEndpointDirectionIn(Int32 addr) - { - return (addr & 0x80) == 0x80; - } - - protected virtual Boolean UsbEndpointDirectionOut(Int32 addr) - { - return (addr & 0x80) == 0x00; - } - - protected virtual Boolean InitializeDevice() - { - try - { - USB_INTERFACE_DESCRIPTOR ifaceDescriptor = new USB_INTERFACE_DESCRIPTOR(); - WINUSB_PIPE_INFORMATION pipeInfo = new WINUSB_PIPE_INFORMATION(); - - if (WinUsb_QueryInterfaceSettings(m_WinUsbHandle, 0, ref ifaceDescriptor)) - { - for (Int32 i = 0; i < ifaceDescriptor.bNumEndpoints; i++) - { - WinUsb_QueryPipe(m_WinUsbHandle, 0, System.Convert.ToByte(i), ref pipeInfo); - - if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionIn(pipeInfo.PipeId))) - { - m_BulkIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkIn); - } - else if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionOut(pipeInfo.PipeId))) - { - m_BulkOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkOut); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionIn(pipeInfo.PipeId)) - { - m_IntIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntIn); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionOut(pipeInfo.PipeId)) - { - m_IntOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntOut); - } - } - - return true; - } - - return false; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - - protected virtual Boolean RestartDevice(String InstanceId) - { - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); - - deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if (SetupDiOpenDeviceInfo(deviceInfoSet, InstanceId, IntPtr.Zero, 0, ref deviceInterfaceData)) - { - SP_PROPCHANGE_PARAMS props = new SP_PROPCHANGE_PARAMS(); - - props.ClassInstallHeader = new SP_CLASSINSTALL_HEADER(); - props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader); - props.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; - - props.Scope = DICS_FLAG_GLOBAL; - props.StateChange = DICS_PROPCHANGE; - props.HwProfile = 0x00; - - if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, Marshal.SizeOf(props))) - { - return SetupDiChangeState(deviceInfoSet, ref deviceInterfaceData); - } - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - #endregion - - #region Interop Definitions - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - protected static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); - - [DllImport("user32.dll", SetLastError = true)] - protected static extern Boolean UnregisterDeviceNotification(IntPtr Handle); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UInt32 hTemplateFile); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, ref USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, ref WINUSB_PIPE_INFORMATION PipeInformation); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_AbortPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_FlushPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ReadPipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_WritePipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Free(IntPtr InterfaceHandle); - - [DllImport("advapi32.dll", SetLastError = true)] - public static extern IntPtr RegisterServiceCtrlHandlerEx(String ServiceName, ServiceControlHandlerEx Callback, IntPtr Context); - - [DllImport("kernel32.dll", SetLastError = true)] - protected static extern Boolean DeviceIoControl(SafeFileHandle DeviceHandle, Int32 IoControlCode, Byte[] InBuffer, Int32 InBufferSize, Byte[] OutBuffer, Int32 OutBufferSize, ref Int32 BytesReturned, IntPtr Overlapped); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Int32 CM_Get_Device_ID(Int32 dnDevInst, IntPtr Buffer, Int32 BufferLen, Int32 ulFlags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, String DeviceInstanceId, IntPtr hwndParent, Int32 Flags, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiChangeState(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_PROPCHANGE_PARAMS ClassInstallParams, Int32 ClassInstallParamsSize); - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.Designer.cs b/DS4Windows/DS4Control/ScpHub.Designer.cs deleted file mode 100644 index ed08d0c1c0..0000000000 --- a/DS4Windows/DS4Control/ScpHub.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpHub - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.cs b/DS4Windows/DS4Control/ScpHub.cs deleted file mode 100644 index fd7031a495..0000000000 --- a/DS4Windows/DS4Control/ScpHub.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.ComponentModel; - -namespace DS4Windows -{ - public partial class ScpHub : Component - { - protected IntPtr m_Reference = IntPtr.Zero; - protected volatile Boolean m_Started = false; - - public event EventHandler Debug = null; - - public event EventHandler Report = null; - - protected virtual Boolean LogDebug(String Data, bool warning) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - - On_Debug(this, args); - - return true; - } - - public Boolean Active - { - get { return m_Started; } - } - - - public ScpHub() - { - InitializeComponent(); - } - - public ScpHub(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - - public virtual Boolean Open() - { - return true; - } - - public virtual Boolean Start() - { - return m_Started; - } - - public virtual Boolean Stop() - { - return !m_Started; - } - - public virtual Boolean Close() - { - if (m_Reference != IntPtr.Zero) ScpDevice.UnregisterNotify(m_Reference); - - return !m_Started; - } - - - public virtual Boolean Suspend() - { - return true; - } - - public virtual Boolean Resume() - { - return true; - } - - protected virtual void On_Debug(object sender, DebugEventArgs e) - { - if (Debug != null) Debug(sender, e); - } - - - protected virtual void On_Report(object sender, ReportEventArgs e) - { - if (Report != null) Report(sender, e); - } - } -} diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs index 2b23fcb603..f755a0e422 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.IO; using System.Reflection; @@ -8,18 +8,34 @@ using System.Drawing; using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; +using System.Diagnostics; + namespace DS4Windows { [Flags] - public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; //Increment by exponents of 2*, starting at 2^0 + public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; // Increment by exponents of 2*, starting at 2^0 public enum Ds3PadId : byte { None = 0xFF, One = 0x00, Two = 0x01, Three = 0x02, Four = 0x03, All = 0x04 }; public enum DS4Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, L1, L2, L3, R1, R2, R3, Square, Triangle, Circle, Cross, DpadUp, DpadRight, DpadDown, DpadLeft, PS, TouchLeft, TouchUpper, TouchMulti, TouchRight, Share, Options, GyroXPos, GyroXNeg, GyroZPos, GyroZNeg, SwipeLeft, SwipeRight, SwipeUp, SwipeDown }; public enum X360Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, LB, LT, LS, RB, RT, RS, X, Y, B, A, DpadUp, DpadRight, DpadDown, DpadLeft, Guide, Back, Start, LeftMouse, RightMouse, MiddleMouse, FourthMouse, FifthMouse, WUP, WDOWN, MouseUp, MouseDown, MouseLeft, MouseRight, Unbound }; + public enum SASteeringWheelEmulationAxisType: byte { None = 0, LX, LY, RX, RY, L2R2, VJoy1X, VJoy1Y, VJoy1Z, VJoy2X, VJoy2Y, VJoy2Z }; + public enum OutContType : uint { None = 0, X360, DS4 } + + public enum GyroOutMode : uint + { + None, + Controls, + Mouse, + MouseJoystick, + } + public class DS4ControlSettings { public DS4Controls control; - public string extras = "0,0,0,0,0,0,0,0"; + public string extras = null; public DS4KeyType keyType = DS4KeyType.None; public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType actionType = ActionType.Default; @@ -27,7 +43,7 @@ public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType shiftActionType = ActionType.Default; public object shiftAction = null; public int shiftTrigger = 0; - public string shiftExtras = "0,0,0,0,0,0,0,0"; + public string shiftExtras = null; public DS4KeyType shiftKeyType = DS4KeyType.None; public DS4ControlSettings(DS4Controls ctrl) @@ -37,17 +53,17 @@ public DS4ControlSettings(DS4Controls ctrl) public void Reset() { - extras = "0,0,0,0,0,0,0,0"; + extras = null; keyType = DS4KeyType.None; actionType = ActionType.Default; action = null; shiftActionType = ActionType.Default; shiftAction = null; shiftTrigger = 0; - shiftExtras = "0,0,0,0,0,0,0,0"; + shiftExtras = null; shiftKeyType = DS4KeyType.None; } - + internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, int trigger = 0) { if (!shift) @@ -60,6 +76,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, actionType = ActionType.Macro; else actionType = ActionType.Default; + action = act; extras = exts; keyType = kt; @@ -74,6 +91,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, shiftActionType = ActionType.Macro; else shiftActionType = ActionType.Default; + shiftAction = act; shiftExtras = exts; shiftKeyType = kt; @@ -85,16 +103,16 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, public class DebugEventArgs : EventArgs { protected DateTime m_Time = DateTime.Now; - protected String m_Data = String.Empty; + protected string m_Data = string.Empty; protected bool warning = false; - public DebugEventArgs(String Data, bool warn) + public DebugEventArgs(string Data, bool warn) { m_Data = Data; warning = warn; } public DateTime Time => m_Time; - public String Data => m_Data; + public string Data => m_Data; public bool Warning => warning; } @@ -113,7 +131,7 @@ public MappingDoneEventArgs(int DeviceID) public class ReportEventArgs : EventArgs { protected Ds3PadId m_Pad = Ds3PadId.None; - protected Byte[] m_Report = new Byte[64]; + protected byte[] m_Report = new byte[64]; public ReportEventArgs() { @@ -136,20 +154,337 @@ public Byte[] Report } } + public class BatteryReportArgs : EventArgs + { + private int index; + private int level; + private bool charging; + + public BatteryReportArgs(int index, int level, bool charging) + { + this.index = index; + this.level = level; + this.charging = charging; + } + + public int getIndex() + { + return index; + } + + public int getLevel() + { + return level; + } + + public bool isCharging() + { + return charging; + } + } + + public class ControllerRemovedArgs : EventArgs + { + private int index; + + public ControllerRemovedArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return this.index; + } + } + + public class DeviceStatusChangeEventArgs : EventArgs + { + private int index; + + public DeviceStatusChangeEventArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return index; + } + } + + public class SerialChangeArgs : EventArgs + { + private int index; + private string serial; + + public SerialChangeArgs(int index, string serial) + { + this.index = index; + this.serial = serial; + } + + public int getIndex() + { + return index; + } + + public string getSerial() + { + return serial; + } + } + public class Global { protected static BackingStore m_Config = new BackingStore(); protected static Int32 m_IdleTimeout = 600000; - static string exepath = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName; + public static string exelocation = Assembly.GetExecutingAssembly().Location; + public static string exedirpath = Directory.GetParent(exelocation).FullName; + public static FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(exelocation); + public static string exeversion = fileVersion.ProductVersion; public static string appdatapath; + public static bool firstRun = false; + public static bool multisavespots = false; + public static string appDataPpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Windows"; + public static bool runHotPlug = false; public static string[] tempprofilename = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public static bool[] useTempProfile = new bool[5] { false, false, false, false, false }; + public static bool[] tempprofileDistance = new bool[5] { false, false, false, false, false }; + public static bool[] useDInputOnly = new bool[5] { true, true, true, true, true }; + public static bool[] linkedProfileCheck = new bool[4] { false, false, false, false }; + public static bool[] touchpadActive = new bool[5] { true, true, true, true, true }; + // Used to hold device type desired from Profile Editor + public static OutContType[] outDevTypeTemp = new OutContType[5] { DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360 }; + // Used to hold the currently active controller output type in use for a slot + public static OutContType[] activeOutDevType = new OutContType[5] { DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None }; + public static bool vigemInstalled = IsViGEmBusInstalled(); + public static bool hidguardInstalled = IsHidGuardianInstalled(); + public static string vigembusVersion = ViGEmBusVersion(); + + public static X360Controls[] defaultButtonMapping = { X360Controls.None, X360Controls.LXNeg, X360Controls.LXPos, + X360Controls.LYNeg, X360Controls.LYPos, X360Controls.RXNeg, X360Controls.RXPos, X360Controls.RYNeg, X360Controls.RYPos, + X360Controls.LB, X360Controls.LT, X360Controls.LS, X360Controls.RB, X360Controls.RT, X360Controls.RS, X360Controls.X, + X360Controls.Y, X360Controls.B, X360Controls.A, X360Controls.DpadUp, X360Controls.DpadRight, X360Controls.DpadDown, + X360Controls.DpadLeft, X360Controls.Guide, X360Controls.LeftMouse, X360Controls.MiddleMouse, X360Controls.RightMouse, X360Controls.LeftMouse, + X360Controls.Back, X360Controls.Start, X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None, + X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None + }; + + // Create mapping array at runtime + public static DS4Controls[] reverseX360ButtonMapping = new Func(() => + { + DS4Controls[] temp = new DS4Controls[defaultButtonMapping.Length]; + for (int i = 0, arlen = defaultButtonMapping.Length; i < arlen; i++) + { + X360Controls mapping = defaultButtonMapping[i]; + if (mapping != X360Controls.None) + { + temp[(int)mapping] = (DS4Controls)i; + } + } + + return temp; + })(); + + public static Dictionary xboxDefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "Left Bumper", + [X360Controls.LT] = "Left Trigger", + [X360Controls.LS] = "Left Stick", + [X360Controls.RB] = "Right Bumper", + [X360Controls.RT] = "Right Trigger", + [X360Controls.RS] = "Right Stick", + [X360Controls.X] = "X Button", + [X360Controls.Y] = "Y Button", + [X360Controls.B] = "B Button", + [X360Controls.A] = "A Button", + [X360Controls.DpadUp] = "Up Button", + [X360Controls.DpadRight] = "Right Button", + [X360Controls.DpadDown] = "Down Button", + [X360Controls.DpadLeft] = "Left Button", + [X360Controls.Guide] = "Guide", + [X360Controls.Back] = "Back", + [X360Controls.Start] = "Start", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static Dictionary ds4DefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "L1", + [X360Controls.LT] = "L2", + [X360Controls.LS] = "L3", + [X360Controls.RB] = "R1", + [X360Controls.RT] = "R2", + [X360Controls.RS] = "R3", + [X360Controls.X] = "Square", + [X360Controls.Y] = "Triangle", + [X360Controls.B] = "Circle", + [X360Controls.A] = "Cross", + [X360Controls.DpadUp] = "Dpad Up", + [X360Controls.DpadRight] = "Dpad Right", + [X360Controls.DpadDown] = "Dpad Down", + [X360Controls.DpadLeft] = "Dpad Left", + [X360Controls.Guide] = "PS", + [X360Controls.Back] = "Share", + [X360Controls.Start] = "Options", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static string getX360ControlString(X360Controls key, OutContType conType) + { + string result = string.Empty; + if (conType == DS4Windows.OutContType.X360) + { + xboxDefaultNames.TryGetValue(key, out result); + } + else if (conType == DS4Windows.OutContType.DS4) + { + ds4DefaultNames.TryGetValue(key, out result); + } + + return result; + } + + public static Dictionary ds4inputNames = new Dictionary() + { + [DS4Controls.LXNeg] = "Left X-Axis-", + [DS4Controls.LXPos] = "Left X-Axis+", + [DS4Controls.LYNeg] = "Left Y-Axis-", + [DS4Controls.LYPos] = "Left Y-Axis+", + [DS4Controls.RXNeg] = "Right X-Axis-", + [DS4Controls.RXPos] = "Right X-Axis+", + [DS4Controls.RYNeg] = "Right Y-Axis-", + [DS4Controls.RYPos] = "Right Y-Axis+", + [DS4Controls.L1] = "L1", + [DS4Controls.L2] = "L2", + [DS4Controls.L3] = "L3", + [DS4Controls.R1] = "R1", + [DS4Controls.R2] = "R2", + [DS4Controls.R3] = "R3", + [DS4Controls.Square] = "Square", + [DS4Controls.Triangle] = "Triangle", + [DS4Controls.Circle] = "Circle", + [DS4Controls.Cross] = "Cross", + [DS4Controls.DpadUp] = "Dpad Up", + [DS4Controls.DpadRight] = "Dpad Right", + [DS4Controls.DpadDown] = "Dpad Down", + [DS4Controls.DpadLeft] = "Dpad Left", + [DS4Controls.PS] = "PS", + [DS4Controls.Share] = "Share", + [DS4Controls.Options] = "Options", + [DS4Controls.TouchLeft] = "Left Touch", + [DS4Controls.TouchUpper] = "Upper Touch", + [DS4Controls.TouchMulti] = "Multitouch", + [DS4Controls.TouchRight] = "Right Touch", + [DS4Controls.GyroXPos] = "Gyro X+", + [DS4Controls.GyroXNeg] = "Gyro X-", + [DS4Controls.GyroZPos] = "Gyro Z+", + [DS4Controls.GyroZNeg] = "Gyro Z-", + [DS4Controls.SwipeLeft] = "Swipe Left", + [DS4Controls.SwipeRight] = "Swipe Right", + [DS4Controls.SwipeUp] = "Swipe Up", + [DS4Controls.SwipeDown] = "Swipe Down", + }; + + public static Dictionary macroDS4Values = new Dictionary() + { + [DS4Controls.Cross] = 261, [DS4Controls.Circle] = 262, + [DS4Controls.Square] = 263, [DS4Controls.Triangle] = 264, + [DS4Controls.Options] = 265, [DS4Controls.Share] = 266, + [DS4Controls.DpadUp] = 267, [DS4Controls.DpadDown] = 268, + [DS4Controls.DpadLeft] = 269, [DS4Controls.DpadRight] = 270, + [DS4Controls.PS] = 271, [DS4Controls.L1] = 272, + [DS4Controls.R1] = 273, [DS4Controls.L2] = 274, + [DS4Controls.R2] = 275, [DS4Controls.L3] = 276, + [DS4Controls.R3] = 277, [DS4Controls.LXPos] = 278, + [DS4Controls.LXNeg] = 279, [DS4Controls.LYPos] = 280, + [DS4Controls.LYNeg] = 281, [DS4Controls.RXPos] = 282, + [DS4Controls.RXNeg] = 283, [DS4Controls.RYPos] = 284, + [DS4Controls.RYNeg] = 285, + }; public static void SaveWhere(string path) { appdatapath = path; m_Config.m_Profile = appdatapath + "\\Profiles.xml"; m_Config.m_Actions = appdatapath + "\\Actions.xml"; + m_Config.m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + m_Config.m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; + } + + public static bool SaveDefault(string path) + { + Boolean Saved = true; + XmlDocument m_Xdoc = new XmlDocument(); + try + { + XmlNode Node; + + m_Xdoc.RemoveAll(); + + Node = m_Xdoc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateComment(string.Format(" Profile Configuration Data. {0} ", DateTime.Now)); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateWhitespace("\r\n"); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateNode(XmlNodeType.Element, "Profile", null); + + m_Xdoc.AppendChild(Node); + + m_Xdoc.Save(path); + } + catch { Saved = false; } + + return Saved; } + /// /// Check if Admin Rights are needed to write in Appliplation Directory /// @@ -158,8 +493,8 @@ public static bool AdminNeeded() { try { - File.WriteAllText(exepath + "\\test.txt", "test"); - File.Delete(exepath + "\\test.txt"); + File.WriteAllText(exedirpath + "\\test.txt", "test"); + File.Delete(exedirpath + "\\test.txt"); return false; } catch (UnauthorizedAccessException) @@ -175,6 +510,230 @@ public static bool IsAdministrator() return principal.IsInRole(WindowsBuiltInRole.Administrator); } + public static bool CheckForDevice(string guid) + { + bool result = false; + Guid deviceGuid = Guid.Parse(guid); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + result = NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static bool CheckForSysDevice(string searchHardwareId) + { + bool result = false; + Guid sysGuid = Guid.Parse("{4d36e97d-e325-11ce-bfc1-08002be10318}"); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref sysGuid, null, 0, 0); + for (int i = 0; !result && NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++) + { + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, + ref NativeMethods.DEVPKEY_Device_HardwareIds, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + string hardwareId = dataBuffer.ToUTF16String(); + //if (hardwareIds.Contains("Virtual Gamepad Emulation Bus")) + // result = true; + if (hardwareId.Equals(searchHardwareId)) + result = true; + } + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + internal static string GetDeviceProperty(string deviceInstanceId, + NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + NativeMethods.SP_DEVINFO_DATA deviceInfoData = new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + Guid hidGuid = new Guid(); + NativeMethods.HidD_GetHidGuid(ref hidGuid); + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static string GetViGEmDriverProperty(NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + Guid deviceGuid = Guid.Parse(VIGEMBUS_GUID); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + public static bool IsHidGuardianInstalled() + { + return CheckForSysDevice(@"Root\HidGuardian"); + } + + const string VIGEMBUS_GUID = "{96E42B22-F5E9-42F8-B043-ED0F932F014F}"; + public static bool IsViGEmBusInstalled() + { + return CheckForDevice(VIGEMBUS_GUID); + } + + public static string ViGEmBusVersion() + { + return GetViGEmDriverProperty(NativeMethods.DEVPKEY_Device_DriverVersion); + } + + public static void FindConfigLocation() + { + if (File.Exists(exedirpath + "\\Auto Profiles.xml") + && File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = true; + } + else if (File.Exists(exedirpath + "\\Auto Profiles.xml")) + SaveWhere(exedirpath); + else if (File.Exists(appDataPpath + "\\Auto Profiles.xml")) + SaveWhere(appDataPpath); + else if (!File.Exists(exedirpath + "\\Auto Profiles.xml") + && !File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = false; + } + } + + public static void SetCulture(string culture) + { + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo(culture); + } + catch { /* Skip setting culture that we cannot set */ } + } + + public static void CreateStdActions() + { + XmlDocument xDoc = new XmlDocument(); + try + { + string[] profiles = Directory.GetFiles(appdatapath + @"\Profiles\"); + string s = string.Empty; + //foreach (string s in profiles) + for (int i = 0, proflen = profiles.Length; i < proflen; i++) + { + s = profiles[i]; + if (Path.GetExtension(s) == ".xml") + { + xDoc.Load(s); + XmlNode el = xDoc.SelectSingleNode("DS4Windows/ProfileActions"); + if (el != null) + { + if (string.IsNullOrEmpty(el.InnerText)) + el.InnerText = "Disconnect Controller"; + else + el.InnerText += "/Disconnect Controller"; + } + else + { + XmlNode Node = xDoc.SelectSingleNode("DS4Windows"); + el = xDoc.CreateElement("ProfileActions"); + el.InnerText = "Disconnect Controller"; + Node.AppendChild(el); + } + + xDoc.Save(s); + LoadActions(); + } + } + } + catch { } + } + + public static bool CreateAutoProfiles(string m_Profile) + { + bool Saved = true; + + try + { + XmlNode Node; + XmlDocument doc = new XmlDocument(); + + Node = doc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + doc.AppendChild(Node); + + Node = doc.CreateComment(string.Format(" Auto-Profile Configuration Data. {0} ", DateTime.Now)); + doc.AppendChild(Node); + + Node = doc.CreateWhitespace("\r\n"); + doc.AppendChild(Node); + + Node = doc.CreateNode(XmlNodeType.Element, "Programs", ""); + doc.AppendChild(Node); + doc.Save(m_Profile); + } + catch { Saved = false; } + + return Saved; + } + public static event EventHandler ControllerStatusChange; // called when a controller is added/removed/battery or touchpad mode changes/etc. public static void ControllerStatusChanged(object sender) { @@ -182,13 +741,58 @@ public static void ControllerStatusChanged(object sender) ControllerStatusChange(sender, EventArgs.Empty); } - //general values + public static event EventHandler BatteryStatusChange; + public static void OnBatteryStatusChange(object sender, int index, int level, bool charging) + { + if (BatteryStatusChange != null) + { + BatteryReportArgs args = new BatteryReportArgs(index, level, charging); + BatteryStatusChange(sender, args); + } + } + + public static event EventHandler ControllerRemoved; + public static void OnControllerRemoved(object sender, int index) + { + if (ControllerRemoved != null) + { + ControllerRemovedArgs args = new ControllerRemovedArgs(index); + ControllerRemoved(sender, args); + } + } + + public static event EventHandler DeviceStatusChange; + public static void OnDeviceStatusChanged(object sender, int index) + { + if (DeviceStatusChange != null) + { + DeviceStatusChangeEventArgs args = new DeviceStatusChangeEventArgs(index); + DeviceStatusChange(sender, args); + } + } + + public static event EventHandler DeviceSerialChange; + public static void OnDeviceSerialChange(object sender, int index, string serial) + { + if (DeviceSerialChange != null) + { + SerialChangeArgs args = new SerialChangeArgs(index, serial); + DeviceSerialChange(sender, args); + } + } + + // general values public static bool UseExclusiveMode { set { m_Config.useExclusiveMode = value; } get { return m_Config.useExclusiveMode; } } + public static bool getUseExclusiveMode() + { + return m_Config.useExclusiveMode; + } + public static DateTime LastChecked { set { m_Config.lastChecked = value; } @@ -200,146 +804,826 @@ public static int CheckWhen set { m_Config.CheckWhen = value; } get { return m_Config.CheckWhen; } } + public static int Notifications { set { m_Config.notifications = value; } get { return m_Config.notifications; } } + public static bool DCBTatStop { set { m_Config.disconnectBTAtStop = value; } get { return m_Config.disconnectBTAtStop; } } + public static bool SwipeProfiles { set { m_Config.swipeProfiles = value; } get { return m_Config.swipeProfiles; } } + public static bool DS4Mapping { set { m_Config.ds4Mapping = value; } get { return m_Config.ds4Mapping; } } + public static bool QuickCharge { set { m_Config.quickCharge = value; } get { return m_Config.quickCharge; } } - public static int FirstXinputPort + + public static bool getQuickCharge() { - set { m_Config.firstXinputPort = value; } - get { return m_Config.firstXinputPort; } + return m_Config.quickCharge; } + public static bool CloseMini { set { m_Config.closeMini = value; } get { return m_Config.closeMini; } } + public static bool StartMinimized { set { m_Config.startMinimized = value; } get { return m_Config.startMinimized; } } + + public static bool MinToTaskbar + { + set { m_Config.minToTaskbar = value; } + get { return m_Config.minToTaskbar; } + } + + public static bool GetMinToTaskbar() + { + return m_Config.minToTaskbar; + } + public static int FormWidth { set { m_Config.formWidth = value; } - get { return m_Config.formWidth;} + get { return m_Config.formWidth; } } + public static int FormHeight { set { m_Config.formHeight = value; } get { return m_Config.formHeight; } } + + public static int FormLocationX + { + set { m_Config.formLocationX = value; } + get { return m_Config.formLocationX; } + } + + public static int FormLocationY + { + set { m_Config.formLocationY = value; } + get { return m_Config.formLocationY; } + } + + public static string UseLang + { + set { m_Config.useLang = value; } + get { return m_Config.useLang; } + } + public static bool DownloadLang { set { m_Config.downloadLang = value; } get { return m_Config.downloadLang; } } + public static bool FlashWhenLate { set { m_Config.flashWhenLate = value; } get { return m_Config.flashWhenLate; } } + + public static bool getFlashWhenLate() + { + return m_Config.flashWhenLate; + } + public static int FlashWhenLateAt { set { m_Config.flashWhenLateAt = value; } get { return m_Config.flashWhenLateAt; } } - public static bool UseWhiteIcon + + public static int getFlashWhenLateAt() + { + return m_Config.flashWhenLateAt; + } + + public static bool isUsingUDPServer() + { + return m_Config.useUDPServ; + } + public static void setUsingUDPServer(bool state) + { + m_Config.useUDPServ = state; + } + + public static int getUDPServerPortNum() + { + return m_Config.udpServPort; + } + public static void setUDPServerPort(int value) + { + m_Config.udpServPort = value; + } + + public static string getUDPServerListenAddress() + { + return m_Config.udpServListenAddress; + } + public static void setUDPServerListenAddress(string value) + { + m_Config.udpServListenAddress = value.Trim(); + } + + public static bool UseWhiteIcon { set { m_Config.useWhiteIcon = value; } get { return m_Config.useWhiteIcon; } } - //controller/profile specfic values - public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; - public static byte[] RumbleBoost => m_Config.rumble; - public static double[] Rainbow => m_Config.rainbow; - public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; - public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; - public static byte[] TouchSensitivity => m_Config.touchSensitivity; - public static byte[] FlashType => m_Config.flashType; - public static int[] FlashAt => m_Config.flashAt; - public static bool[] LedAsBatteryIndicator => m_Config.ledAsBattery; - public static int[] ChargingType => m_Config.chargingType; - public static bool[] DinputOnly => m_Config.dinputOnly; - public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; - public static bool[] UseTPforControls => m_Config.useTPforControls; - public static bool[] UseSAforMouse => m_Config.useSAforMouse; - public static string[] SATriggers => m_Config.sATriggers; - public static int[] GyroSensitivity => m_Config.gyroSensitivity; - public static int[] GyroInvert => m_Config.gyroInvert; - public static DS4Color[] MainColor => m_Config.m_Leds; - public static DS4Color[] LowColor => m_Config.m_LowLeds; - public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; - public static DS4Color[] CustomColor => m_Config.m_CustomLeds; - public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool UseCustomSteamFolder + { + set { m_Config.useCustomSteamFolder = value; } + get { return m_Config.useCustomSteamFolder; } + } + + public static string CustomSteamFolder + { + set { m_Config.customSteamFolder = value; } + get { return m_Config.customSteamFolder; } + } + + public static bool AutoProfileRevertDefaultProfile + { + set { m_Config.autoProfileRevertDefaultProfile = value; } + get { return m_Config.autoProfileRevertDefaultProfile; } + } + + // controller/profile specfic values + public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; + + public static byte[] RumbleBoost => m_Config.rumble; + public static byte getRumbleBoost(int index) + { + return m_Config.rumble[index]; + } + + public static void setRumbleAutostopTime(int index, int value) + { + m_Config.rumbleAutostopTime[index] = value; + + DS4Device tempDev = Program.rootHub.DS4Controllers[index]; + if (tempDev != null && tempDev.isSynced()) + tempDev.RumbleAutostopTime = value; + } + + public static int getRumbleAutostopTime(int index) + { + return m_Config.rumbleAutostopTime[index]; + } + + public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; + public static bool getFlushHIDQueue(int index) + { + return m_Config.flushHIDQueue[index]; + } + + public static bool[] EnableTouchToggle => m_Config.enableTouchToggle; + public static bool getEnableTouchToggle(int index) + { + return m_Config.enableTouchToggle[index]; + } + + public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; + public static int getIdleDisconnectTimeout(int index) + { + return m_Config.idleDisconnectTimeout[index]; + } + + public static byte[] TouchSensitivity => m_Config.touchSensitivity; + public static byte[] getTouchSensitivity() + { + return m_Config.touchSensitivity; + } + + public static byte getTouchSensitivity(int index) + { + return m_Config.touchSensitivity[index]; + } + + public static bool[] TouchActive => touchpadActive; + public static bool GetTouchActive(int index) + { + return touchpadActive[index]; + } + + public static LightbarSettingInfo[] LightbarSettingsInfo => m_Config.lightbarSettingInfo; + public static LightbarSettingInfo getLightbarSettingsInfo(int index) + { + return m_Config.lightbarSettingInfo[index]; + } + + public static bool[] DinputOnly => m_Config.dinputOnly; + public static bool getDInputOnly(int index) + { + return m_Config.dinputOnly[index]; + } + + public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; + + public static bool[] UseTPforControls => m_Config.useTPforControls; + public static bool getUseTPforControls(int index) + { + return m_Config.useTPforControls[index]; + } + + public static bool[] UseSAforMouse => m_Config.useSAforMouse; + public static bool isUsingSAforMouse(int index) + { + return m_Config.gyroOutMode[index] == DS4Windows.GyroOutMode.Mouse; + } + + public static string[] SATriggers => m_Config.sATriggers; + public static string getSATriggers(int index) + { + return m_Config.sATriggers[index]; + } + + public static bool[] SATriggerCond => m_Config.sATriggerCond; + public static bool getSATriggerCond(int index) + { + return m_Config.sATriggerCond[index]; + } + public static void SetSaTriggerCond(int index, string text) + { + m_Config.SetSaTriggerCond(index, text); + } + + + public static GyroOutMode[] GyroOutputMode => m_Config.gyroOutMode; + public static GyroOutMode GetGyroOutMode(int device) + { + return m_Config.gyroOutMode[device]; + } + + public static string[] SAMousestickTriggers => m_Config.sAMouseStickTriggers; + public static string GetSAMouseStickTriggers(int device) + { + return m_Config.sAMouseStickTriggers[device]; + } + + public static bool[] SAMouseStickTriggerCond => m_Config.sAMouseStickTriggerCond; + public static bool GetSAMouseStickTriggerCond(int device) + { + return m_Config.sAMouseStickTriggerCond[device]; + } + public static void SetSaMouseStickTriggerCond(int index, string text) + { + m_Config.SetSaMouseStickTriggerCond(index, text); + } + + public static bool[] GyroMouseStickTriggerTurns = m_Config.gyroMouseStickTriggerTurns; + public static bool GetGyroMouseStickTriggerTurns(int device) + { + return m_Config.gyroMouseStickTriggerTurns[device]; + } + + public static int[] GyroMouseStickHorizontalAxis => + m_Config.gyroMouseStickHorizontalAxis; + public static int getGyroMouseStickHorizontalAxis(int index) + { + return m_Config.gyroMouseStickHorizontalAxis[index]; + } + + public static GyroMouseStickInfo[] GyroMouseStickInf => m_Config.gyroMStickInfo; + public static GyroMouseStickInfo GetGyroMouseStickInfo(int device) + { + return m_Config.gyroMStickInfo[device]; + } + + public static bool[] GyroMouseStickToggle => m_Config.gyroMouseStickToggle; + public static void SetGyroMouseStickToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseStickToggle(index, value, control); + + public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis; + public static SASteeringWheelEmulationAxisType GetSASteeringWheelEmulationAxis(int index) + { + return m_Config.sASteeringWheelEmulationAxis[index]; + } + + public static int[] SASteeringWheelEmulationRange => m_Config.sASteeringWheelEmulationRange; + public static int GetSASteeringWheelEmulationRange(int index) + { + return m_Config.sASteeringWheelEmulationRange[index]; + } + + public static int[][] TouchDisInvertTriggers => m_Config.touchDisInvertTriggers; + public static int[] getTouchDisInvertTriggers(int index) + { + return m_Config.touchDisInvertTriggers[index]; + } + + public static int[] GyroSensitivity => m_Config.gyroSensitivity; + public static int getGyroSensitivity(int index) + { + return m_Config.gyroSensitivity[index]; + } + + public static int[] GyroSensVerticalScale => m_Config.gyroSensVerticalScale; + public static int getGyroSensVerticalScale(int index) + { + return m_Config.gyroSensVerticalScale[index]; + } + + public static int[] GyroInvert => m_Config.gyroInvert; + public static int getGyroInvert(int index) + { + return m_Config.gyroInvert[index]; + } + + public static bool[] GyroTriggerTurns => m_Config.gyroTriggerTurns; + public static bool getGyroTriggerTurns(int index) + { + return m_Config.gyroTriggerTurns[index]; + } + + public static bool[] GyroSmoothing => m_Config.gyroSmoothing; + public static bool getGyroSmoothing(int index) + { + return m_Config.gyroSmoothing[index]; + } + + public static double[] GyroSmoothingWeight => m_Config.gyroSmoothWeight; + public static double getGyroSmoothingWeight(int index) + { + return m_Config.gyroSmoothWeight[index]; + } + + public static int[] GyroMouseHorizontalAxis => m_Config.gyroMouseHorizontalAxis; + public static int getGyroMouseHorizontalAxis(int index) + { + return m_Config.gyroMouseHorizontalAxis[index]; + } + + public static int[] GyroMouseDeadZone => m_Config.gyroMouseDZ; + public static int GetGyroMouseDeadZone(int index) + { + return m_Config.gyroMouseDZ[index]; + } + + public static void SetGyroMouseDeadZone(int index, int value, ControlService control) + { + m_Config.SetGyroMouseDZ(index, value, control); + } + + public static bool[] GyroMouseToggle => m_Config.gyroMouseToggle; + public static void SetGyroMouseToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseToggle(index, value, control); + + //public static DS4Color[] MainColor => m_Config.m_Leds; + public static ref DS4Color getMainColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_Led; + //return ref m_Config.m_Leds[index]; + } + + //public static DS4Color[] LowColor => m_Config.m_LowLeds; + public static ref DS4Color getLowColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_LowLed; + //return ref m_Config.m_LowLeds[index]; + } + + //public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; + public static ref DS4Color getChargingColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_ChargingLed; + //return ref m_Config.m_ChargingLeds[index]; + } + + //public static DS4Color[] CustomColor => m_Config.m_CustomLeds; + public static ref DS4Color getCustomColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_CustomLed; + //return ref m_Config.m_CustomLeds[index]; + } + + //public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool getUseCustomLed(int index) + { + return m_Config.lightbarSettingInfo[index].ds4winSettings.useCustomLed; + //return m_Config.useCustomLeds[index]; + } + + //public static DS4Color[] FlashColor => m_Config.m_FlashLeds; + public static ref DS4Color getFlashColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_FlashLed; + //return ref m_Config.m_FlashLeds[index]; + } + + public static byte[] TapSensitivity => m_Config.tapSensitivity; + public static byte getTapSensitivity(int index) + { + return m_Config.tapSensitivity[index]; + } + + public static bool[] DoubleTap => m_Config.doubleTap; + public static bool getDoubleTap(int index) + { + return m_Config.doubleTap[index]; + } + + public static int[] ScrollSensitivity => m_Config.scrollSensitivity; + public static int[] getScrollSensitivity() + { + return m_Config.scrollSensitivity; + } + public static int getScrollSensitivity(int index) + { + return m_Config.scrollSensitivity[index]; + } + + public static bool[] LowerRCOn => m_Config.lowerRCOn; + public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static bool getTouchpadJitterCompensation(int index) + { + return m_Config.touchpadJitterCompensation[index]; + } + + public static int[] TouchpadInvert => m_Config.touchpadInvert; + public static int getTouchpadInvert(int index) + { + return m_Config.touchpadInvert[index]; + } + + public static TriggerDeadZoneZInfo[] L2ModInfo => m_Config.l2ModInfo; + public static TriggerDeadZoneZInfo GetL2ModInfo(int index) + { + return m_Config.l2ModInfo[index]; + } + + //public static byte[] L2Deadzone => m_Config.l2Deadzone; + public static byte getL2Deadzone(int index) + { + return m_Config.l2ModInfo[index].deadZone; + //return m_Config.l2Deadzone[index]; + } + + public static TriggerDeadZoneZInfo[] R2ModInfo => m_Config.r2ModInfo; + public static TriggerDeadZoneZInfo GetR2ModInfo(int index) + { + return m_Config.r2ModInfo[index]; + } + + //public static byte[] R2Deadzone => m_Config.r2Deadzone; + public static byte getR2Deadzone(int index) + { + return m_Config.r2ModInfo[index].deadZone; + //return m_Config.r2Deadzone[index]; + } + + public static double[] SXDeadzone => m_Config.SXDeadzone; + public static double getSXDeadzone(int index) + { + return m_Config.SXDeadzone[index]; + } + + public static double[] SZDeadzone => m_Config.SZDeadzone; + public static double getSZDeadzone(int index) + { + return m_Config.SZDeadzone[index]; + } + + //public static int[] LSDeadzone => m_Config.LSDeadzone; + public static int getLSDeadzone(int index) + { + return m_Config.lsModInfo[index].deadZone; + //return m_Config.LSDeadzone[index]; + } + + //public static int[] RSDeadzone => m_Config.RSDeadzone; + public static int getRSDeadzone(int index) + { + return m_Config.rsModInfo[index].deadZone; + //return m_Config.RSDeadzone[index]; + } + + //public static int[] LSAntiDeadzone => m_Config.LSAntiDeadzone; + public static int getLSAntiDeadzone(int index) + { + return m_Config.lsModInfo[index].antiDeadZone; + //return m_Config.LSAntiDeadzone[index]; + } + + //public static int[] RSAntiDeadzone => m_Config.RSAntiDeadzone; + public static int getRSAntiDeadzone(int index) + { + return m_Config.rsModInfo[index].antiDeadZone; + //return m_Config.RSAntiDeadzone[index]; + } + + public static StickDeadZoneInfo[] LSModInfo => m_Config.lsModInfo; + public static StickDeadZoneInfo GetLSDeadInfo(int index) + { + return m_Config.lsModInfo[index]; + } + + public static StickDeadZoneInfo[] RSModInfo => m_Config.rsModInfo; + public static StickDeadZoneInfo GetRSDeadInfo(int index) + { + return m_Config.rsModInfo[index]; + } + + public static double[] SXAntiDeadzone => m_Config.SXAntiDeadzone; + public static double getSXAntiDeadzone(int index) + { + return m_Config.SXAntiDeadzone[index]; + } + + public static double[] SZAntiDeadzone => m_Config.SZAntiDeadzone; + public static double getSZAntiDeadzone(int index) + { + return m_Config.SZAntiDeadzone[index]; + } + + //public static int[] LSMaxzone => m_Config.LSMaxzone; + public static int getLSMaxzone(int index) + { + return m_Config.lsModInfo[index].maxZone; + //return m_Config.LSMaxzone[index]; + } + + //public static int[] RSMaxzone => m_Config.RSMaxzone; + public static int getRSMaxzone(int index) + { + return m_Config.rsModInfo[index].maxZone; + //return m_Config.RSMaxzone[index]; + } + + public static double[] SXMaxzone => m_Config.SXMaxzone; + public static double getSXMaxzone(int index) + { + return m_Config.SXMaxzone[index]; + } + + public static double[] SZMaxzone => m_Config.SZMaxzone; + public static double getSZMaxzone(int index) + { + return m_Config.SZMaxzone[index]; + } + + //public static int[] L2AntiDeadzone => m_Config.l2AntiDeadzone; + public static int getL2AntiDeadzone(int index) + { + return m_Config.l2ModInfo[index].antiDeadZone; + //return m_Config.l2AntiDeadzone[index]; + } + + //public static int[] R2AntiDeadzone => m_Config.r2AntiDeadzone; + public static int getR2AntiDeadzone(int index) + { + return m_Config.r2ModInfo[index].antiDeadZone; + //return m_Config.r2AntiDeadzone[index]; + } + + //public static int[] L2Maxzone => m_Config.l2Maxzone; + public static int getL2Maxzone(int index) + { + return m_Config.l2ModInfo[index].maxZone; + //return m_Config.l2Maxzone[index]; + } + + //public static int[] R2Maxzone => m_Config.r2Maxzone; + public static int getR2Maxzone(int index) + { + return m_Config.r2ModInfo[index].maxZone; + //return m_Config.r2Maxzone[index]; + } + + public static int[] LSCurve => m_Config.lsCurve; + public static int getLSCurve(int index) + { + return m_Config.lsCurve[index]; + } + + public static int[] RSCurve => m_Config.rsCurve; + public static int getRSCurve(int index) + { + return m_Config.rsCurve[index]; + } + + public static double[] LSRotation => m_Config.LSRotation; + public static double getLSRotation(int index) + { + return m_Config.LSRotation[index]; + } + + public static double[] RSRotation => m_Config.RSRotation; + public static double getRSRotation(int index) + { + return m_Config.RSRotation[index]; + } + + public static double[] L2Sens => m_Config.l2Sens; + public static double getL2Sens(int index) + { + return m_Config.l2Sens[index]; + } + + public static double[] R2Sens => m_Config.r2Sens; + public static double getR2Sens(int index) + { + return m_Config.r2Sens[index]; + } + + public static double[] SXSens => m_Config.SXSens; + public static double getSXSens(int index) + { + return m_Config.SXSens[index]; + } + + public static double[] SZSens => m_Config.SZSens; + public static double getSZSens(int index) + { + return m_Config.SZSens[index]; + } + + public static double[] LSSens => m_Config.LSSens; + public static double getLSSens(int index) + { + return m_Config.LSSens[index]; + } + + public static double[] RSSens => m_Config.RSSens; + public static double getRSSens(int index) + { + return m_Config.RSSens[index]; + } + + public static bool[] MouseAccel => m_Config.mouseAccel; + public static bool getMouseAccel(int device) + { + return m_Config.mouseAccel[device]; + } + + public static int[] BTPollRate => m_Config.btPollRate; + public static int getBTPollRate(int index) + { + return m_Config.btPollRate[index]; + } + + public static SquareStickInfo[] SquStickInfo = m_Config.squStickInfo; + public static SquareStickInfo GetSquareStickInfo(int device) + { + return m_Config.squStickInfo[device]; + } + + public static void setLsOutCurveMode(int index, int value) + { + m_Config.setLsOutCurveMode(index, value); + } + public static int getLsOutCurveMode(int index) + { + return m_Config.getLsOutCurveMode(index); + } + public static BezierCurve[] lsOutBezierCurveObj => m_Config.lsOutBezierCurveObj; + + public static void setRsOutCurveMode(int index, int value) + { + m_Config.setRsOutCurveMode(index, value); + } + public static int getRsOutCurveMode(int index) + { + return m_Config.getRsOutCurveMode(index); + } + public static BezierCurve[] rsOutBezierCurveObj => m_Config.rsOutBezierCurveObj; - public static DS4Color[] FlashColor => m_Config.m_FlashLeds; - public static byte[] TapSensitivity => m_Config.tapSensitivity; - public static bool[] DoubleTap => m_Config.doubleTap; - public static int[] ScrollSensitivity => m_Config.scrollSensitivity; - public static bool[] LowerRCOn => m_Config.lowerRCOn; - public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static void setL2OutCurveMode(int index, int value) + { + m_Config.setL2OutCurveMode(index, value); + } + public static int getL2OutCurveMode(int index) + { + return m_Config.getL2OutCurveMode(index); + } + public static BezierCurve[] l2OutBezierCurveObj => m_Config.l2OutBezierCurveObj; - public static byte[] L2Deadzone => m_Config.l2Deadzone; - public static byte[] R2Deadzone => m_Config.r2Deadzone; - public static double[] SXDeadzone => m_Config.SXDeadzone; - public static double[] SZDeadzone => m_Config.SZDeadzone; - public static int[] LSDeadzone => m_Config.LSDeadzone; - public static int[] RSDeadzone => m_Config.RSDeadzone; - public static int[] LSCurve => m_Config.lsCurve; - public static int[] RSCurve => m_Config.rsCurve; - public static double[] L2Sens => m_Config.l2Sens; - public static double[] R2Sens => m_Config.r2Sens; - public static double[] SXSens => m_Config.SXSens; - public static double[] SZSens => m_Config.SZSens; - public static double[] LSSens => m_Config.LSSens; - public static double[] RSSens => m_Config.RSSens; - public static bool[] MouseAccel => m_Config.mouseAccel; + public static void setR2OutCurveMode(int index, int value) + { + m_Config.setR2OutCurveMode(index, value); + } + public static int getR2OutCurveMode(int index) + { + return m_Config.getR2OutCurveMode(index); + } + public static BezierCurve[] r2OutBezierCurveObj => m_Config.r2OutBezierCurveObj; + + public static void setSXOutCurveMode(int index, int value) + { + m_Config.setSXOutCurveMode(index, value); + } + public static int getSXOutCurveMode(int index) + { + return m_Config.getSXOutCurveMode(index); + } + public static BezierCurve[] sxOutBezierCurveObj => m_Config.sxOutBezierCurveObj; + + public static void setSZOutCurveMode(int index, int value) + { + m_Config.setSZOutCurveMode(index, value); + } + public static int getSZOutCurveMode(int index) + { + return m_Config.getSZOutCurveMode(index); + } + public static BezierCurve[] szOutBezierCurveObj => m_Config.szOutBezierCurveObj; + + public static bool[] TrackballMode => m_Config.trackballMode; + public static bool getTrackballMode(int index) + { + return m_Config.trackballMode[index]; + } + + public static double[] TrackballFriction => m_Config.trackballFriction; + public static double getTrackballFriction(int index) + { + return m_Config.trackballFriction[index]; + } + + public static OutContType[] OutContType => m_Config.outputDevType; public static string[] LaunchProgram => m_Config.launchProgram; public static string[] ProfilePath => m_Config.profilePath; + public static string[] OlderProfilePath => m_Config.olderProfilePath; + public static bool[] DistanceProfiles = m_Config.distanceProfiles; + public static List[] ProfileActions => m_Config.profileActions; + public static int getProfileActionCount(int index) + { + return m_Config.profileActionCount[index]; + } + + public static void calculateProfileActionCount(int index) + { + m_Config.profileActionCount[index] = m_Config.profileActions[index].Count; + } + + public static List getProfileActions(int index) + { + return m_Config.profileActions[index]; + } public static void UpdateDS4CSetting (int deviceNum, string buttonName, bool shift, object action, string exts, DS4KeyType kt, int trigger = 0) { m_Config.UpdateDS4CSetting(deviceNum, buttonName, shift, action, exts, kt, trigger); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } + public static void UpdateDS4Extra(int deviceNum, string buttonName, bool shift, string exts) { m_Config.UpdateDS4CExtra(deviceNum, buttonName, shift, exts); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } - public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, DS4Controls control, bool shift) => m_Config.GetDS4Action(deviceNum, control, shift); public static DS4KeyType GetDS4KeyType(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4KeyType(deviceNum, buttonName, shift); public static string GetDS4Extra(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Extra(deviceNum, buttonName, shift); public static int GetDS4STrigger(int deviceNum, string buttonName) => m_Config.GetDS4STrigger(deviceNum, buttonName); + public static int GetDS4STrigger(int deviceNum, DS4Controls control) => m_Config.GetDS4STrigger(deviceNum, control); public static List getDS4CSettings(int device) => m_Config.ds4settings[device]; public static DS4ControlSettings getDS4CSetting(int deviceNum, string control) => m_Config.getDS4CSetting(deviceNum, control); - public static bool HasCustomAction(int deviceNum) => m_Config.HasCustomActions(deviceNum); + public static DS4ControlSettings getDS4CSetting(int deviceNum, DS4Controls control) => m_Config.getDS4CSetting(deviceNum, control); + public static bool HasCustomActions(int deviceNum) => m_Config.HasCustomActions(deviceNum); public static bool HasCustomExtras(int deviceNum) => m_Config.HasCustomExtras(deviceNum); - public static void SaveAction(string name, string controls, int mode, string details, bool edit, string extras = "") + public static bool containsCustomAction(int deviceNum) + { + return m_Config.containsCustomAction[deviceNum]; + } + + public static bool containsCustomExtras(int deviceNum) + { + return m_Config.containsCustomExtras[deviceNum]; + } + + public static void SaveAction(string name, string controls, int mode, + string details, bool edit, string extras = "") { m_Config.SaveAction(name, controls, mode, details, edit, extras); Mapping.actionDone.Add(new Mapping.ActionState()); @@ -356,66 +1640,155 @@ public static void RemoveAction(string name) public static int GetActionIndexOf(string name) { - for (int i = 0; i < m_Config.actions.Count; i++) + for (int i = 0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { if (m_Config.actions[i].name == name) return i; + } + return -1; } + public static int GetProfileActionIndexOf(int device, string name) + { + int index = -1; + m_Config.profileActionIndexDict[device].TryGetValue(name, out index); + return index; + } + public static SpecialAction GetAction(string name) { - foreach (SpecialAction sA in m_Config.actions) + //foreach (SpecialAction sA in m_Config.actions) + for (int i=0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { + SpecialAction sA = m_Config.actions[i]; if (sA.name == name) return sA; + } + return new SpecialAction("null", "null", "null", "null"); } + public static SpecialAction GetProfileAction(int device, string name) + { + SpecialAction sA = null; + m_Config.profileActionDict[device].TryGetValue(name, out sA); + return sA; + } + + public static void calculateProfileActionDicts(int device) + { + m_Config.profileActionDict[device].Clear(); + m_Config.profileActionIndexDict[device].Clear(); + + foreach (string actionname in m_Config.profileActions[device]) + { + m_Config.profileActionDict[device][actionname] = GetAction(actionname); + m_Config.profileActionIndexDict[device][actionname] = GetActionIndexOf(actionname); + } + } + + public static void cacheProfileCustomsFlags(int device) + { + bool customAct = false; + m_Config.containsCustomAction[device] = customAct = HasCustomActions(device); + m_Config.containsCustomExtras[device] = HasCustomExtras(device); + + if (!customAct) + { + customAct = m_Config.gyroOutMode[device] == GyroOutMode.MouseJoystick; + customAct = customAct || m_Config.sASteeringWheelEmulationAxis[device] >= SASteeringWheelEmulationAxisType.VJoy1X; + m_Config.containsCustomAction[device] = customAct; + } + } + + public static void CacheExtraProfileInfo(int device) + { + calculateProfileActionCount(device); + calculateProfileActionDicts(device); + cacheProfileCustomsFlags(device); + } + + public static X360Controls getX360ControlsByName(string key) + { + return m_Config.getX360ControlsByName(key); + } + + public static string getX360ControlString(X360Controls key) + { + return m_Config.getX360ControlString(key); + } + + public static DS4Controls getDS4ControlsByName(string key) + { + return m_Config.getDS4ControlsByName(key); + } + + public static X360Controls getDefaultX360ControlBinding(DS4Controls dc) + { + return defaultButtonMapping[(int)dc]; + } + + public static bool containsLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + return m_Config.linkedProfiles.ContainsKey(tempSerial); + } + + public static string getLinkedProfile(string serial) + { + string temp = string.Empty; + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + temp = m_Config.linkedProfiles[tempSerial]; + } + + return temp; + } + + public static void changeLinkedProfile(string serial, string profile) + { + string tempSerial = serial.Replace(":", string.Empty); + m_Config.linkedProfiles[tempSerial] = profile; + } + + public static void removeLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + m_Config.linkedProfiles.Remove(tempSerial); + } + } - /*public static X360Controls getCustomButton(int device, DS4Controls controlName) => m_Config.GetCustomButton(device, controlName); - - public static ushort getCustomKey(int device, DS4Controls controlName) => m_Config.GetCustomKey(device, controlName); - - public static string getCustomMacro(int device, DS4Controls controlName) => m_Config.GetCustomMacro(device, controlName); - - public static string getCustomExtras(int device, DS4Controls controlName) => m_Config.GetCustomExtras(device, controlName); - - public static DS4KeyType getCustomKeyType(int device, DS4Controls controlName) => m_Config.GetCustomKeyType(device, controlName); - - public static bool getHasCustomKeysorButtons(int device) => m_Config.customMapButtons[device].Count > 0 - || m_Config.customMapKeys[device].Count > 0; - - public static bool getHasCustomExtras(int device) => m_Config.customMapExtras[device].Count > 0; - public static Dictionary getCustomButtons(int device) => m_Config.customMapButtons[device]; - public static Dictionary getCustomKeys(int device) => m_Config.customMapKeys[device]; - public static Dictionary getCustomMacros(int device) => m_Config.customMapMacros[device]; - public static Dictionary getCustomExtras(int device) => m_Config.customMapExtras[device]; - public static Dictionary getCustomKeyTypes(int device) => m_Config.customMapKeyTypes[device]; - - public static X360Controls getShiftCustomButton(int device, DS4Controls controlName) => m_Config.GetShiftCustomButton(device, controlName); - public static ushort getShiftCustomKey(int device, DS4Controls controlName) => m_Config.GetShiftCustomKey(device, controlName); - public static string getShiftCustomMacro(int device, DS4Controls controlName) => m_Config.GetShiftCustomMacro(device, controlName); - public static string getShiftCustomExtras(int device, DS4Controls controlName) => m_Config.GetShiftCustomExtras(device, controlName); - public static DS4KeyType getShiftCustomKeyType(int device, DS4Controls controlName) => m_Config.GetShiftCustomKeyType(device, controlName); - public static bool getHasShiftCustomKeysorButtons(int device) => m_Config.shiftCustomMapButtons[device].Count > 0 - || m_Config.shiftCustomMapKeys[device].Count > 0; - public static bool getHasShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device].Count > 0; - public static Dictionary getShiftCustomButtons(int device) => m_Config.shiftCustomMapButtons[device]; - public static Dictionary getShiftCustomKeys(int device) => m_Config.shiftCustomMapKeys[device]; - public static Dictionary getShiftCustomMacros(int device) => m_Config.shiftCustomMapMacros[device]; - public static Dictionary getShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device]; - public static Dictionary getShiftCustomKeyTypes(int device) => m_Config.shiftCustomMapKeyTypes[device]; */ public static bool Load() => m_Config.Load(); - public static void LoadProfile(int device, bool launchprogram, ControlService control) + public static void LoadProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) { - m_Config.LoadProfile(device, launchprogram, control); + m_Config.LoadProfile(device, launchprogram, control, "", xinputChange, postLoad); tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } - public static void LoadTempProfile(int device, string name, bool launchprogram, ControlService control) + public static void LoadTempProfile(int device, string name, bool launchprogram, + ControlService control, bool xinputChange = true) { m_Config.LoadProfile(device, launchprogram, control, appdatapath + @"\Profiles\" + name + ".xml"); tempprofilename[device] = name; + useTempProfile[device] = true; + tempprofileDistance[device] = name.ToLower().Contains("distance"); + } + + public static void LoadBlankDevProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) + { + m_Config.LoadBlankProfile(device, launchprogram, control, "", xinputChange, postLoad); + tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } public static bool Save() @@ -428,22 +1801,66 @@ public static void SaveProfile(int device, string propath) m_Config.SaveProfile(device, propath); } + public static void SaveAsNewProfile(int device, string propath) + { + m_Config.SaveAsNewProfile(device, propath); + } + + public static bool SaveLinkedProfiles() + { + return m_Config.SaveLinkedProfiles(); + } + + public static bool LoadLinkedProfiles() + { + return m_Config.LoadLinkedProfiles(); + } + + public static bool SaveControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.SaveControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.SaveControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + + public static bool LoadControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.LoadControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.LoadControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + private static byte applyRatio(byte b1, byte b2, double r) { - if (r > 100) - r = 100; - else if (r < 0) - r = 0; - r /= 100f; - return (byte)Math.Round((b1 * (1 - r) + b2 *r),0); + if (r > 100.0) + r = 100.0; + else if (r < 0.0) + r = 0.0; + + r *= 0.01; + return (byte)Math.Round((b1 * (1 - r)) + b2 * r, 0); } - public static DS4Color getTransitionedColor(DS4Color c1, DS4Color c2, double ratio) - {//; + + public static DS4Color getTransitionedColor(ref DS4Color c1, ref DS4Color c2, double ratio) + { //Color cs = Color.FromArgb(c1.red, c1.green, c1.blue); - c1.red = applyRatio(c1.red, c2.red, ratio); - c1.green = applyRatio(c1.green, c2.green, ratio); - c1.blue = applyRatio(c1.blue, c2.blue, ratio); - return c1; + DS4Color cs = new DS4Color + { + red = applyRatio(c1.red, c2.red, ratio), + green = applyRatio(c1.green, c2.green, ratio), + blue = applyRatio(c1.blue, c2.blue, ratio) + }; + return cs; } private static Color applyRatio(Color c1, Color c2, uint r) @@ -463,6 +1880,7 @@ private static Color applyRatio(Color c1, Color c2, uint r) csR = HuetoRGB(hue2,sat2,bri2 - br*ratio); else csR = HuetoRGB(hue2 - hr * ratio, sat2 - sr * ratio, bri2 - br * ratio); + return csR; } @@ -473,234 +1891,546 @@ public static Color HuetoRGB(float hue, float sat, float bri) float m = bri - C / 2; float R, G, B; if (0 <= hue && hue < 60) - { R = C; G = X; B = 0;} + { + R = C; G = X; B = 0; + } else if (60 <= hue && hue < 120) - {R = X; G = C; B = 0; } + { + R = X; G = C; B = 0; + } else if (120 <= hue && hue < 180) - { R = 0; G = C; B = X; } + { + R = 0; G = C; B = X; + } else if (180 <= hue && hue < 240) - { R = 0; G = X; B = C; } + { + R = 0; G = X; B = C; + } else if (240 <= hue && hue < 300) - { R = X; G = 0; B = C; } + { + R = X; G = 0; B = C; + } else if (300 <= hue && hue < 360) - { R = C; G = 0; B = X; } + { + R = C; G = 0; B = X; + } else - { R = 255; G = 0; B = 0; } + { + R = 255; G = 0; B = 0; + } + R += m; G += m; B += m; - R *= 255; G *= 255; B *= 255; + R *= 255.0f; G *= 255.0f; B *= 255.0f; return Color.FromArgb((int)R, (int)G, (int)B); } - } - + public static double Clamp(double min, double value, double max) + { + return (value < min) ? min : (value > max) ? max : value; + } + private static int ClampInt(int min, int value, int max) + { + return (value < min) ? min : (value > max) ? max : value; + } + } public class BackingStore { //public String m_Profile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Tool" + "\\Profiles.xml"; public String m_Profile = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName + "\\Profiles.xml"; public String m_Actions = Global.appdatapath + "\\Actions.xml"; + public string m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + public string m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; protected XmlDocument m_Xdoc = new XmlDocument(); - //fifth value used to for options, not fifth controller - public int[] buttonMouseSensitivity = { 25, 25, 25, 25, 25 }; - - public bool[] flushHIDQueue = { true, true, true, true, true }; - public int[] idleDisconnectTimeout = { 0, 0, 0, 0, 0 }; - public Boolean[] touchpadJitterCompensation = { true, true, true, true, true }; - public Boolean[] lowerRCOn = { false, false, false, false, false }; - public Boolean[] ledAsBattery = { false, false, false, false, false }; - public Byte[] flashType = { 0, 0, 0, 0, 0 }; - public String[] profilePath = { String.Empty, String.Empty, String.Empty, String.Empty, String.Empty }; - public Byte[] rumble = { 100, 100, 100, 100, 100 }; - public Byte[] touchSensitivity = { 100, 100, 100, 100, 100 }; - public Byte[] l2Deadzone = { 0, 0, 0, 0, 0 }, r2Deadzone = { 0, 0, 0, 0, 0 }; - public int[] LSDeadzone = { 0, 0, 0, 0, 0 }, RSDeadzone = { 0, 0, 0, 0, 0 }; - public double[] SXDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }; - public double[] l2Sens = { 1, 1, 1, 1, 1 }, r2Sens = { 1, 1, 1, 1, 1 }; - public double[] LSSens = { 1, 1, 1, 1, 1 }, RSSens = { 1, 1, 1, 1, 1 }; - public double[] SXSens = { 1, 1, 1, 1, 1 }, SZSens = { 1, 1, 1, 1, 1 }; - public Byte[] tapSensitivity = { 0, 0, 0, 0, 0 }; - public bool[] doubleTap = { false, false, false, false, false }; - public int[] scrollSensitivity = { 0, 0, 0, 0, 0 }; - public double[] rainbow = { 0, 0, 0, 0, 0 }; - public int[] flashAt = { 0, 0, 0, 0, 0 }; - public bool[] mouseAccel = { true, true, true, true, true }; - public DS4Color[] m_LowLeds = new DS4Color[] - { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + // fifth value used for options, not fifth controller + public int[] buttonMouseSensitivity = new int[5] { 25, 25, 25, 25, 25 }; + + public bool[] flushHIDQueue = new bool[5] { false, false, false, false, false }; + public bool[] enableTouchToggle = new bool[5] { true, true, true, true, true }; + public int[] idleDisconnectTimeout = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] touchpadJitterCompensation = new bool[5] { true, true, true, true, true }; + public bool[] lowerRCOn = new bool[5] { false, false, false, false, false }; + public string[] profilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public string[] olderProfilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public Dictionary linkedProfiles = new Dictionary(); + // Cache properties instead of performing a string comparison every frame + public bool[] distanceProfiles = new bool[5] { false, false, false, false, false }; + public Byte[] rumble = new Byte[5] { 100, 100, 100, 100, 100 }; + public int[] rumbleAutostopTime = new int[5] { 0, 0, 0, 0, 0 }; // Value in milliseconds (0=autustop timer disabled) + public Byte[] touchSensitivity = new Byte[5] { 100, 100, 100, 100, 100 }; + public StickDeadZoneInfo[] lsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public StickDeadZoneInfo[] rsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public TriggerDeadZoneZInfo[] l2ModInfo = new TriggerDeadZoneZInfo[5] + { + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_Leds = new DS4Color[] + public TriggerDeadZoneZInfo[] r2ModInfo = new TriggerDeadZoneZInfo[5] { - new DS4Color(Color.Blue), - new DS4Color(Color.Red), - new DS4Color(Color.Green), - new DS4Color(Color.Pink), - new DS4Color(Color.White) + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_ChargingLeds = new DS4Color[] + + public double[] LSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, RSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] SXDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }; + public double[] SXMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, + SZMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, + SZAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] l2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, r2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] LSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, RSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, SZSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public Byte[] tapSensitivity = new Byte[5] { 0, 0, 0, 0, 0 }; + public bool[] doubleTap = new bool[5] { false, false, false, false, false }; + public int[] scrollSensitivity = new int[5] { 0, 0, 0, 0, 0 }; + public int[] touchpadInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] mouseAccel = new bool[5] { false, false, false, false, false }; + public int[] btPollRate = new int[5] { 4, 4, 4, 4, 4 }; + public int[] gyroMouseDZ = new int[5] { MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE }; + public bool[] gyroMouseToggle = new bool[5] { false, false, false, + false, false }; + + public SquareStickInfo[] squStickInfo = new SquareStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), }; - public DS4Color[] m_FlashLeds = new DS4Color[] + + private void setOutBezierCurveObjArrayItem(BezierCurve[] bezierCurveArray, int device, int curveOptionValue, BezierCurve.AxisType axisType) + { + // Set bezier curve obj of axis. 0=Linear (no curve mapping), 1-5=Pre-defined curves, 6=User supplied custom curve string value of a profile (comma separated list of 4 decimal numbers) + switch (curveOptionValue) + { + // Commented out case 1..5 because Mapping.cs:SetCurveAndDeadzone function has the original IF-THEN-ELSE code logic for those original 1..5 output curve mappings (ie. no need to initialize the lookup result table). + // Only the new bezier custom curve option 6 uses the lookup result table (initialized in BezierCurve class based on an input curve definition). + //case 1: bezierCurveArray[device].InitBezierCurve(99.0, 91.0, 0.00, 0.00, axisType); break; // Enhanced Precision (hard-coded curve) (almost the same curve as bezier 0.70, 0.28, 1.00, 1.00) + //case 2: bezierCurveArray[device].InitBezierCurve(99.0, 92.0, 0.00, 0.00, axisType); break; // Quadric + //case 3: bezierCurveArray[device].InitBezierCurve(99.0, 93.0, 0.00, 0.00, axisType); break; // Cubic + //case 4: bezierCurveArray[device].InitBezierCurve(99.0, 94.0, 0.00, 0.00, axisType); break; // Easeout Quad + //case 5: bezierCurveArray[device].InitBezierCurve(99.0, 95.0, 0.00, 0.00, axisType); break; // Easeout Cubic + case 6: bezierCurveArray[device].InitBezierCurve(bezierCurveArray[device].CustomDefinition, axisType); break; // Custom output curve + } + } + + public BezierCurve[] lsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] rsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] l2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] r2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] sxOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] szOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + + private int[] _lsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getLsOutCurveMode(int index) { return _lsOutCurveMode[index]; } + public void setLsOutCurveMode(int index, int value) { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + if (value >= 1) setOutBezierCurveObjArrayItem(lsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _lsOutCurveMode[index] = value; + } + + private int[] _rsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getRsOutCurveMode(int index) { return _rsOutCurveMode[index]; } + public void setRsOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(rsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _rsOutCurveMode[index] = value; + } + + private int[] _l2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getL2OutCurveMode(int index) { return _l2OutCurveMode[index]; } + public void setL2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(l2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _l2OutCurveMode[index] = value; + } + + private int[] _r2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getR2OutCurveMode(int index) { return _r2OutCurveMode[index]; } + public void setR2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(r2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _r2OutCurveMode[index] = value; + } + + private int[] _sxOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSXOutCurveMode(int index) { return _sxOutCurveMode[index]; } + public void setSXOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(sxOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _sxOutCurveMode[index] = value; + } + + private int[] _szOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSZOutCurveMode(int index) { return _szOutCurveMode[index]; } + public void setSZOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(szOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _szOutCurveMode[index] = value; + } + + public LightbarSettingInfo[] lightbarSettingInfo = new LightbarSettingInfo[5] + { + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo() }; - public bool[] useCustomLeds = new bool[] { false, false, false, false }; - public DS4Color[] m_CustomLeds = new DS4Color[] + + public string[] launchProgram = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public bool[] dinputOnly = new bool[5] { false, false, false, false, false }; + public bool[] startTouchpadOff = new bool[5] { false, false, false, false, false }; + public bool[] useTPforControls = new bool[5] { false, false, false, false, false }; + public bool[] useSAforMouse = new bool[5] { false, false, false, false, false }; + public GyroOutMode[] gyroOutMode = new GyroOutMode[5] { GyroOutMode.Controls, GyroOutMode.Controls, + GyroOutMode.Controls, GyroOutMode.Controls, GyroOutMode.Controls }; + public string[] sATriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public string[] sAMouseStickTriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public bool[] sATriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] sAMouseStickTriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] gyroMouseStickTriggerTurns = new bool[5] { true, true, true, true, true }; + public GyroMouseStickInfo[] gyroMStickInfo = new GyroMouseStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), new GyroMouseStickInfo(), + new GyroMouseStickInfo() }; - public int[] chargingType = { 0, 0, 0, 0, 0 }; - public string[] launchProgram = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; - public bool[] dinputOnly = { false, false, false, false, false }; - public bool[] startTouchpadOff = { false, false, false, false, false }; - public bool[] useTPforControls = { false, false, false, false, false }; - public bool[] useSAforMouse = { false, false, false, false, false }; - public string[] sATriggers = { "", "", "", "", "" }; - public int[] lsCurve = { 0, 0, 0, 0, 0 }; - public int[] rsCurve = { 0, 0, 0, 0, 0 }; + + public bool[] gyroMouseStickToggle = new bool[5] { false, false, false, + false, false }; + + public SASteeringWheelEmulationAxisType[] sASteeringWheelEmulationAxis = new SASteeringWheelEmulationAxisType[5] { SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None }; + public int[] sASteeringWheelEmulationRange = new int[5] { 360, 360, 360, 360, 360 }; + public int[][] touchDisInvertTriggers = new int[5][] { new int[1] { -1 }, new int[1] { -1 }, new int[1] { -1 }, + new int[1] { -1 }, new int[1] { -1 } }; + public int[] lsCurve = new int[5] { 0, 0, 0, 0, 0 }; + public int[] rsCurve = new int[5] { 0, 0, 0, 0, 0 }; public Boolean useExclusiveMode = false; public Int32 formWidth = 782; public Int32 formHeight = 550; + public int formLocationX = 0; + public int formLocationY = 0; public Boolean startMinimized = false; + public Boolean minToTaskbar = false; public DateTime lastChecked; public int CheckWhen = 1; public int notifications = 2; public bool disconnectBTAtStop = false; public bool swipeProfiles = true; - public bool ds4Mapping = true; + public bool ds4Mapping = false; public bool quickCharge = false; - public int firstXinputPort = 1; public bool closeMini = false; public List actions = new List(); - public List[] ds4settings = { new List(), new List(), new List(), new List(), new List() }; - /*public Dictionary[] customMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] customMapKeys = { null, null, null, null, null }; - public Dictionary[] customMapMacros = { null, null, null, null, null }; - public Dictionary[] customMapButtons = { null, null, null, null, null }; - public Dictionary[] customMapExtras = { null, null, null, null, null }; - - public Dictionary[] shiftCustomMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapKeys = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapMacros = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapButtons = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapExtras = { null, null, null, null, null };*/ - public List[] profileActions = { null, null, null, null, null }; + public List[] ds4settings = new List[5] + { new List(), new List(), new List(), + new List(), new List() }; + + public List[] profileActions = new List[5] { null, null, null, null, null }; + public int[] profileActionCount = new int[5] { 0, 0, 0, 0, 0 }; + public Dictionary[] profileActionDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public Dictionary[] profileActionIndexDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public string useLang = ""; public bool downloadLang = true; public bool useWhiteIcon; public bool flashWhenLate = true; - public int flashWhenLateAt = 10; - public int[] gyroSensitivity = { 100, 100, 100, 100, 100 }; - public int[] gyroInvert = { 0, 0, 0, 0, 0 }; + public int flashWhenLateAt = 20; + public bool useUDPServ = false; + public int udpServPort = 26760; + public string udpServListenAddress = "127.0.0.1"; // 127.0.0.1=IPAddress.Loopback (default), 0.0.0.0=IPAddress.Any as all interfaces, x.x.x.x = Specific ipv4 interface address or hostname + public bool useCustomSteamFolder; + public string customSteamFolder; + // Cache whether profile has custom action + public bool[] containsCustomAction = new bool[5] { false, false, false, false, false }; + + // Cache whether profile has custom extras + public bool[] containsCustomExtras = new bool[5] { false, false, false, false, false }; + + public int[] gyroSensitivity = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroSensVerticalScale = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] gyroTriggerTurns = new bool[5] { true, true, true, true, true }; + public bool[] gyroSmoothing = new bool[5] { false, false, false, false, false }; + public double[] gyroSmoothWeight = new double[5] { 0.5, 0.5, 0.5, 0.5, 0.5 }; + public int[] gyroMouseHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public int[] gyroMouseStickHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public bool[] trackballMode = new bool[5] { false, false, false, false, false }; + public double[] trackballFriction = new double[5] { 10.0, 10.0, 10.0, 10.0, 10.0 }; + // Used to hold the controller type desired in a profile + public OutContType[] outputDevType = new OutContType[5] { OutContType.X360, + OutContType.X360, OutContType.X360, + OutContType.X360, OutContType.X360 }; + + // TRUE=AutoProfile reverts to default profile if current foreground process is unknown, FALSE=Leave existing profile active when a foreground proces is unknown (ie. no matching auto-profile rule) + public bool autoProfileRevertDefaultProfile = true; + + bool tempBool = false; public BackingStore() { for (int i = 0; i < 5; i++) { foreach (DS4Controls dc in Enum.GetValues(typeof(DS4Controls))) + { if (dc != DS4Controls.None) ds4settings[i].Add(new DS4ControlSettings(dc)); - /*customMapKeyTypes[i] = new Dictionary(); - customMapKeys[i] = new Dictionary(); - customMapMacros[i] = new Dictionary(); - customMapButtons[i] = new Dictionary(); - customMapExtras[i] = new Dictionary(); - - shiftCustomMapKeyTypes[i] = new Dictionary(); - shiftCustomMapKeys[i] = new Dictionary(); - shiftCustomMapMacros[i] = new Dictionary(); - shiftCustomMapButtons[i] = new Dictionary(); - shiftCustomMapExtras[i] = new Dictionary();*/ + } + profileActions[i] = new List(); profileActions[i].Add("Disconnect Controller"); + profileActionCount[i] = profileActions[i].Count; + } + + SetupDefaultColors(); + } + + private void SetupDefaultColors() + { + lightbarSettingInfo[0].ds4winSettings.m_Led = new DS4Color(Color.Blue); + lightbarSettingInfo[1].ds4winSettings.m_Led = new DS4Color(Color.Red); + lightbarSettingInfo[2].ds4winSettings.m_Led = new DS4Color(Color.Green); + lightbarSettingInfo[3].ds4winSettings.m_Led = new DS4Color(Color.Pink); + lightbarSettingInfo[4].ds4winSettings.m_Led = new DS4Color(Color.White); + } + + private string stickOutputCurveString(int id) + { + string result = "linear"; + switch (id) + { + case 0: break; + case 1: result = "enhanced-precision"; break; + case 2: result = "quadratic"; break; + case 3: result = "cubic"; break; + case 4: result = "easeout-quad"; break; + case 5: result = "easeout-cubic"; break; + case 6: result = "custom"; break; + default: break; + } + + return result; + } + + private int stickOutputCurveId(string name) + { + int id = 0; + switch (name) + { + case "linear": id = 0; break; + case "enhanced-precision": id = 1; break; + case "quadratic": id = 2; break; + case "cubic": id = 3; break; + case "easeout-quad": id = 4; break; + case "easeout-cubic": id = 5; break; + case "custom": id = 6; break; + default: break; + } + + return id; + } + + private string axisOutputCurveString(int id) + { + return stickOutputCurveString(id); + } + + private int axisOutputCurveId(string name) + { + return stickOutputCurveId(name); + } + + private bool SaTriggerCondValue(string text) + { + bool result = true; + switch (text) + { + case "and": result = true; break; + case "or": result = false; break; + default: result = true; break; } + + return result; + } + + private string SaTriggerCondString(bool value) + { + string result = value ? "and" : "or"; + return result; + } + + public void SetSaTriggerCond(int index, string text) + { + sATriggerCond[index] = SaTriggerCondValue(text); } - /*public X360Controls GetCustomButton(int device, DS4Controls controlName) + public void SetSaMouseStickTriggerCond(int index, string text) { - if (customMapButtons[device].ContainsKey(controlName)) - return customMapButtons[device][controlName]; - else return X360Controls.None; + sAMouseStickTriggerCond[index] = SaTriggerCondValue(text); } - public UInt16 GetCustomKey(int device, DS4Controls controlName) + + public void SetGyroMouseDZ(int index, int value, ControlService control) + { + gyroMouseDZ[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].CursorGyroDead = value; + } + + public void SetGyroMouseToggle(int index, bool value, ControlService control) { - if (customMapKeys[device].ContainsKey(controlName)) - return customMapKeys[device][controlName]; - else return 0; + gyroMouseToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomMacro(int device, DS4Controls controlName) + + public void SetGyroMouseStickToggle(int index, bool value, ControlService control) { - if (customMapMacros[device].ContainsKey(controlName)) - return customMapMacros[device][controlName]; - else return "0"; + gyroMouseStickToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomExtras(int device, DS4Controls controlName) + + private string OutContDeviceString(OutContType id) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "X360"; + switch (id) + { + case OutContType.None: + case OutContType.X360: result = "X360"; break; + case OutContType.DS4: result = "DS4"; break; + default: break; + } + + return result; } - public DS4KeyType GetCustomKeyType(int device, DS4Controls controlName) + + private OutContType OutContDeviceId(string name) { - try + OutContType id = OutContType.X360; + switch (name) { - if (customMapKeyTypes[device].ContainsKey(controlName)) - return customMapKeyTypes[device][controlName]; - else return 0; + case "None": + case "X360": id = OutContType.X360; break; + case "DS4": id = OutContType.DS4; break; + default: break; } - catch { return 0; } + + return id; } - public X360Controls GetShiftCustomButton(int device, DS4Controls controlName) + private void PortOldGyroSettings(int device) { - if (shiftCustomMapButtons[device].ContainsKey(controlName)) - return shiftCustomMapButtons[device][controlName]; - else return X360Controls.None; + if (gyroOutMode[device] == GyroOutMode.None) + { + gyroOutMode[device] = GyroOutMode.Controls; + } } - public UInt16 GetShiftCustomKey(int device, DS4Controls controlName) + + private string GetGyroOutModeString(GyroOutMode mode) { - if (shiftCustomMapKeys[device].ContainsKey(controlName)) - return shiftCustomMapKeys[device][controlName]; - else return 0; + string result = "None"; + switch(mode) + { + case GyroOutMode.Controls: + result = "Controls"; + break; + case GyroOutMode.Mouse: + result = "Mouse"; + break; + case GyroOutMode.MouseJoystick: + result = "MouseJoystick"; + break; + default: + break; + } + + return result; } - public string GetShiftCustomMacro(int device, DS4Controls controlName) + + private GyroOutMode GetGyroOutModeType(string modeString) { - if (shiftCustomMapMacros[device].ContainsKey(controlName)) - return shiftCustomMapMacros[device][controlName]; - else return "0"; + GyroOutMode result = GyroOutMode.None; + switch(modeString) + { + case "Controls": + result = GyroOutMode.Controls; + break; + case "Mouse": + result = GyroOutMode.Mouse; + break; + case "MouseJoystick": + result = GyroOutMode.MouseJoystick; + break; + default: + break; + } + + return result; } - public string GetShiftCustomExtras(int device, DS4Controls controlName) + + private string GetLightbarModeString(LightbarMode mode) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "DS4Win"; + switch (mode) + { + case LightbarMode.DS4Win: + result = "DS4Win"; + break; + case LightbarMode.Passthru: + result = "Passthru"; + break; + default: + break; + } + return result; } - public DS4KeyType GetShiftCustomKeyType(int device, DS4Controls controlName) + + private LightbarMode GetLightbarModeType(string modeString) { - try + LightbarMode result = LightbarMode.DS4Win; + switch (modeString) { - if (shiftCustomMapKeyTypes[device].ContainsKey(controlName)) - return shiftCustomMapKeyTypes[device][controlName]; - else return 0; + case "DS4Win": + result = LightbarMode.DS4Win; + break; + case "Passthru": + result = LightbarMode.Passthru; + break; + default: + break; } - catch { return 0; } - }*/ + + return result; + } + + public bool SaveAsNewProfile(int device, string propath) + { + bool Saved = true; + ResetProfile(device); + Saved = SaveProfile(device, propath); + return Saved; + } public bool SaveProfile(int device, string propath) { @@ -719,49 +2449,81 @@ public bool SaveProfile(int device, string propath) Node = m_Xdoc.CreateComment(string.Format(" DS4Windows Configuration Data. {0} ", DateTime.Now)); m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateComment(string.Format(" Made with DS4Windows version {0} ", Global.exeversion)); + m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateWhitespace("\r\n"); m_Xdoc.AppendChild(Node); Node = m_Xdoc.CreateNode(XmlNodeType.Element, "DS4Windows", null); + LightbarSettingInfo lightbarSettings = lightbarSettingInfo[device]; + LightbarDS4WinInfo lightInfo = lightbarSettings.ds4winSettings; + XmlNode xmlFlushHIDQueue = m_Xdoc.CreateNode(XmlNodeType.Element, "flushHIDQueue", null); xmlFlushHIDQueue.InnerText = flushHIDQueue[device].ToString(); Node.AppendChild(xmlFlushHIDQueue); + XmlNode xmlTouchToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "touchToggle", null); xmlTouchToggle.InnerText = enableTouchToggle[device].ToString(); Node.AppendChild(xmlTouchToggle); XmlNode xmlIdleDisconnectTimeout = m_Xdoc.CreateNode(XmlNodeType.Element, "idleDisconnectTimeout", null); xmlIdleDisconnectTimeout.InnerText = idleDisconnectTimeout[device].ToString(); Node.AppendChild(xmlIdleDisconnectTimeout); XmlNode xmlColor = m_Xdoc.CreateNode(XmlNodeType.Element, "Color", null); - xmlColor.InnerText = m_Leds[device].red.ToString() + "," + m_Leds[device].green.ToString() + "," + m_Leds[device].blue.ToString(); + xmlColor.InnerText = lightInfo.m_Led.red.ToString() + "," + lightInfo.m_Led.green.ToString() + "," + lightInfo.m_Led.blue.ToString(); Node.AppendChild(xmlColor); XmlNode xmlRumbleBoost = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleBoost", null); xmlRumbleBoost.InnerText = rumble[device].ToString(); Node.AppendChild(xmlRumbleBoost); - XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = ledAsBattery[device].ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); - XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = flashType[device].ToString(); Node.AppendChild(xmlLowBatteryFlash); - XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = flashAt[device].ToString(); Node.AppendChild(xmlFlashBatterAt); + XmlNode xmlRumbleAutostopTime = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleAutostopTime", null); xmlRumbleAutostopTime.InnerText = rumbleAutostopTime[device].ToString(); Node.AppendChild(xmlRumbleAutostopTime); + XmlNode xmlLightbarMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LightbarMode", null); xmlLightbarMode.InnerText = GetLightbarModeString(lightbarSettings.mode); Node.AppendChild(xmlLightbarMode); + XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = lightInfo.ledAsBattery.ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); + XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = lightInfo.flashType.ToString(); Node.AppendChild(xmlLowBatteryFlash); + XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = lightInfo.flashAt.ToString(); Node.AppendChild(xmlFlashBatterAt); XmlNode xmlTouchSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "touchSensitivity", null); xmlTouchSensitivity.InnerText = touchSensitivity[device].ToString(); Node.AppendChild(xmlTouchSensitivity); XmlNode xmlLowColor = m_Xdoc.CreateNode(XmlNodeType.Element, "LowColor", null); - xmlLowColor.InnerText = m_LowLeds[device].red.ToString() + "," + m_LowLeds[device].green.ToString() + "," + m_LowLeds[device].blue.ToString(); + xmlLowColor.InnerText = lightInfo.m_LowLed.red.ToString() + "," + lightInfo.m_LowLed.green.ToString() + "," + lightInfo.m_LowLed.blue.ToString(); Node.AppendChild(xmlLowColor); XmlNode xmlChargingColor = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingColor", null); - xmlChargingColor.InnerText = m_ChargingLeds[device].red.ToString() + "," + m_ChargingLeds[device].green.ToString() + "," + m_ChargingLeds[device].blue.ToString(); + xmlChargingColor.InnerText = lightInfo.m_ChargingLed.red.ToString() + "," + lightInfo.m_ChargingLed.green.ToString() + "," + lightInfo.m_ChargingLed.blue.ToString(); Node.AppendChild(xmlChargingColor); XmlNode xmlFlashColor = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashColor", null); - xmlFlashColor.InnerText = m_FlashLeds[device].red.ToString() + "," + m_FlashLeds[device].green.ToString() + "," + m_FlashLeds[device].blue.ToString(); + xmlFlashColor.InnerText = lightInfo.m_FlashLed.red.ToString() + "," + lightInfo.m_FlashLed.green.ToString() + "," + lightInfo.m_FlashLed.blue.ToString(); Node.AppendChild(xmlFlashColor); XmlNode xmlTouchpadJitterCompensation = m_Xdoc.CreateNode(XmlNodeType.Element, "touchpadJitterCompensation", null); xmlTouchpadJitterCompensation.InnerText = touchpadJitterCompensation[device].ToString(); Node.AppendChild(xmlTouchpadJitterCompensation); XmlNode xmlLowerRCOn = m_Xdoc.CreateNode(XmlNodeType.Element, "lowerRCOn", null); xmlLowerRCOn.InnerText = lowerRCOn[device].ToString(); Node.AppendChild(xmlLowerRCOn); XmlNode xmlTapSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "tapSensitivity", null); xmlTapSensitivity.InnerText = tapSensitivity[device].ToString(); Node.AppendChild(xmlTapSensitivity); XmlNode xmlDouble = m_Xdoc.CreateNode(XmlNodeType.Element, "doubleTap", null); xmlDouble.InnerText = doubleTap[device].ToString(); Node.AppendChild(xmlDouble); XmlNode xmlScrollSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "scrollSensitivity", null); xmlScrollSensitivity.InnerText = scrollSensitivity[device].ToString(); Node.AppendChild(xmlScrollSensitivity); - XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2Deadzone[device].ToString(); Node.AppendChild(xmlLeftTriggerMiddle); - XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2Deadzone[device].ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLeftTriggerMiddle); + XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlTouchpadInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchpadInvert", null); xmlTouchpadInvert.InnerText = touchpadInvert[device].ToString(); Node.AppendChild(xmlTouchpadInvert); + XmlNode xmlL2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "L2AntiDeadZone", null); xmlL2AD.InnerText = l2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlL2AD); + XmlNode xmlR2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "R2AntiDeadZone", null); xmlR2AD.InnerText = r2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlR2AD); + XmlNode xmlL2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxZone", null); xmlL2Maxzone.InnerText = l2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlL2Maxzone); + XmlNode xmlR2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxZone", null); xmlR2Maxzone.InnerText = r2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlR2Maxzone); + XmlNode xmlL2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxOutput", null); xmlL2MaxOutput.InnerText = l2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlL2MaxOutput); + XmlNode xmlR2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxOutput", null); xmlR2MaxOutput.InnerText = r2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlR2MaxOutput); XmlNode xmlButtonMouseSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "ButtonMouseSensitivity", null); xmlButtonMouseSensitivity.InnerText = buttonMouseSensitivity[device].ToString(); Node.AppendChild(xmlButtonMouseSensitivity); - XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = rainbow[device].ToString(); Node.AppendChild(xmlRainbow); - XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = LSDeadzone[device].ToString(); Node.AppendChild(xmlLSD); - XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = RSDeadzone[device].ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = lightInfo.rainbow.ToString(); Node.AppendChild(xmlRainbow); + XmlNode xmlMaxSatRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "MaxSatRainbow", null); xmlMaxSatRainbow.InnerText = Convert.ToInt32(lightInfo.maxRainbowSat * 100.0).ToString(); Node.AppendChild(xmlMaxSatRainbow); + XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = lsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLSD); + XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = rsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlLSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSAntiDeadZone", null); xmlLSAD.InnerText = lsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlLSAD); + XmlNode xmlRSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSAntiDeadZone", null); xmlRSAD.InnerText = rsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlRSAD); + XmlNode xmlLSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxZone", null); xmlLSMaxZone.InnerText = lsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlLSMaxZone); + XmlNode xmlRSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxZone", null); xmlRSMaxZone.InnerText = rsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlRSMaxZone); + XmlNode xmlLSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxOutput", null); xmlLSMaxOutput.InnerText = lsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlLSMaxOutput); + XmlNode xmlRSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxOutput", null); xmlRSMaxOutput.InnerText = rsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlRSMaxOutput); + XmlNode xmlLSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "LSRotation", null); xmlLSRotation.InnerText = Convert.ToInt32(LSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlLSRotation); + XmlNode xmlRSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "RSRotation", null); xmlRSRotation.InnerText = Convert.ToInt32(RSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlRSRotation); + XmlNode xmlSXD = m_Xdoc.CreateNode(XmlNodeType.Element, "SXDeadZone", null); xmlSXD.InnerText = SXDeadzone[device].ToString(); Node.AppendChild(xmlSXD); XmlNode xmlSZD = m_Xdoc.CreateNode(XmlNodeType.Element, "SZDeadZone", null); xmlSZD.InnerText = SZDeadzone[device].ToString(); Node.AppendChild(xmlSZD); + XmlNode xmlSXMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXMaxZone", null); xmlSXMaxzone.InnerText = Convert.ToInt32(SXMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXMaxzone); + XmlNode xmlSZMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZMaxZone", null); xmlSZMaxzone.InnerText = Convert.ToInt32(SZMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZMaxzone); + + XmlNode xmlSXAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXAntiDeadZone", null); xmlSXAntiDeadzone.InnerText = Convert.ToInt32(SXAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXAntiDeadzone); + XmlNode xmlSZAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZAntiDeadZone", null); xmlSZAntiDeadzone.InnerText = Convert.ToInt32(SZAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZAntiDeadzone); + XmlNode xmlSens = m_Xdoc.CreateNode(XmlNodeType.Element, "Sensitivity", null); xmlSens.InnerText = $"{LSSens[device]}|{RSSens[device]}|{l2Sens[device]}|{r2Sens[device]}|{SXSens[device]}|{SZSens[device]}"; Node.AppendChild(xmlSens); - XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = chargingType[device].ToString(); Node.AppendChild(xmlChargingType); + XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = lightInfo.chargingType.ToString(); Node.AppendChild(xmlChargingType); XmlNode xmlMouseAccel = m_Xdoc.CreateNode(XmlNodeType.Element, "MouseAcceleration", null); xmlMouseAccel.InnerText = mouseAccel[device].ToString(); Node.AppendChild(xmlMouseAccel); //XmlNode xmlShiftMod = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftModifier", null); xmlShiftMod.InnerText = shiftModifier[device].ToString(); Node.AppendChild(xmlShiftMod); XmlNode xmlLaunchProgram = m_Xdoc.CreateNode(XmlNodeType.Element, "LaunchProgram", null); xmlLaunchProgram.InnerText = launchProgram[device].ToString(); Node.AppendChild(xmlLaunchProgram); @@ -770,11 +2532,75 @@ public bool SaveProfile(int device, string propath) XmlNode xmlUseTPforControls = m_Xdoc.CreateNode(XmlNodeType.Element, "UseTPforControls", null); xmlUseTPforControls.InnerText = useTPforControls[device].ToString(); Node.AppendChild(xmlUseTPforControls); XmlNode xmlUseSAforMouse = m_Xdoc.CreateNode(XmlNodeType.Element, "UseSAforMouse", null); xmlUseSAforMouse.InnerText = useSAforMouse[device].ToString(); Node.AppendChild(xmlUseSAforMouse); XmlNode xmlSATriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggers", null); xmlSATriggers.InnerText = sATriggers[device].ToString(); Node.AppendChild(xmlSATriggers); + XmlNode xmlSATriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggerCond", null); xmlSATriggerCond.InnerText = SaTriggerCondString(sATriggerCond[device]); Node.AppendChild(xmlSATriggerCond); + XmlNode xmlSASteeringWheelEmulationAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationAxis", null); xmlSASteeringWheelEmulationAxis.InnerText = sASteeringWheelEmulationAxis[device].ToString("G"); Node.AppendChild(xmlSASteeringWheelEmulationAxis); + XmlNode xmlSASteeringWheelEmulationRange = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationRange", null); xmlSASteeringWheelEmulationRange.InnerText = sASteeringWheelEmulationRange[device].ToString(); Node.AppendChild(xmlSASteeringWheelEmulationRange); + + + + XmlNode xmlTouchDisInvTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchDisInvTriggers", null); + string tempTouchDisInv = string.Join(",", touchDisInvertTriggers[device]); + xmlTouchDisInvTriggers.InnerText = tempTouchDisInv; + Node.AppendChild(xmlTouchDisInvTriggers); + XmlNode xmlGyroSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensitivity", null); xmlGyroSensitivity.InnerText = gyroSensitivity[device].ToString(); Node.AppendChild(xmlGyroSensitivity); + XmlNode xmlGyroSensVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensVerticalScale", null); xmlGyroSensVerticalScale.InnerText = gyroSensVerticalScale[device].ToString(); Node.AppendChild(xmlGyroSensVerticalScale); XmlNode xmlGyroInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroInvert", null); xmlGyroInvert.InnerText = gyroInvert[device].ToString(); Node.AppendChild(xmlGyroInvert); + XmlNode xmlGyroTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroTriggerTurns", null); xmlGyroTriggerTurns.InnerText = gyroTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroTriggerTurns); + XmlNode xmlGyroSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothingWeight", null); xmlGyroSmoothWeight.InnerText = Convert.ToInt32(gyroSmoothWeight[device] * 100).ToString(); Node.AppendChild(xmlGyroSmoothWeight); + XmlNode xmlGyroSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothing", null); xmlGyroSmoothing.InnerText = gyroSmoothing[device].ToString(); Node.AppendChild(xmlGyroSmoothing); + XmlNode xmlGyroMouseHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseHAxis", null); xmlGyroMouseHAxis.InnerText = gyroMouseHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMouseHAxis); + XmlNode xmlGyroMouseDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseDeadZone", null); xmlGyroMouseDZ.InnerText = gyroMouseDZ[device].ToString(); Node.AppendChild(xmlGyroMouseDZ); + XmlNode xmlGyroMouseToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseToggle", null); xmlGyroMouseToggle.InnerText = gyroMouseToggle[device].ToString(); Node.AppendChild(xmlGyroMouseToggle); + + XmlNode xmlGyroOutMode = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroOutputMode", null); xmlGyroOutMode.InnerText = GetGyroOutModeString(gyroOutMode[device]); Node.AppendChild(xmlGyroOutMode); + XmlNode xmlGyroMStickTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggers", null); xmlGyroMStickTriggers.InnerText = sAMouseStickTriggers[device].ToString(); Node.AppendChild(xmlGyroMStickTriggers); + XmlNode xmlGyroMStickTriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerCond", null); xmlGyroMStickTriggerCond.InnerText = SaTriggerCondString(sAMouseStickTriggerCond[device]); Node.AppendChild(xmlGyroMStickTriggerCond); + XmlNode xmlGyroMStickTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerTurns", null); xmlGyroMStickTriggerTurns.InnerText = gyroMouseStickTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroMStickTriggerTurns); + XmlNode xmlGyroMStickHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickHAxis", null); xmlGyroMStickHAxis.InnerText = gyroMouseStickHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMStickHAxis); + XmlNode xmlGyroMStickDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickDeadZone", null); xmlGyroMStickDZ.InnerText = gyroMStickInfo[device].deadZone.ToString(); Node.AppendChild(xmlGyroMStickDZ); + XmlNode xmlGyroMStickMaxZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickMaxZone", null); xmlGyroMStickMaxZ.InnerText = gyroMStickInfo[device].maxZone.ToString(); Node.AppendChild(xmlGyroMStickMaxZ); + XmlNode xmlGyroMStickAntiDX = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadX", null); xmlGyroMStickAntiDX.InnerText = gyroMStickInfo[device].antiDeadX.ToString(); Node.AppendChild(xmlGyroMStickAntiDX); + XmlNode xmlGyroMStickAntiDY = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadY", null); xmlGyroMStickAntiDY.InnerText = gyroMStickInfo[device].antiDeadY.ToString(); Node.AppendChild(xmlGyroMStickAntiDY); + XmlNode xmlGyroMStickInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickInvert", null); xmlGyroMStickInvert.InnerText = gyroMStickInfo[device].inverted.ToString(); Node.AppendChild(xmlGyroMStickInvert); + XmlNode xmlGyroMStickToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickToggle", null); xmlGyroMStickToggle.InnerText = gyroMouseStickToggle[device].ToString(); Node.AppendChild(xmlGyroMStickToggle); + XmlNode xmlGyroMStickVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickVerticalScale", null); xmlGyroMStickVerticalScale.InnerText = gyroMStickInfo[device].vertScale.ToString(); Node.AppendChild(xmlGyroMStickVerticalScale); + XmlNode xmlGyroMStickSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothing", null); xmlGyroMStickSmoothing.InnerText = gyroMStickInfo[device].useSmoothing.ToString(); Node.AppendChild(xmlGyroMStickSmoothing); + XmlNode xmlGyroMStickSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothingWeight", null); xmlGyroMStickSmoothWeight.InnerText = Convert.ToInt32(gyroMStickInfo[device].smoothWeight * 100).ToString(); Node.AppendChild(xmlGyroMStickSmoothWeight); + XmlNode xmlLSC = m_Xdoc.CreateNode(XmlNodeType.Element, "LSCurve", null); xmlLSC.InnerText = lsCurve[device].ToString(); Node.AppendChild(xmlLSC); XmlNode xmlRSC = m_Xdoc.CreateNode(XmlNodeType.Element, "RSCurve", null); xmlRSC.InnerText = rsCurve[device].ToString(); Node.AppendChild(xmlRSC); XmlNode xmlProfileActions = m_Xdoc.CreateNode(XmlNodeType.Element, "ProfileActions", null); xmlProfileActions.InnerText = string.Join("/", profileActions[device]); Node.AppendChild(xmlProfileActions); + XmlNode xmlBTPollRate = m_Xdoc.CreateNode(XmlNodeType.Element, "BTPollRate", null); xmlBTPollRate.InnerText = btPollRate[device].ToString(); Node.AppendChild(xmlBTPollRate); + + XmlNode xmlLsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveMode", null); xmlLsOutputCurveMode.InnerText = stickOutputCurveString(getLsOutCurveMode(device)); Node.AppendChild(xmlLsOutputCurveMode); + XmlNode xmlLsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveCustom", null); xmlLsOutputCurveCustom.InnerText = lsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlLsOutputCurveCustom); + + XmlNode xmlRsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveMode", null); xmlRsOutputCurveMode.InnerText = stickOutputCurveString(getRsOutCurveMode(device)); Node.AppendChild(xmlRsOutputCurveMode); + XmlNode xmlRsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveCustom", null); xmlRsOutputCurveCustom.InnerText = rsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlRsOutputCurveCustom); + + XmlNode xmlLsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSSquareStick", null); xmlLsSquareStickMode.InnerText = squStickInfo[device].lsMode.ToString(); Node.AppendChild(xmlLsSquareStickMode); + XmlNode xmlRsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSSquareStick", null); xmlRsSquareStickMode.InnerText = squStickInfo[device].rsMode.ToString(); Node.AppendChild(xmlRsSquareStickMode); + + XmlNode xmlSquareStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareStickRoundness", null); xmlSquareStickRoundness.InnerText = squStickInfo[device].lsRoundness.ToString(); Node.AppendChild(xmlSquareStickRoundness); + XmlNode xmlSquareRStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareRStickRoundness", null); xmlSquareRStickRoundness.InnerText = squStickInfo[device].rsRoundness.ToString(); Node.AppendChild(xmlSquareRStickRoundness); + + XmlNode xmlL2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveMode", null); xmlL2OutputCurveMode.InnerText = axisOutputCurveString(getL2OutCurveMode(device)); Node.AppendChild(xmlL2OutputCurveMode); + XmlNode xmlL2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveCustom", null); xmlL2OutputCurveCustom.InnerText = l2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlL2OutputCurveCustom); + + XmlNode xmlR2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveMode", null); xmlR2OutputCurveMode.InnerText = axisOutputCurveString(getR2OutCurveMode(device)); Node.AppendChild(xmlR2OutputCurveMode); + XmlNode xmlR2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveCustom", null); xmlR2OutputCurveCustom.InnerText = r2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlR2OutputCurveCustom); + + XmlNode xmlSXOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveMode", null); xmlSXOutputCurveMode.InnerText = axisOutputCurveString(getSXOutCurveMode(device)); Node.AppendChild(xmlSXOutputCurveMode); + XmlNode xmlSXOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveCustom", null); xmlSXOutputCurveCustom.InnerText = sxOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSXOutputCurveCustom); + + XmlNode xmlSZOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveMode", null); xmlSZOutputCurveMode.InnerText = axisOutputCurveString(getSZOutCurveMode(device)); Node.AppendChild(xmlSZOutputCurveMode); + XmlNode xmlSZOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveCustom", null); xmlSZOutputCurveCustom.InnerText = szOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSZOutputCurveCustom); + + XmlNode xmlTrackBallMode = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballMode", null); xmlTrackBallMode.InnerText = trackballMode[device].ToString(); Node.AppendChild(xmlTrackBallMode); + XmlNode xmlTrackBallFriction = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballFriction", null); xmlTrackBallFriction.InnerText = trackballFriction[device].ToString(); Node.AppendChild(xmlTrackBallFriction); + + XmlNode xmlOutContDevice = m_Xdoc.CreateNode(XmlNodeType.Element, "OutputContDevice", null); xmlOutContDevice.InnerText = OutContDeviceString(outputDevType[device]); Node.AppendChild(xmlOutContDevice); XmlNode NodeControl = m_Xdoc.CreateNode(XmlNodeType.Element, "Control", null); XmlNode Key = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); @@ -799,16 +2625,21 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.action is string) + { if (dcs.action.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.keyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; - if (dcs.keyType.HasFlag(DS4KeyType.Macro)) + else if (dcs.keyType.HasFlag(DS4KeyType.Macro)) keyType += DS4KeyType.Macro; + if (dcs.keyType.HasFlag(DS4KeyType.Toggle)) keyType += DS4KeyType.Toggle; if (dcs.keyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -828,21 +2659,31 @@ public bool SaveProfile(int device, string propath) buttonNode.InnerText = dcs.action.ToString(); Key.AppendChild(buttonNode); } - else if (dcs.action is string || dcs.action is X360Controls) + else if (dcs.action is string) { buttonNode.InnerText = dcs.action.ToString(); Button.AppendChild(buttonNode); } + else if (dcs.action is X360Controls) + { + buttonNode.InnerText = getX360ControlString((X360Controls)dcs.action); + Button.AppendChild(buttonNode); + } } bool hasvalue = false; if (!string.IsNullOrEmpty(dcs.extras)) + { foreach (string s in dcs.extras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -856,8 +2697,11 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.shiftAction is string) + { if (dcs.shiftAction.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.shiftKeyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; if (dcs.shiftKeyType.HasFlag(DS4KeyType.Macro)) @@ -866,6 +2710,7 @@ public bool SaveProfile(int device, string propath) keyType += DS4KeyType.Toggle; if (dcs.shiftKeyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateElement(dcs.control.ToString()); @@ -892,14 +2737,20 @@ public bool SaveProfile(int device, string propath) ShiftButton.AppendChild(buttonNode); } } + hasvalue = false; if (!string.IsNullOrEmpty(dcs.shiftExtras)) + { foreach (string s in dcs.shiftExtras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -907,6 +2758,7 @@ public bool SaveProfile(int device, string propath) ShiftExtras.AppendChild(extraNode); } } + Node.AppendChild(NodeControl); if (Button.HasChildNodes) NodeControl.AppendChild(Button); @@ -932,104 +2784,7 @@ public bool SaveProfile(int device, string propath) NodeShiftControl.AppendChild(ShiftKeyType); if (ShiftExtras.HasChildNodes) NodeShiftControl.AppendChild(ShiftExtras); - /*else if (xmlControls != null) - { - Node.AppendChild(xmlControls); - }*/ - /*if (shiftModifier[device] > 0) - { - XmlNode NodeShiftControl = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftControl", null); - - XmlNode ShiftKey = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); - XmlNode ShiftMacro = m_Xdoc.CreateNode(XmlNodeType.Element, "Macro", null); - XmlNode ShiftKeyType = m_Xdoc.CreateNode(XmlNodeType.Element, "KeyType", null); - XmlNode ShiftButton = m_Xdoc.CreateNode(XmlNodeType.Element, "Button", null); - XmlNode ShiftExtras = m_Xdoc.CreateNode(XmlNodeType.Element, "Extras", null); - if (shiftbuttons != null) - { - foreach (var button in shiftbuttons) - { - // Save even if string (for xbox controller buttons) - if (button.Tag != null) - { - XmlNode buttonNode; - string keyType = String.Empty; - if (button.Tag is KeyValuePair) - if (((KeyValuePair)button.Tag).Key == "Unbound") - keyType += DS4KeyType.Unbound; - - if (button.Font.Strikeout) - keyType += DS4KeyType.HoldMacro; - if (button.Font.Underline) - keyType += DS4KeyType.Macro; - if (button.Font.Italic) - keyType += DS4KeyType.Toggle; - if (button.Font.Bold) - keyType += DS4KeyType.ScanCode; - if (keyType != String.Empty) - { - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - buttonNode.InnerText = keyType; - ShiftKeyType.AppendChild(buttonNode); - } - - string[] extras; - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - if (button.Tag is KeyValuePair, string> || button.Tag is KeyValuePair || button.Tag is KeyValuePair) - { - KeyValuePair
, or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID `%s`.", t, Y.getID(e)) : S(!1) + }, + _mountImageIntoNode: function(e, t, o) { + if ("production" !== n.env.NODE_ENV ? S(t && (t.nodeType === F || t.nodeType === B), "mountComponentIntoNode(...): Target container is not valid.") : S(t && (t.nodeType === F || t.nodeType === B)), o) { + var a = P(t); + if (O.canReuseMarkup(e, a)) return; + var i = a.getAttribute(O.CHECKSUM_ATTR_NAME); + a.removeAttribute(O.CHECKSUM_ATTR_NAME); + var s = a.outerHTML; + a.setAttribute(O.CHECKSUM_ATTR_NAME, i); + var u = r(e, s), + c = " (client) " + e.substring(u - 20, u + 20) + "\n (server) " + s.substring(u - 20, u + 20); + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side:\n%s", c) : S(t.nodeType !== B), "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? V(!1, "React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:\n%s", c) : null) + } + "production" !== n.env.NODE_ENV ? S(t.nodeType !== B, "You're trying to render a component to the document but you didn't use server rendering. We can't do this without using server rendering due to cross-browser quirks. See React.renderToString() for server rendering.") : S(t.nodeType !== B), k(t, e) + }, + getReactRootID: o, + getID: a, + setID: s, + getNode: u, + getNodeFromInstance: c, + purgeID: p + }; + R.measureMethods(Y, "ReactMount", { + _renderNewRootComponent: "_renderNewRootComponent", + _mountImageIntoNode: "_mountImageIntoNode" + }), t.exports = Y + }).call(this, e("_process")) + }, { + "./DOMProperty": 17, + "./ReactBrowserEventEmitter": 39, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactElementValidator": 70, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMarkupChecksum": 82, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./ReactUpdateQueue": 105, + "./ReactUpdates": 106, + "./containsNode": 130, + "./emptyObject": 137, + "./getReactRootElementInContainer": 151, + "./instantiateReactComponent": 156, + "./invariant": 157, + "./setInnerHTML": 171, + "./shouldUpdateReactComponent": 174, + "./warning": 178, + _process: 2 + }], + 84: [function(e, t) { + "use strict"; + + function n(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.INSERT_MARKUP, + markupIndex: h.push(t) - 1, + textContent: null, + fromIndex: null, + toIndex: n + }) + } + + function r(e, t, n) { + f.push({ + parentID: e, + parentNode: null, + type: c.MOVE_EXISTING, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: n + }) + } + + function o(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.REMOVE_NODE, + markupIndex: null, + textContent: null, + fromIndex: t, + toIndex: null + }) + } + + function a(e, t) { + f.push({ + parentID: e, + parentNode: null, + type: c.TEXT_CONTENT, + markupIndex: null, + textContent: t, + fromIndex: null, + toIndex: null + }) + } + + function i() { + f.length && (u.processChildrenUpdates(f, h), s()) + } + + function s() { + f.length = 0, h.length = 0 + } + var u = e("./ReactComponentEnvironment"), + c = e("./ReactMultiChildUpdateTypes"), + l = e("./ReactReconciler"), + p = e("./ReactChildReconciler"), + d = 0, + f = [], + h = [], + v = { + Mixin: { + mountChildren: function(e, t, n) { + var r = p.instantiateChildren(e, t, n); + this._renderedChildren = r; + var o = [], + a = 0; + for (var i in r) + if (r.hasOwnProperty(i)) { + var s = r[i], + u = this._rootNodeID + i, + c = l.mountComponent(s, u, t, n); + s._mountIndex = a, o.push(c), a++ + } return o + }, + updateTextContent: function(e) { + d++; + var t = !0; + try { + var n = this._renderedChildren; + p.unmountChildren(n); + for (var r in n) n.hasOwnProperty(r) && this._unmountChildByName(n[r], r); + this.setTextContent(e), t = !1 + } finally { + d--, d || (t ? s() : i()) + } + }, + updateChildren: function(e, t, n) { + d++; + var r = !0; + try { + this._updateChildren(e, t, n), r = !1 + } finally { + d--, d || (r ? s() : i()) + } + }, + _updateChildren: function(e, t, n) { + var r = this._renderedChildren, + o = p.updateChildren(r, e, t, n); + if (this._renderedChildren = o, o || r) { + var a, i = 0, + s = 0; + for (a in o) + if (o.hasOwnProperty(a)) { + var u = r && r[a], + c = o[a]; + u === c ? (this.moveChild(u, s, i), i = Math.max(u._mountIndex, i), u._mountIndex = s) : (u && (i = Math.max(u._mountIndex, i), this._unmountChildByName(u, a)), this._mountChildByNameAtIndex(c, a, s, t, n)), s++ + } for (a in r) !r.hasOwnProperty(a) || o && o.hasOwnProperty(a) || this._unmountChildByName(r[a], a) + } + }, + unmountChildren: function() { + var e = this._renderedChildren; + p.unmountChildren(e), this._renderedChildren = null + }, + moveChild: function(e, t, n) { + e._mountIndex < n && r(this._rootNodeID, e._mountIndex, t) + }, + createChild: function(e, t) { + n(this._rootNodeID, t, e._mountIndex) + }, + removeChild: function(e) { + o(this._rootNodeID, e._mountIndex) + }, + setTextContent: function(e) { + a(this._rootNodeID, e) + }, + _mountChildByNameAtIndex: function(e, t, n, r, o) { + var a = this._rootNodeID + t, + i = l.mountComponent(e, a, r, o); + e._mountIndex = n, this.createChild(e, i) + }, + _unmountChildByName: function(e) { + this.removeChild(e), e._mountIndex = null + } + } + }; + t.exports = v + }, { + "./ReactChildReconciler": 42, + "./ReactComponentEnvironment": 47, + "./ReactMultiChildUpdateTypes": 85, + "./ReactReconciler": 95 + }], + 85: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + INSERT_MARKUP: null, + MOVE_EXISTING: null, + REMOVE_NODE: null, + TEXT_CONTENT: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 86: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + if ("function" == typeof e.type) return e.type; + var t = e.type, + n = p[t]; + return null == n && (p[t] = n = c(t)), n + } + + function o(e) { + return "production" !== n.env.NODE_ENV ? u(l, "There is no registered component for the tag %s", e.type) : u(l), new l(e.type, e.props) + } + + function a(e) { + return new d(e) + } + + function i(e) { + return e instanceof d + } + var s = e("./Object.assign"), + u = e("./invariant"), + c = null, + l = null, + p = {}, + d = null, + f = { + injectGenericComponentClass: function(e) { + l = e + }, + injectTextComponentClass: function(e) { + d = e + }, + injectComponentClasses: function(e) { + s(p, e) + }, + injectAutoWrapper: function(e) { + c = e + } + }, + h = { + getComponentClassForElement: r, + createInternalComponent: o, + createInstanceForText: a, + isTextComponent: i, + injection: f + }; + t.exports = h + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./invariant": 157, + _process: 2 + }], + 87: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + isValidOwner: function(e) { + return !(!e || "function" != typeof e.attachRef || "function" != typeof e.detachRef) + }, + addComponentAsRefTo: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.attachRef(t, e) + }, + removeComponentAsRefFrom: function(e, t, a) { + "production" !== n.env.NODE_ENV ? r(o.isValidOwner(a), "removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This usually means that you're trying to remove a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.") : r(o.isValidOwner(a)), a.getPublicInstance().refs[t] === e.getPublicInstance() && a.detachRef(t) + } + }; + t.exports = o + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 88: [function(e, t) { + (function(e) { + "use strict"; + + function n(e, t, n) { + return n + } + var r = { + enableMeasure: !1, + storedMeasure: n, + measureMethods: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) + for (var a in o) o.hasOwnProperty(a) && (t[a] = r.measure(n, o[a], t[a])) + }, + measure: function(t, n, o) { + if ("production" !== e.env.NODE_ENV) { + var a = null, + i = function() { + return r.enableMeasure ? (a || (a = r.storedMeasure(t, n, o)), a.apply(this, arguments)) : o.apply(this, arguments) + }; + return i.displayName = t + "_" + n, i + } + return o + }, + injection: { + injectMeasure: function(e) { + r.storedMeasure = e + } + } + }; + t.exports = r + }).call(this, e("_process")) + }, { + _process: 2 + }], + 89: [function(e, t) { + "use strict"; + + function n(e) { + return function(t, n, r) { + t[n] = t.hasOwnProperty(n) ? e(t[n], r) : r + } + } + + function r(e, t) { + for (var n in t) + if (t.hasOwnProperty(n)) { + var r = u[n]; + r && u.hasOwnProperty(n) ? r(e, n, t[n]) : e.hasOwnProperty(n) || (e[n] = t[n]) + } return e + } + var o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./joinClasses"), + s = n(function(e, t) { + return o({}, t, e) + }), + u = { + children: a, + className: n(i), + style: s + }, + c = { + mergeProps: function(e, t) { + return r(o({}, e), t) + } + }; + t.exports = c + }, { + "./Object.assign": 35, + "./emptyFunction": 136, + "./joinClasses": 162 + }], + 90: [function(e, t) { + (function(e) { + "use strict"; + var n = {}; + "production" !== e.env.NODE_ENV && (n = { + prop: "prop", + context: "context", + childContext: "child context" + }), t.exports = n + }).call(this, e("_process")) + }, { + _process: 2 + }], + 91: [function(e, t) { + "use strict"; + var n = e("./keyMirror"), + r = n({ + prop: null, + context: null, + childContext: null + }); + t.exports = r + }, { + "./keyMirror": 163 + }], + 92: [function(e, t) { + "use strict"; + + function n(e) { + function t(t, n, r, o, a) { + if (o = o || C, null == n[r]) { + var i = g[a]; + return t ? new Error("Required " + i + " `" + r + "` was not specified in " + ("`" + o + "`.")) : null + } + return e(n, r, o, a) + } + var n = t.bind(null, !1); + return n.isRequired = t.bind(null, !0), n + } + + function r(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if (i !== e) { + var s = g[o], + u = v(a); + return new Error("Invalid " + s + " `" + n + "` of type `" + u + "` " + ("supplied to `" + r + "`, expected `" + e + "`.")) + } + return null + } + return n(t) + } + + function o() { + return n(E.thatReturns(null)) + } + + function a(e) { + function t(t, n, r, o) { + var a = t[n]; + if (!Array.isArray(a)) { + var i = g[o], + s = h(a); + return new Error("Invalid " + i + " `" + n + "` of type " + ("`" + s + "` supplied to `" + r + "`, expected an array.")) + } + for (var u = 0; u < a.length; u++) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } + return null + } + return n(t) + } + + function i() { + function e(e, t, n, r) { + if (!m.isValidElement(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactElement.")) + } + return null + } + return n(e) + } + + function s(e) { + function t(t, n, r, o) { + if (!(t[n] instanceof e)) { + var a = g[o], + i = e.name || C; + return new Error("Invalid " + a + " `" + n + "` supplied to " + ("`" + r + "`, expected instance of `" + i + "`.")) + } + return null + } + return n(t) + } + + function u(e) { + function t(t, n, r, o) { + for (var a = t[n], i = 0; i < e.length; i++) + if (a === e[i]) return null; + var s = g[o], + u = JSON.stringify(e); + return new Error("Invalid " + s + " `" + n + "` of value `" + a + "` " + ("supplied to `" + r + "`, expected one of " + u + ".")) + } + return n(t) + } + + function c(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type " + ("`" + i + "` supplied to `" + r + "`, expected an object.")) + } + for (var u in a) + if (a.hasOwnProperty(u)) { + var c = e(a, u, r, o); + if (c instanceof Error) return c + } return null + } + return n(t) + } + + function l(e) { + function t(t, n, r, o) { + for (var a = 0; a < e.length; a++) { + var i = e[a]; + if (null == i(t, n, r, o)) return null + } + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` supplied to " + ("`" + r + "`.")) + } + return n(t) + } + + function p() { + function e(e, t, n, r) { + if (!f(e[t])) { + var o = g[r]; + return new Error("Invalid " + o + " `" + t + "` supplied to " + ("`" + n + "`, expected a ReactNode.")) + } + return null + } + return n(e) + } + + function d(e) { + function t(t, n, r, o) { + var a = t[n], + i = h(a); + if ("object" !== i) { + var s = g[o]; + return new Error("Invalid " + s + " `" + n + "` of type `" + i + "` " + ("supplied to `" + r + "`, expected `object`.")) + } + for (var u in e) { + var c = e[u]; + if (c) { + var l = c(a, u, r, o); + if (l) return l + } + } + return null + } + return n(t) + } + + function f(e) { + switch (typeof e) { + case "number": + case "string": + case "undefined": + return !0; + case "boolean": + return !e; + case "object": + if (Array.isArray(e)) return e.every(f); + if (null === e || m.isValidElement(e)) return !0; + e = y.extractIfFragment(e); + for (var t in e) + if (!f(e[t])) return !1; + return !0; + default: + return !1 + } + } + + function h(e) { + var t = typeof e; + return Array.isArray(e) ? "array" : e instanceof RegExp ? "object" : t + } + + function v(e) { + var t = h(e); + if ("object" === t) { + if (e instanceof Date) return "date"; + if (e instanceof RegExp) return "regexp" + } + return t + } + var m = e("./ReactElement"), + y = e("./ReactFragment"), + g = e("./ReactPropTypeLocationNames"), + E = e("./emptyFunction"), + C = "<>", + b = i(), + _ = p(), + N = { + array: r("array"), + bool: r("boolean"), + func: r("function"), + number: r("number"), + object: r("object"), + string: r("string"), + any: o(), + arrayOf: a, + element: b, + instanceOf: s, + node: _, + objectOf: c, + oneOf: u, + oneOfType: l, + shape: d + }; + t.exports = N + }, { + "./ReactElement": 69, + "./ReactFragment": 75, + "./ReactPropTypeLocationNames": 90, + "./emptyFunction": 136 + }], + 93: [function(e, t) { + "use strict"; + + function n() { + this.listenersToPut = [] + } + var r = e("./PooledClass"), + o = e("./ReactBrowserEventEmitter"), + a = e("./Object.assign"); + a(n.prototype, { + enqueuePutListener: function(e, t, n) { + this.listenersToPut.push({ + rootNodeID: e, + propKey: t, + propValue: n + }) + }, + putListeners: function() { + for (var e = 0; e < this.listenersToPut.length; e++) { + var t = this.listenersToPut[e]; + o.putListener(t.rootNodeID, t.propKey, t.propValue) + } + }, + reset: function() { + this.listenersToPut.length = 0 + }, + destructor: function() { + this.reset() + } + }), r.addPoolingTo(n), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39 + }], + 94: [function(e, t) { + "use strict"; + + function n() { + this.reinitializeTransaction(), this.renderToStaticMarkup = !1, this.reactMountReady = r.getPooled(null), this.putListenerQueue = s.getPooled() + } + var r = e("./CallbackQueue"), + o = e("./PooledClass"), + a = e("./ReactBrowserEventEmitter"), + i = e("./ReactInputSelection"), + s = e("./ReactPutListenerQueue"), + u = e("./Transaction"), + c = e("./Object.assign"), + l = { + initialize: i.getSelectionInformation, + close: i.restoreSelection + }, + p = { + initialize: function() { + var e = a.isEnabled(); + return a.setEnabled(!1), e + }, + close: function(e) { + a.setEnabled(e) + } + }, + d = { + initialize: function() { + this.reactMountReady.reset() + }, + close: function() { + this.reactMountReady.notifyAll() + } + }, + f = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: function() { + this.putListenerQueue.putListeners() + } + }, + h = [f, l, p, d], + v = { + getTransactionWrappers: function() { + return h + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + r.release(this.reactMountReady), this.reactMountReady = null, s.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + c(n.prototype, u.Mixin, v), o.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactBrowserEventEmitter": 39, + "./ReactInputSelection": 77, + "./ReactPutListenerQueue": 93, + "./Transaction": 123 + }], + 95: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + o.attachRefs(this, this._currentElement) + } + var o = e("./ReactRef"), + a = e("./ReactElementValidator"), + i = { + mountComponent: function(e, t, o, i) { + var s = e.mountComponent(t, o, i); + return "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(e._currentElement), o.getReactMountReady().enqueue(r, e), s + }, + unmountComponent: function(e) { + o.detachRefs(e, e._currentElement), e.unmountComponent() + }, + receiveComponent: function(e, t, i, s) { + var u = e._currentElement; + if (t !== u || null == t._owner) { + "production" !== n.env.NODE_ENV && a.checkAndWarnForMutatedProps(t); + var c = o.shouldUpdateRefs(u, t); + c && o.detachRefs(e, u), e.receiveComponent(t, i, s), c && i.getReactMountReady().enqueue(r, e) + } + }, + performUpdateIfNecessary: function(e, t) { + e.performUpdateIfNecessary(t) + } + }; + t.exports = i + }).call(this, e("_process")) + }, { + "./ReactElementValidator": 70, + "./ReactRef": 96, + _process: 2 + }], + 96: [function(e, t) { + "use strict"; + + function n(e, t, n) { + "function" == typeof e ? e(t.getPublicInstance()) : o.addComponentAsRefTo(t, e, n) + } + + function r(e, t, n) { + "function" == typeof e ? e(null) : o.removeComponentAsRefFrom(t, e, n) + } + var o = e("./ReactOwner"), + a = {}; + a.attachRefs = function(e, t) { + var r = t.ref; + null != r && n(r, e, t._owner) + }, a.shouldUpdateRefs = function(e, t) { + return t._owner !== e._owner || t.ref !== e.ref + }, a.detachRefs = function(e, t) { + var n = t.ref; + null != n && r(n, e, t._owner) + }, t.exports = a + }, { + "./ReactOwner": 87 + }], + 97: [function(e, t) { + "use strict"; + var n = { + injectCreateReactRootIndex: function(e) { + r.createReactRootIndex = e + } + }, + r = { + createReactRootIndex: null, + injection: n + }; + t.exports = r + }, {}], + 98: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToString(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!1), t.perform(function() { + var n = l(e, null), + o = n.mountComponent(r, t, c); + return s.addChecksumToMarkup(o) + }, null) + } finally { + u.release(t) + } + } + + function o(e) { + "production" !== n.env.NODE_ENV ? p(a.isValidElement(e), "renderToStaticMarkup(): You must pass a valid ReactElement.") : p(a.isValidElement(e)); + var t; + try { + var r = i.createReactRootID(); + return t = u.getPooled(!0), t.perform(function() { + var n = l(e, null); + return n.mountComponent(r, t, c) + }, null) + } finally { + u.release(t) + } + } + var a = e("./ReactElement"), + i = e("./ReactInstanceHandles"), + s = e("./ReactMarkupChecksum"), + u = e("./ReactServerRenderingTransaction"), + c = e("./emptyObject"), + l = e("./instantiateReactComponent"), + p = e("./invariant"); + t.exports = { + renderToString: r, + renderToStaticMarkup: o + } + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactInstanceHandles": 78, + "./ReactMarkupChecksum": 82, + "./ReactServerRenderingTransaction": 99, + "./emptyObject": 137, + "./instantiateReactComponent": 156, + "./invariant": 157, + _process: 2 + }], + 99: [function(e, t) { + "use strict"; + + function n(e) { + this.reinitializeTransaction(), this.renderToStaticMarkup = e, this.reactMountReady = o.getPooled(null), this.putListenerQueue = a.getPooled() + } + var r = e("./PooledClass"), + o = e("./CallbackQueue"), + a = e("./ReactPutListenerQueue"), + i = e("./Transaction"), + s = e("./Object.assign"), + u = e("./emptyFunction"), + c = { + initialize: function() { + this.reactMountReady.reset() + }, + close: u + }, + l = { + initialize: function() { + this.putListenerQueue.reset() + }, + close: u + }, + p = [l, c], + d = { + getTransactionWrappers: function() { + return p + }, + getReactMountReady: function() { + return this.reactMountReady + }, + getPutListenerQueue: function() { + return this.putListenerQueue + }, + destructor: function() { + o.release(this.reactMountReady), this.reactMountReady = null, a.release(this.putListenerQueue), this.putListenerQueue = null + } + }; + s(n.prototype, i.Mixin, d), r.addPoolingTo(n), t.exports = n + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactPutListenerQueue": 93, + "./Transaction": 123, + "./emptyFunction": 136 + }], + 100: [function(e, t) { + "use strict"; + + function n(e, t) { + var n = {}; + return function(r) { + n[t] = r, e.setState(n) + } + } + var r = { + createStateSetter: function(e, t) { + return function(n, r, o, a, i, s) { + var u = t.call(e, n, r, o, a, i, s); + u && e.setState(u) + } + }, + createStateKeySetter: function(e, t) { + var r = e.__keySetters || (e.__keySetters = {}); + return r[t] || (r[t] = n(e, t)) + } + }; + r.Mixin = { + createStateSetter: function(e) { + return r.createStateSetter(this, e) + }, + createStateKeySetter: function(e) { + return r.createStateKeySetter(this, e) + } + }, t.exports = r + }, {}], + 101: [function(e, t) { + "use strict"; + + function n() {} + + function r(e) { + return function(t, r) { + var o; + b.isDOMComponent(t) ? o = t.getDOMNode() : t.tagName && (o = t); + var a = new n; + a.target = o; + var i = new g(d.eventNameDispatchConfigs[e], m.getID(o), a); + E(i, r), u.accumulateTwoPhaseDispatches(i), y.batchedUpdates(function() { + s.enqueueEvents(i), s.processEventQueue() + }) + } + } + + function o() { + b.Simulate = {}; + var e; + for (e in d.eventNameDispatchConfigs) b.Simulate[e] = r(e) + } + + function a(e) { + return function(t, r) { + var o = new n(e); + E(o, r), b.isDOMComponent(t) ? b.simulateNativeEventOnDOMComponent(e, t, o) : t.tagName && b.simulateNativeEventOnNode(e, t, o) + } + } + var i = e("./EventConstants"), + s = e("./EventPluginHub"), + u = e("./EventPropagators"), + c = e("./React"), + l = e("./ReactElement"), + p = e("./ReactEmptyComponent"), + d = e("./ReactBrowserEventEmitter"), + f = e("./ReactCompositeComponent"), + h = e("./ReactInstanceHandles"), + v = e("./ReactInstanceMap"), + m = e("./ReactMount"), + y = e("./ReactUpdates"), + g = e("./SyntheticEvent"), + E = e("./Object.assign"), + C = i.topLevelTypes, + b = { + renderIntoDocument: function(e) { + var t = document.createElement("div"); + return c.render(e, t) + }, + isElement: function(e) { + return l.isValidElement(e) + }, + isElementOfType: function(e, t) { + return l.isValidElement(e) && e.type === t + }, + isDOMComponent: function(e) { + return !!(e && e.tagName && e.getDOMNode) + }, + isDOMComponentElement: function(e) { + return !!(e && l.isValidElement(e) && e.tagName) + }, + isCompositeComponent: function(e) { + return "function" == typeof e.render && "function" == typeof e.setState + }, + isCompositeComponentWithType: function(e, t) { + return !(!b.isCompositeComponent(e) || e.constructor !== t) + }, + isCompositeComponentElement: function(e) { + if (!l.isValidElement(e)) return !1; + var t = e.type.prototype; + return "function" == typeof t.render && "function" == typeof t.setState + }, + isCompositeComponentElementWithType: function(e, t) { + return !(!b.isCompositeComponentElement(e) || e.constructor !== t) + }, + getRenderedChildOfCompositeComponent: function(e) { + if (!b.isCompositeComponent(e)) return null; + var t = v.get(e); + return t._renderedComponent.getPublicInstance() + }, + findAllInRenderedTree: function(e, t) { + if (!e) return []; + var n = t(e) ? [e] : []; + if (b.isDOMComponent(e)) { + var r, o = v.get(e), + a = o._renderedComponent._renderedChildren; + for (r in a) a.hasOwnProperty(r) && a[r].getPublicInstance && (n = n.concat(b.findAllInRenderedTree(a[r].getPublicInstance(), t))) + } else b.isCompositeComponent(e) && (n = n.concat(b.findAllInRenderedTree(b.getRenderedChildOfCompositeComponent(e), t))); + return n + }, + scryRenderedDOMComponentsWithClass: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + var n = e.props.className; + return b.isDOMComponent(e) && n && -1 !== (" " + n + " ").indexOf(" " + t + " ") + }) + }, + findRenderedDOMComponentWithClass: function(e, t) { + var n = b.scryRenderedDOMComponentsWithClass(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match (found: " + n.length + ") for class:" + t); + return n[0] + }, + scryRenderedDOMComponentsWithTag: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isDOMComponent(e) && e.tagName === t.toUpperCase() + }) + }, + findRenderedDOMComponentWithTag: function(e, t) { + var n = b.scryRenderedDOMComponentsWithTag(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for tag:" + t); + return n[0] + }, + scryRenderedComponentsWithType: function(e, t) { + return b.findAllInRenderedTree(e, function(e) { + return b.isCompositeComponentWithType(e, t) + }) + }, + findRenderedComponentWithType: function(e, t) { + var n = b.scryRenderedComponentsWithType(e, t); + if (1 !== n.length) throw new Error("Did not find exactly one match for componentType:" + t); + return n[0] + }, + mockComponent: function(e, t) { + return t = t || e.mockTagName || "div", e.prototype.render.mockImplementation(function() { + return c.createElement(t, null, this.props.children) + }), this + }, + simulateNativeEventOnNode: function(e, t, n) { + n.target = t, d.ReactEventListener.dispatchEvent(e, n) + }, + simulateNativeEventOnDOMComponent: function(e, t, n) { + b.simulateNativeEventOnNode(e, t.getDOMNode(), n) + }, + nativeTouchData: function(e, t) { + return { + touches: [{ + pageX: e, + pageY: t + }] + } + }, + createRenderer: function() { + return new _ + }, + Simulate: null, + SimulateNative: {} + }, + _ = function() { + this._instance = null + }; + _.prototype.getRenderOutput = function() { + return this._instance && this._instance._renderedComponent && this._instance._renderedComponent._renderedOutput || null + }; + var N = function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }; + N.prototype = { + mountComponent: function() {}, + receiveComponent: function(e) { + this._renderedOutput = e, this._currentElement = null === e || e === !1 ? p.emptyElement : e + }, + unmountComponent: function() {} + }; + var O = function() {}; + E(O.prototype, f.Mixin, { + _instantiateReactComponent: function(e) { + return new N(e) + }, + _replaceNodeWithMarkupByID: function() {}, + _renderValidatedComponent: f.Mixin._renderValidatedComponentWithoutOwnerOrContext + }), _.prototype.render = function(e, t) { + var n = y.ReactReconcileTransaction.getPooled(); + this._render(e, n, t), y.ReactReconcileTransaction.release(n) + }, _.prototype.unmount = function() { + this._instance && this._instance.unmountComponent() + }, _.prototype._render = function(e, t, n) { + if (this._instance) this._instance.receiveComponent(e, t, n); + else { + var r = h.createReactRootID(), + o = new O(e.type); + o.construct(e), o.mountComponent(r, t, n), this._instance = o + } + }; + var R = s.injection.injectEventPluginOrder; + s.injection.injectEventPluginOrder = function() { + R.apply(this, arguments), o() + }; + var D = s.injection.injectEventPluginsByName; + s.injection.injectEventPluginsByName = function() { + D.apply(this, arguments), o() + }, o(); + var w; + for (w in C) { + var M = 0 === w.indexOf("top") ? w.charAt(3).toLowerCase() + w.substr(4) : w; + b.SimulateNative[M] = a(w) + } + t.exports = b + }, { + "./EventConstants": 22, + "./EventPluginHub": 24, + "./EventPropagators": 27, + "./Object.assign": 35, + "./React": 37, + "./ReactBrowserEventEmitter": 39, + "./ReactCompositeComponent": 49, + "./ReactElement": 69, + "./ReactEmptyComponent": 71, + "./ReactInstanceHandles": 78, + "./ReactInstanceMap": 79, + "./ReactMount": 83, + "./ReactUpdates": 106, + "./SyntheticEvent": 115 + }], + 102: [function(e, t) { + "use strict"; + var n = e("./ReactChildren"), + r = e("./ReactFragment"), + o = { + getChildMapping: function(e) { + return e ? r.extract(n.map(e, function(e) { + return e + })) : e + }, + mergeChildMappings: function(e, t) { + function n(n) { + return t.hasOwnProperty(n) ? t[n] : e[n] + } + e = e || {}, t = t || {}; + var r = {}, + o = []; + for (var a in e) t.hasOwnProperty(a) ? o.length && (r[a] = o, o = []) : o.push(a); + var i, s = {}; + for (var u in t) { + if (r.hasOwnProperty(u)) + for (i = 0; i < r[u].length; i++) { + var c = r[u][i]; + s[r[u][i]] = n(c) + } + s[u] = n(u) + } + for (i = 0; i < o.length; i++) s[o[i]] = n(o[i]); + return s + } + }; + t.exports = o + }, { + "./ReactChildren": 43, + "./ReactFragment": 75 + }], + 103: [function(e, t) { + "use strict"; + + function n() { + var e = document.createElement("div"), + t = e.style; + "AnimationEvent" in window || delete i.animationend.animation, "TransitionEvent" in window || delete i.transitionend.transition; + for (var n in i) { + var r = i[n]; + for (var o in r) + if (o in t) { + s.push(r[o]); + break + } + } + } + + function r(e, t, n) { + e.addEventListener(t, n, !1) + } + + function o(e, t, n) { + e.removeEventListener(t, n, !1) + } + var a = e("./ExecutionEnvironment"), + i = { + transitionend: { + transition: "transitionend", + WebkitTransition: "webkitTransitionEnd", + MozTransition: "mozTransitionEnd", + OTransition: "oTransitionEnd", + msTransition: "MSTransitionEnd" + }, + animationend: { + animation: "animationend", + WebkitAnimation: "webkitAnimationEnd", + MozAnimation: "mozAnimationEnd", + OAnimation: "oAnimationEnd", + msAnimation: "MSAnimationEnd" + } + }, + s = []; + a.canUseDOM && n(); + var u = { + addEndEventListener: function(e, t) { + return 0 === s.length ? void window.setTimeout(t, 0) : void s.forEach(function(n) { + r(e, n, t) + }) + }, + removeEndEventListener: function(e, t) { + 0 !== s.length && s.forEach(function(n) { + o(e, n, t) + }) + } + }; + t.exports = u + }, { + "./ExecutionEnvironment": 28 + }], + 104: [function(e, t) { + "use strict"; + var n = e("./React"), + r = e("./ReactTransitionChildMapping"), + o = e("./Object.assign"), + a = e("./cloneWithProps"), + i = e("./emptyFunction"), + s = n.createClass({ + displayName: "ReactTransitionGroup", + propTypes: { + component: n.PropTypes.any, + childFactory: n.PropTypes.func + }, + getDefaultProps: function() { + return { + component: "span", + childFactory: i.thatReturnsArgument + } + }, + getInitialState: function() { + return { + children: r.getChildMapping(this.props.children) + } + }, + componentWillMount: function() { + this.currentlyTransitioningKeys = {}, this.keysToEnter = [], this.keysToLeave = [] + }, + componentDidMount: function() { + var e = this.state.children; + for (var t in e) e[t] && this.performAppear(t) + }, + componentWillReceiveProps: function(e) { + var t = r.getChildMapping(e.children), + n = this.state.children; + this.setState({ + children: r.mergeChildMappings(n, t) + }); + var o; + for (o in t) { + var a = n && n.hasOwnProperty(o); + !t[o] || a || this.currentlyTransitioningKeys[o] || this.keysToEnter.push(o) + } + for (o in n) { + var i = t && t.hasOwnProperty(o); + !n[o] || i || this.currentlyTransitioningKeys[o] || this.keysToLeave.push(o) + } + }, + componentDidUpdate: function() { + var e = this.keysToEnter; + this.keysToEnter = [], e.forEach(this.performEnter); + var t = this.keysToLeave; + this.keysToLeave = [], t.forEach(this.performLeave) + }, + performAppear: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillAppear ? t.componentWillAppear(this._handleDoneAppearing.bind(this, e)) : this._handleDoneAppearing(e) + }, + _handleDoneAppearing: function(e) { + var t = this.refs[e]; + t.componentDidAppear && t.componentDidAppear(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performEnter: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillEnter ? t.componentWillEnter(this._handleDoneEntering.bind(this, e)) : this._handleDoneEntering(e) + }, + _handleDoneEntering: function(e) { + var t = this.refs[e]; + t.componentDidEnter && t.componentDidEnter(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + n && n.hasOwnProperty(e) || this.performLeave(e) + }, + performLeave: function(e) { + this.currentlyTransitioningKeys[e] = !0; + var t = this.refs[e]; + t.componentWillLeave ? t.componentWillLeave(this._handleDoneLeaving.bind(this, e)) : this._handleDoneLeaving(e) + }, + _handleDoneLeaving: function(e) { + var t = this.refs[e]; + t.componentDidLeave && t.componentDidLeave(), delete this.currentlyTransitioningKeys[e]; + var n = r.getChildMapping(this.props.children); + if (n && n.hasOwnProperty(e)) this.performEnter(e); + else { + var a = o({}, this.state.children); + delete a[e], this.setState({ + children: a + }) + } + }, + render: function() { + var e = []; + for (var t in this.state.children) { + var r = this.state.children[t]; + r && e.push(a(this.props.childFactory(r), { + ref: t, + key: t + })) + } + return n.createElement(this.props.component, this.props, e) + } + }); + t.exports = s + }, { + "./Object.assign": 35, + "./React": 37, + "./ReactTransitionChildMapping": 102, + "./cloneWithProps": 129, + "./emptyFunction": 136 + }], + 105: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + e !== a.currentlyMountingInstance && c.enqueueUpdate(e) + } + + function o(e, t) { + "production" !== n.env.NODE_ENV ? p(null == i.current, "%s(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.", t) : p(null == i.current); + var r = u.get(e); + return r ? r === a.currentlyUnmountingInstance ? null : r : ("production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? d(!t, "%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op.", t, t) : null), null) + } + var a = e("./ReactLifeCycle"), + i = e("./ReactCurrentOwner"), + s = e("./ReactElement"), + u = e("./ReactInstanceMap"), + c = e("./ReactUpdates"), + l = e("./Object.assign"), + p = e("./invariant"), + d = e("./warning"), + f = { + enqueueCallback: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t); + var i = o(e); + return i && i !== a.currentlyMountingInstance ? (i._pendingCallbacks ? i._pendingCallbacks.push(t) : i._pendingCallbacks = [t], void r(i)) : null + }, + enqueueCallbackInternal: function(e, t) { + "production" !== n.env.NODE_ENV ? p("function" == typeof t, "enqueueCallback(...): You called `setProps`, `replaceProps`, `setState`, `replaceState`, or `forceUpdate` with a callback that isn't callable.") : p("function" == typeof t), e._pendingCallbacks ? e._pendingCallbacks.push(t) : e._pendingCallbacks = [t], r(e) + }, + enqueueForceUpdate: function(e) { + var t = o(e, "forceUpdate"); + t && (t._pendingForceUpdate = !0, r(t)) + }, + enqueueReplaceState: function(e, t) { + var n = o(e, "replaceState"); + n && (n._pendingStateQueue = [t], n._pendingReplaceState = !0, r(n)) + }, + enqueueSetState: function(e, t) { + var n = o(e, "setState"); + if (n) { + var a = n._pendingStateQueue || (n._pendingStateQueue = []); + a.push(t), r(n) + } + }, + enqueueSetProps: function(e, t) { + var a = o(e, "setProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "setProps(...): You called `setProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement, + u = l({}, i.props, t); + a._pendingElement = s.cloneAndReplaceProps(i, u), r(a) + } + }, + enqueueReplaceProps: function(e, t) { + var a = o(e, "replaceProps"); + if (a) { + "production" !== n.env.NODE_ENV ? p(a._isTopLevel, "replaceProps(...): You called `replaceProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.") : p(a._isTopLevel); + var i = a._pendingElement || a._currentElement; + a._pendingElement = s.cloneAndReplaceProps(i, t), r(a) + } + }, + enqueueElementInternal: function(e, t) { + e._pendingElement = t, r(e) + } + }; + t.exports = f + }).call(this, e("_process")) + }, { + "./Object.assign": 35, + "./ReactCurrentOwner": 51, + "./ReactElement": 69, + "./ReactInstanceMap": 79, + "./ReactLifeCycle": 80, + "./ReactUpdates": 106, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 106: [function(e, t) { + (function(n) { + "use strict"; + + function r() { + "production" !== n.env.NODE_ENV ? y(M.ReactReconcileTransaction && _, "ReactUpdates: must inject a reconcile transaction class and batching strategy") : y(M.ReactReconcileTransaction && _) + } + + function o() { + this.reinitializeTransaction(), this.dirtyComponentsLength = null, this.callbackQueue = l.getPooled(), this.reconcileTransaction = M.ReactReconcileTransaction.getPooled() + } + + function a(e, t, n, o, a) { + r(), _.batchedUpdates(e, t, n, o, a) + } + + function i(e, t) { + return e._mountOrder - t._mountOrder + } + + function s(e) { + var t = e.dirtyComponentsLength; + "production" !== n.env.NODE_ENV ? y(t === E.length, "Expected flush transaction's stored dirty-components length (%s) to match dirty-components array length (%s).", t, E.length) : y(t === E.length), E.sort(i); + for (var r = 0; t > r; r++) { + var o = E[r], + a = o._pendingCallbacks; + if (o._pendingCallbacks = null, h.performUpdateIfNecessary(o, e.reconcileTransaction), a) + for (var s = 0; s < a.length; s++) e.callbackQueue.enqueue(a[s], o.getPublicInstance()) + } + } + + function u(e) { + return r(), "production" !== n.env.NODE_ENV ? g(null == d.current, "enqueueUpdate(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.") : null, _.isBatchingUpdates ? void E.push(e) : void _.batchedUpdates(u, e) + } + + function c(e, t) { + "production" !== n.env.NODE_ENV ? y(_.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched.") : y(_.isBatchingUpdates), C.enqueue(e, t), b = !0 + } + var l = e("./CallbackQueue"), + p = e("./PooledClass"), + d = e("./ReactCurrentOwner"), + f = e("./ReactPerf"), + h = e("./ReactReconciler"), + v = e("./Transaction"), + m = e("./Object.assign"), + y = e("./invariant"), + g = e("./warning"), + E = [], + C = l.getPooled(), + b = !1, + _ = null, + N = { + initialize: function() { + this.dirtyComponentsLength = E.length + }, + close: function() { + this.dirtyComponentsLength !== E.length ? (E.splice(0, this.dirtyComponentsLength), D()) : E.length = 0 + } + }, + O = { + initialize: function() { + this.callbackQueue.reset() + }, + close: function() { + this.callbackQueue.notifyAll() + } + }, + R = [N, O]; + m(o.prototype, v.Mixin, { + getTransactionWrappers: function() { + return R + }, + destructor: function() { + this.dirtyComponentsLength = null, l.release(this.callbackQueue), this.callbackQueue = null, M.ReactReconcileTransaction.release(this.reconcileTransaction), this.reconcileTransaction = null + }, + perform: function(e, t, n) { + return v.Mixin.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, e, t, n) + } + }), p.addPoolingTo(o); + var D = function() { + for (; E.length || b;) { + if (E.length) { + var e = o.getPooled(); + e.perform(s, null, e), o.release(e) + } + if (b) { + b = !1; + var t = C; + C = l.getPooled(), t.notifyAll(), l.release(t) + } + } + }; + D = f.measure("ReactUpdates", "flushBatchedUpdates", D); + var w = { + injectReconcileTransaction: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a reconcile transaction class") : y(e), M.ReactReconcileTransaction = e + }, + injectBatchingStrategy: function(e) { + "production" !== n.env.NODE_ENV ? y(e, "ReactUpdates: must provide a batching strategy") : y(e), "production" !== n.env.NODE_ENV ? y("function" == typeof e.batchedUpdates, "ReactUpdates: must provide a batchedUpdates() function") : y("function" == typeof e.batchedUpdates), "production" !== n.env.NODE_ENV ? y("boolean" == typeof e.isBatchingUpdates, "ReactUpdates: must provide an isBatchingUpdates boolean attribute") : y("boolean" == typeof e.isBatchingUpdates), _ = e + } + }, + M = { + ReactReconcileTransaction: null, + batchedUpdates: a, + enqueueUpdate: u, + flushBatchedUpdates: D, + injection: w, + asap: c + }; + t.exports = M + }).call(this, e("_process")) + }, { + "./CallbackQueue": 13, + "./Object.assign": 35, + "./PooledClass": 36, + "./ReactCurrentOwner": 51, + "./ReactPerf": 88, + "./ReactReconciler": 95, + "./Transaction": 123, + "./invariant": 157, + "./warning": 178, + _process: 2 + }], + 107: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./LinkedStateMixin"), + o = e("./React"), + a = e("./ReactComponentWithPureRenderMixin"), + i = e("./ReactCSSTransitionGroup"), + s = e("./ReactFragment"), + u = e("./ReactTransitionGroup"), + c = e("./ReactUpdates"), + l = e("./cx"), + p = e("./cloneWithProps"), + d = e("./update"); + o.addons = { + CSSTransitionGroup: i, + LinkedStateMixin: r, + PureRenderMixin: a, + TransitionGroup: u, + batchedUpdates: c.batchedUpdates, + classSet: l, + cloneWithProps: p, + createFragment: s.create, + update: d + }, "production" !== n.env.NODE_ENV && (o.addons.Perf = e("./ReactDefaultPerf"), o.addons.TestUtils = e("./ReactTestUtils")), t.exports = o + }).call(this, e("_process")) + }, { + "./LinkedStateMixin": 31, + "./React": 37, + "./ReactCSSTransitionGroup": 40, + "./ReactComponentWithPureRenderMixin": 48, + "./ReactDefaultPerf": 67, + "./ReactFragment": 75, + "./ReactTestUtils": 101, + "./ReactTransitionGroup": 104, + "./ReactUpdates": 106, + "./cloneWithProps": 129, + "./cx": 134, + "./update": 177, + _process: 2 + }], + 108: [function(e, t) { + "use strict"; + var n = e("./DOMProperty"), + r = n.injection.MUST_USE_ATTRIBUTE, + o = { + Properties: { + cx: r, + cy: r, + d: r, + dx: r, + dy: r, + fill: r, + fillOpacity: r, + fontFamily: r, + fontSize: r, + fx: r, + fy: r, + gradientTransform: r, + gradientUnits: r, + markerEnd: r, + markerMid: r, + markerStart: r, + offset: r, + opacity: r, + patternContentUnits: r, + patternUnits: r, + points: r, + preserveAspectRatio: r, + r: r, + rx: r, + ry: r, + spreadMethod: r, + stopColor: r, + stopOpacity: r, + stroke: r, + strokeDasharray: r, + strokeLinecap: r, + strokeOpacity: r, + strokeWidth: r, + textAnchor: r, + transform: r, + version: r, + viewBox: r, + x1: r, + x2: r, + x: r, + y1: r, + y2: r, + y: r + }, + DOMAttributeNames: { + fillOpacity: "fill-opacity", + fontFamily: "font-family", + fontSize: "font-size", + gradientTransform: "gradientTransform", + gradientUnits: "gradientUnits", + markerEnd: "marker-end", + markerMid: "marker-mid", + markerStart: "marker-start", + patternContentUnits: "patternContentUnits", + patternUnits: "patternUnits", + preserveAspectRatio: "preserveAspectRatio", + spreadMethod: "spreadMethod", + stopColor: "stop-color", + stopOpacity: "stop-opacity", + strokeDasharray: "stroke-dasharray", + strokeLinecap: "stroke-linecap", + strokeOpacity: "stroke-opacity", + strokeWidth: "stroke-width", + textAnchor: "text-anchor", + viewBox: "viewBox" + } + }; + t.exports = o + }, { + "./DOMProperty": 17 + }], + 109: [function(e, t) { + "use strict"; + + function n(e) { + if ("selectionStart" in e && i.hasSelectionCapabilities(e)) return { + start: e.selectionStart, + end: e.selectionEnd + }; + if (window.getSelection) { + var t = window.getSelection(); + return { + anchorNode: t.anchorNode, + anchorOffset: t.anchorOffset, + focusNode: t.focusNode, + focusOffset: t.focusOffset + } + } + if (document.selection) { + var n = document.selection.createRange(); + return { + parentElement: n.parentElement(), + text: n.text, + top: n.boundingTop, + left: n.boundingLeft + } + } + } + + function r(e) { + if (y || null == h || h !== u()) return null; + var t = n(h); + if (!m || !p(m, t)) { + m = t; + var r = s.getPooled(f.select, v, e); + return r.type = "select", r.target = h, a.accumulateTwoPhaseDispatches(r), r + } + } + var o = e("./EventConstants"), + a = e("./EventPropagators"), + i = e("./ReactInputSelection"), + s = e("./SyntheticEvent"), + u = e("./getActiveElement"), + c = e("./isTextInputElement"), + l = e("./keyOf"), + p = e("./shallowEqual"), + d = o.topLevelTypes, + f = { + select: { + phasedRegistrationNames: { + bubbled: l({ + onSelect: null + }), + captured: l({ + onSelectCapture: null + }) + }, + dependencies: [d.topBlur, d.topContextMenu, d.topFocus, d.topKeyDown, d.topMouseDown, d.topMouseUp, d.topSelectionChange] + } + }, + h = null, + v = null, + m = null, + y = !1, + g = { + eventTypes: f, + extractEvents: function(e, t, n, o) { + switch (e) { + case d.topFocus: + (c(t) || "true" === t.contentEditable) && (h = t, v = n, m = null); + break; + case d.topBlur: + h = null, v = null, m = null; + break; + case d.topMouseDown: + y = !0; + break; + case d.topContextMenu: + case d.topMouseUp: + return y = !1, r(o); + case d.topSelectionChange: + case d.topKeyDown: + case d.topKeyUp: + return r(o) + } + } + }; + t.exports = g + }, { + "./EventConstants": 22, + "./EventPropagators": 27, + "./ReactInputSelection": 77, + "./SyntheticEvent": 115, + "./getActiveElement": 143, + "./isTextInputElement": 160, + "./keyOf": 164, + "./shallowEqual": 173 + }], + 110: [function(e, t) { + "use strict"; + var n = Math.pow(2, 53), + r = { + createReactRootIndex: function() { + return Math.ceil(Math.random() * n) + } + }; + t.exports = r + }, {}], + 111: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./EventConstants"), + o = e("./EventPluginUtils"), + a = e("./EventPropagators"), + i = e("./SyntheticClipboardEvent"), + s = e("./SyntheticEvent"), + u = e("./SyntheticFocusEvent"), + c = e("./SyntheticKeyboardEvent"), + l = e("./SyntheticMouseEvent"), + p = e("./SyntheticDragEvent"), + d = e("./SyntheticTouchEvent"), + f = e("./SyntheticUIEvent"), + h = e("./SyntheticWheelEvent"), + v = e("./getEventCharCode"), + m = e("./invariant"), + y = e("./keyOf"), + g = e("./warning"), + E = r.topLevelTypes, + C = { + blur: { + phasedRegistrationNames: { + bubbled: y({ + onBlur: !0 + }), + captured: y({ + onBlurCapture: !0 + }) + } + }, + click: { + phasedRegistrationNames: { + bubbled: y({ + onClick: !0 + }), + captured: y({ + onClickCapture: !0 + }) + } + }, + contextMenu: { + phasedRegistrationNames: { + bubbled: y({ + onContextMenu: !0 + }), + captured: y({ + onContextMenuCapture: !0 + }) + } + }, + copy: { + phasedRegistrationNames: { + bubbled: y({ + onCopy: !0 + }), + captured: y({ + onCopyCapture: !0 + }) + } + }, + cut: { + phasedRegistrationNames: { + bubbled: y({ + onCut: !0 + }), + captured: y({ + onCutCapture: !0 + }) + } + }, + doubleClick: { + phasedRegistrationNames: { + bubbled: y({ + onDoubleClick: !0 + }), + captured: y({ + onDoubleClickCapture: !0 + }) + } + }, + drag: { + phasedRegistrationNames: { + bubbled: y({ + onDrag: !0 + }), + captured: y({ + onDragCapture: !0 + }) + } + }, + dragEnd: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnd: !0 + }), + captured: y({ + onDragEndCapture: !0 + }) + } + }, + dragEnter: { + phasedRegistrationNames: { + bubbled: y({ + onDragEnter: !0 + }), + captured: y({ + onDragEnterCapture: !0 + }) + } + }, + dragExit: { + phasedRegistrationNames: { + bubbled: y({ + onDragExit: !0 + }), + captured: y({ + onDragExitCapture: !0 + }) + } + }, + dragLeave: { + phasedRegistrationNames: { + bubbled: y({ + onDragLeave: !0 + }), + captured: y({ + onDragLeaveCapture: !0 + }) + } + }, + dragOver: { + phasedRegistrationNames: { + bubbled: y({ + onDragOver: !0 + }), + captured: y({ + onDragOverCapture: !0 + }) + } + }, + dragStart: { + phasedRegistrationNames: { + bubbled: y({ + onDragStart: !0 + }), + captured: y({ + onDragStartCapture: !0 + }) + } + }, + drop: { + phasedRegistrationNames: { + bubbled: y({ + onDrop: !0 + }), + captured: y({ + onDropCapture: !0 + }) + } + }, + focus: { + phasedRegistrationNames: { + bubbled: y({ + onFocus: !0 + }), + captured: y({ + onFocusCapture: !0 + }) + } + }, + input: { + phasedRegistrationNames: { + bubbled: y({ + onInput: !0 + }), + captured: y({ + onInputCapture: !0 + }) + } + }, + keyDown: { + phasedRegistrationNames: { + bubbled: y({ + onKeyDown: !0 + }), + captured: y({ + onKeyDownCapture: !0 + }) + } + }, + keyPress: { + phasedRegistrationNames: { + bubbled: y({ + onKeyPress: !0 + }), + captured: y({ + onKeyPressCapture: !0 + }) + } + }, + keyUp: { + phasedRegistrationNames: { + bubbled: y({ + onKeyUp: !0 + }), + captured: y({ + onKeyUpCapture: !0 + }) + } + }, + load: { + phasedRegistrationNames: { + bubbled: y({ + onLoad: !0 + }), + captured: y({ + onLoadCapture: !0 + }) + } + }, + error: { + phasedRegistrationNames: { + bubbled: y({ + onError: !0 + }), + captured: y({ + onErrorCapture: !0 + }) + } + }, + mouseDown: { + phasedRegistrationNames: { + bubbled: y({ + onMouseDown: !0 + }), + captured: y({ + onMouseDownCapture: !0 + }) + } + }, + mouseMove: { + phasedRegistrationNames: { + bubbled: y({ + onMouseMove: !0 + }), + captured: y({ + onMouseMoveCapture: !0 + }) + } + }, + mouseOut: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOut: !0 + }), + captured: y({ + onMouseOutCapture: !0 + }) + } + }, + mouseOver: { + phasedRegistrationNames: { + bubbled: y({ + onMouseOver: !0 + }), + captured: y({ + onMouseOverCapture: !0 + }) + } + }, + mouseUp: { + phasedRegistrationNames: { + bubbled: y({ + onMouseUp: !0 + }), + captured: y({ + onMouseUpCapture: !0 + }) + } + }, + paste: { + phasedRegistrationNames: { + bubbled: y({ + onPaste: !0 + }), + captured: y({ + onPasteCapture: !0 + }) + } + }, + reset: { + phasedRegistrationNames: { + bubbled: y({ + onReset: !0 + }), + captured: y({ + onResetCapture: !0 + }) + } + }, + scroll: { + phasedRegistrationNames: { + bubbled: y({ + onScroll: !0 + }), + captured: y({ + onScrollCapture: !0 + }) + } + }, + submit: { + phasedRegistrationNames: { + bubbled: y({ + onSubmit: !0 + }), + captured: y({ + onSubmitCapture: !0 + }) + } + }, + touchCancel: { + phasedRegistrationNames: { + bubbled: y({ + onTouchCancel: !0 + }), + captured: y({ + onTouchCancelCapture: !0 + }) + } + }, + touchEnd: { + phasedRegistrationNames: { + bubbled: y({ + onTouchEnd: !0 + }), + captured: y({ + onTouchEndCapture: !0 + }) + } + }, + touchMove: { + phasedRegistrationNames: { + bubbled: y({ + onTouchMove: !0 + }), + captured: y({ + onTouchMoveCapture: !0 + }) + } + }, + touchStart: { + phasedRegistrationNames: { + bubbled: y({ + onTouchStart: !0 + }), + captured: y({ + onTouchStartCapture: !0 + }) + } + }, + wheel: { + phasedRegistrationNames: { + bubbled: y({ + onWheel: !0 + }), + captured: y({ + onWheelCapture: !0 + }) + } + } + }, + b = { + topBlur: C.blur, + topClick: C.click, + topContextMenu: C.contextMenu, + topCopy: C.copy, + topCut: C.cut, + topDoubleClick: C.doubleClick, + topDrag: C.drag, + topDragEnd: C.dragEnd, + topDragEnter: C.dragEnter, + topDragExit: C.dragExit, + topDragLeave: C.dragLeave, + topDragOver: C.dragOver, + topDragStart: C.dragStart, + topDrop: C.drop, + topError: C.error, + topFocus: C.focus, + topInput: C.input, + topKeyDown: C.keyDown, + topKeyPress: C.keyPress, + topKeyUp: C.keyUp, + topLoad: C.load, + topMouseDown: C.mouseDown, + topMouseMove: C.mouseMove, + topMouseOut: C.mouseOut, + topMouseOver: C.mouseOver, + topMouseUp: C.mouseUp, + topPaste: C.paste, + topReset: C.reset, + topScroll: C.scroll, + topSubmit: C.submit, + topTouchCancel: C.touchCancel, + topTouchEnd: C.touchEnd, + topTouchMove: C.touchMove, + topTouchStart: C.touchStart, + topWheel: C.wheel + }; + for (var _ in b) b[_].dependencies = [_]; + var N = { + eventTypes: C, + executeDispatch: function(e, t, r) { + var a = o.executeDispatch(e, t, r); + "production" !== n.env.NODE_ENV ? g("boolean" != typeof a, "Returning `false` from an event handler is deprecated and will be ignored in a future release. Instead, manually call e.stopPropagation() or e.preventDefault(), as appropriate.") : null, a === !1 && (e.stopPropagation(), e.preventDefault()) + }, + extractEvents: function(e, t, r, o) { + var y = b[e]; + if (!y) return null; + var g; + switch (e) { + case E.topInput: + case E.topLoad: + case E.topError: + case E.topReset: + case E.topSubmit: + g = s; + break; + case E.topKeyPress: + if (0 === v(o)) return null; + case E.topKeyDown: + case E.topKeyUp: + g = c; + break; + case E.topBlur: + case E.topFocus: + g = u; + break; + case E.topClick: + if (2 === o.button) return null; + case E.topContextMenu: + case E.topDoubleClick: + case E.topMouseDown: + case E.topMouseMove: + case E.topMouseOut: + case E.topMouseOver: + case E.topMouseUp: + g = l; + break; + case E.topDrag: + case E.topDragEnd: + case E.topDragEnter: + case E.topDragExit: + case E.topDragLeave: + case E.topDragOver: + case E.topDragStart: + case E.topDrop: + g = p; + break; + case E.topTouchCancel: + case E.topTouchEnd: + case E.topTouchMove: + case E.topTouchStart: + g = d; + break; + case E.topScroll: + g = f; + break; + case E.topWheel: + g = h; + break; + case E.topCopy: + case E.topCut: + case E.topPaste: + g = i + } + "production" !== n.env.NODE_ENV ? m(g, "SimpleEventPlugin: Unhandled event type, `%s`.", e) : m(g); + var C = g.getPooled(y, r, o); + return a.accumulateTwoPhaseDispatches(C), C + } + }; + t.exports = N + }).call(this, e("_process")) + }, { + "./EventConstants": 22, + "./EventPluginUtils": 26, + "./EventPropagators": 27, + "./SyntheticClipboardEvent": 112, + "./SyntheticDragEvent": 114, + "./SyntheticEvent": 115, + "./SyntheticFocusEvent": 116, + "./SyntheticKeyboardEvent": 118, + "./SyntheticMouseEvent": 119, + "./SyntheticTouchEvent": 120, + "./SyntheticUIEvent": 121, + "./SyntheticWheelEvent": 122, + "./getEventCharCode": 144, + "./invariant": 157, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 112: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + clipboardData: function(e) { + return "clipboardData" in e ? e.clipboardData : window.clipboardData + } + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 113: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 114: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + dataTransfer: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 115: [function(e, t) { + "use strict"; + + function n(e, t, n) { + this.dispatchConfig = e, this.dispatchMarker = t, this.nativeEvent = n; + var r = this.constructor.Interface; + for (var o in r) + if (r.hasOwnProperty(o)) { + var i = r[o]; + this[o] = i ? i(n) : n[o] + } var s = null != n.defaultPrevented ? n.defaultPrevented : n.returnValue === !1; + this.isDefaultPrevented = s ? a.thatReturnsTrue : a.thatReturnsFalse, this.isPropagationStopped = a.thatReturnsFalse + } + var r = e("./PooledClass"), + o = e("./Object.assign"), + a = e("./emptyFunction"), + i = e("./getEventTarget"), + s = { + type: null, + target: i, + currentTarget: a.thatReturnsNull, + eventPhase: null, + bubbles: null, + cancelable: null, + timeStamp: function(e) { + return e.timeStamp || Date.now() + }, + defaultPrevented: null, + isTrusted: null + }; + o(n.prototype, { + preventDefault: function() { + this.defaultPrevented = !0; + var e = this.nativeEvent; + e.preventDefault ? e.preventDefault() : e.returnValue = !1, this.isDefaultPrevented = a.thatReturnsTrue + }, + stopPropagation: function() { + var e = this.nativeEvent; + e.stopPropagation ? e.stopPropagation() : e.cancelBubble = !0, this.isPropagationStopped = a.thatReturnsTrue + }, + persist: function() { + this.isPersistent = a.thatReturnsTrue + }, + isPersistent: a.thatReturnsFalse, + destructor: function() { + var e = this.constructor.Interface; + for (var t in e) this[t] = null; + this.dispatchConfig = null, this.dispatchMarker = null, this.nativeEvent = null + } + }), n.Interface = s, n.augmentClass = function(e, t) { + var n = this, + a = Object.create(n.prototype); + o(a, e.prototype), e.prototype = a, e.prototype.constructor = e, e.Interface = o({}, n.Interface, t), e.augmentClass = n.augmentClass, r.addPoolingTo(e, r.threeArgumentPooler) + }, r.addPoolingTo(n, r.threeArgumentPooler), t.exports = n + }, { + "./Object.assign": 35, + "./PooledClass": 36, + "./emptyFunction": 136, + "./getEventTarget": 147 + }], + 116: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = { + relatedTarget: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticUIEvent": 121 + }], + 117: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = { + data: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticEvent": 115 + }], + 118: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventCharCode"), + a = e("./getEventKey"), + i = e("./getEventModifierState"), + s = { + key: a, + location: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + repeat: null, + locale: null, + getModifierState: i, + charCode: function(e) { + return "keypress" === e.type ? o(e) : 0 + }, + keyCode: function(e) { + return "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + }, + which: function(e) { + return "keypress" === e.type ? o(e) : "keydown" === e.type || "keyup" === e.type ? e.keyCode : 0 + } + }; + r.augmentClass(n, s), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventCharCode": 144, + "./getEventKey": 145, + "./getEventModifierState": 146 + }], + 119: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./ViewportMetrics"), + a = e("./getEventModifierState"), + i = { + screenX: null, + screenY: null, + clientX: null, + clientY: null, + ctrlKey: null, + shiftKey: null, + altKey: null, + metaKey: null, + getModifierState: a, + button: function(e) { + var t = e.button; + return "which" in e ? t : 2 === t ? 2 : 4 === t ? 1 : 0 + }, + buttons: null, + relatedTarget: function(e) { + return e.relatedTarget || (e.fromElement === e.srcElement ? e.toElement : e.fromElement) + }, + pageX: function(e) { + return "pageX" in e ? e.pageX : e.clientX + o.currentScrollLeft + }, + pageY: function(e) { + return "pageY" in e ? e.pageY : e.clientY + o.currentScrollTop + } + }; + r.augmentClass(n, i), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./ViewportMetrics": 124, + "./getEventModifierState": 146 + }], + 120: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticUIEvent"), + o = e("./getEventModifierState"), + a = { + touches: null, + targetTouches: null, + changedTouches: null, + altKey: null, + metaKey: null, + ctrlKey: null, + shiftKey: null, + getModifierState: o + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticUIEvent": 121, + "./getEventModifierState": 146 + }], + 121: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticEvent"), + o = e("./getEventTarget"), + a = { + view: function(e) { + if (e.view) return e.view; + var t = o(e); + if (null != t && t.window === t) return t; + var n = t.ownerDocument; + return n ? n.defaultView || n.parentWindow : window + }, + detail: function(e) { + return e.detail || 0 + } + }; + r.augmentClass(n, a), t.exports = n + }, { + "./SyntheticEvent": 115, + "./getEventTarget": 147 + }], + 122: [function(e, t) { + "use strict"; + + function n(e, t, n) { + r.call(this, e, t, n) + } + var r = e("./SyntheticMouseEvent"), + o = { + deltaX: function(e) { + return "deltaX" in e ? e.deltaX : "wheelDeltaX" in e ? -e.wheelDeltaX : 0 + }, + deltaY: function(e) { + return "deltaY" in e ? e.deltaY : "wheelDeltaY" in e ? -e.wheelDeltaY : "wheelDelta" in e ? -e.wheelDelta : 0 + }, + deltaZ: null, + deltaMode: null + }; + r.augmentClass(n, o), t.exports = n + }, { + "./SyntheticMouseEvent": 119 + }], + 123: [function(e, t) { + (function(n) { + "use strict"; + var r = e("./invariant"), + o = { + reinitializeTransaction: function() { + this.transactionWrappers = this.getTransactionWrappers(), this.wrapperInitData ? this.wrapperInitData.length = 0 : this.wrapperInitData = [], this._isInTransaction = !1 + }, + _isInTransaction: !1, + getTransactionWrappers: null, + isInTransaction: function() { + return !!this._isInTransaction + }, + perform: function(e, t, o, a, i, s, u, c) { + "production" !== n.env.NODE_ENV ? r(!this.isInTransaction(), "Transaction.perform(...): Cannot initialize a transaction when there is already an outstanding transaction.") : r(!this.isInTransaction()); + var l, p; + try { + this._isInTransaction = !0, l = !0, this.initializeAll(0), p = e.call(t, o, a, i, s, u, c), l = !1 + } finally { + try { + if (l) try { + this.closeAll(0) + } catch (d) {} else this.closeAll(0) + } finally { + this._isInTransaction = !1 + } + } + return p + }, + initializeAll: function(e) { + for (var t = this.transactionWrappers, n = e; n < t.length; n++) { + var r = t[n]; + try { + this.wrapperInitData[n] = a.OBSERVED_ERROR, this.wrapperInitData[n] = r.initialize ? r.initialize.call(this) : null + } finally { + if (this.wrapperInitData[n] === a.OBSERVED_ERROR) try { + this.initializeAll(n + 1) + } catch (o) {} + } + } + }, + closeAll: function(e) { + "production" !== n.env.NODE_ENV ? r(this.isInTransaction(), "Transaction.closeAll(): Cannot close transaction when none are open.") : r(this.isInTransaction()); + for (var t = this.transactionWrappers, o = e; o < t.length; o++) { + var i, s = t[o], + u = this.wrapperInitData[o]; + try { + i = !0, u !== a.OBSERVED_ERROR && s.close && s.close.call(this, u), i = !1 + } finally { + if (i) try { + this.closeAll(o + 1) + } catch (c) {} + } + } + this.wrapperInitData.length = 0 + } + }, + a = { + Mixin: o, + OBSERVED_ERROR: {} + }; + t.exports = a + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 124: [function(e, t) { + "use strict"; + var n = { + currentScrollLeft: 0, + currentScrollTop: 0, + refreshScrollValues: function(e) { + n.currentScrollLeft = e.x, n.currentScrollTop = e.y + } + }; + t.exports = n + }, {}], + 125: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + if ("production" !== n.env.NODE_ENV ? o(null != t, "accumulateInto(...): Accumulated items must not be null or undefined.") : o(null != t), null == e) return t; + var r = Array.isArray(e), + a = Array.isArray(t); + return r && a ? (e.push.apply(e, t), e) : r ? (e.push(t), e) : a ? [e].concat(t) : [e, t] + } + var o = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./invariant": 157, + _process: 2 + }], + 126: [function(e, t) { + "use strict"; + + function n(e) { + for (var t = 1, n = 0, o = 0; o < e.length; o++) t = (t + e.charCodeAt(o)) % r, n = (n + t) % r; + return t | n << 16 + } + var r = 65521; + t.exports = n + }, {}], + 127: [function(e, t) { + function n(e) { + return e.replace(r, function(e, t) { + return t.toUpperCase() + }) + } + var r = /-(.)/g; + t.exports = n + }, {}], + 128: [function(e, t) { + "use strict"; + + function n(e) { + return r(e.replace(o, "ms-")) + } + var r = e("./camelize"), + o = /^-ms-/; + t.exports = n + }, { + "./camelize": 127 + }], + 129: [function(e, t) { + (function(n) { + "use strict"; + + function r(e, t) { + "production" !== n.env.NODE_ENV && ("production" !== n.env.NODE_ENV ? s(!e.ref, "You are calling cloneWithProps() on a child with a ref. This is dangerous because you're creating a new child which will not be added as a ref to its parent.") : null); + var r = a.mergeProps(t, e.props); + return !r.hasOwnProperty(u) && e.props.hasOwnProperty(u) && (r.children = e.props.children), o.createElement(e.type, r) + } + var o = e("./ReactElement"), + a = e("./ReactPropTransferer"), + i = e("./keyOf"), + s = e("./warning"), + u = i({ + children: null + }); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactElement": 69, + "./ReactPropTransferer": 89, + "./keyOf": 164, + "./warning": 178, + _process: 2 + }], + 130: [function(e, t) { + function n(e, t) { + return e && t ? e === t ? !0 : r(e) ? !1 : r(t) ? n(e, t.parentNode) : e.contains ? e.contains(t) : e.compareDocumentPosition ? !!(16 & e.compareDocumentPosition(t)) : !1 : !1 + } + var r = e("./isTextNode"); + t.exports = n + }, { + "./isTextNode": 161 + }], + 131: [function(e, t) { + function n(e) { + return !!e && ("object" == typeof e || "function" == typeof e) && "length" in e && !("setInterval" in e) && "number" != typeof e.nodeType && (Array.isArray(e) || "callee" in e || "item" in e) + } + + function r(e) { + return n(e) ? Array.isArray(e) ? e.slice() : o(e) : [e] + } + var o = e("./toArray"); + t.exports = r + }, { + "./toArray": 175 + }], + 132: [function(e, t) { + (function(n) { + "use strict"; + + function r(e) { + var t = a.createFactory(e), + r = o.createClass({ + tagName: e.toUpperCase(), + displayName: "ReactFullPageComponent" + e, + componentWillUnmount: function() { + "production" !== n.env.NODE_ENV ? i(!1, "%s tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg , , and ) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.", this.constructor.displayName) : i(!1) + }, + render: function() { + return t(this.props) + } + }); + return r + } + var o = e("./ReactClass"), + a = e("./ReactElement"), + i = e("./invariant"); + t.exports = r + }).call(this, e("_process")) + }, { + "./ReactClass": 44, + "./ReactElement": 69, + "./invariant": 157, + _process: 2 + }], + 133: [function(e, t) { + (function(n) { + function r(e) { + var t = e.match(l); + return t && t[1].toLowerCase() + } + + function o(e, t) { + var o = c; + "production" !== n.env.NODE_ENV ? u(!!c, "createNodesFromMarkup dummy not initialized") : u(!!c); + var a = r(e), + l = a && s(a); + if (l) { + o.innerHTML = l[1] + e + l[2]; + for (var p = l[0]; p--;) o = o.lastChild + } else o.innerHTML = e; + var d = o.getElementsByTagName("script"); + d.length && ("production" !== n.env.NODE_ENV ? u(t, "createNodesFromMarkup(...): Unexpected + + diff --git a/DS4Windows/DS4Control/ControlSerivce.cs b/DS4Windows/DS4Control/ControlSerivce.cs deleted file mode 100644 index 28759a73fc..0000000000 --- a/DS4Windows/DS4Control/ControlSerivce.cs +++ /dev/null @@ -1,851 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using System.IO; -using System.Reflection; -using System.Media; -using System.Threading.Tasks; -using static DS4Windows.Global; -namespace DS4Windows -{ - public class ControlService - { - public X360Device x360Bus; - public DS4Device[] DS4Controllers = new DS4Device[4]; - public Mouse[] touchPad = new Mouse[4]; - private bool running = false; - private DS4State[] MappedState = new DS4State[4]; - private DS4State[] CurrentState = new DS4State[4]; - private DS4State[] PreviousState = new DS4State[4]; - public DS4StateExposed[] ExposedState = new DS4StateExposed[4]; - public bool recordingMacro = false; - public event EventHandler Debug = null; - public bool eastertime = false; - private int eCode = 0; - bool[] buttonsdown = { false, false, false, false }; - List dcs = new List(); - bool[] held = new bool[4]; - int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - SoundPlayer sp = new SoundPlayer(); - - private class X360Data - { - public byte[] Report = new byte[28]; - public byte[] Rumble = new byte[8]; - } - private X360Data[] processingData = new X360Data[4]; - - public ControlService() - { - sp.Stream = Properties.Resources.EE; - x360Bus = new X360Device(); - AddtoDS4List(); - for (int i = 0; i < DS4Controllers.Length; i++) - { - processingData[i] = new X360Data(); - MappedState[i] = new DS4State(); - CurrentState[i] = new DS4State(); - PreviousState[i] = new DS4State(); - ExposedState[i] = new DS4StateExposed(CurrentState[i]); - } - } - - void AddtoDS4List() - { - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Cross); - dcs.Add(DS4Controls.Circle); - dcs.Add(DS4Controls.Square); - dcs.Add(DS4Controls.Triangle); - dcs.Add(DS4Controls.Options); - dcs.Add(DS4Controls.Share); - dcs.Add(DS4Controls.DpadUp); - dcs.Add(DS4Controls.DpadDown); - dcs.Add(DS4Controls.DpadLeft); - dcs.Add(DS4Controls.DpadRight); - dcs.Add(DS4Controls.PS); - dcs.Add(DS4Controls.L1); - dcs.Add(DS4Controls.R1); - dcs.Add(DS4Controls.L2); - dcs.Add(DS4Controls.R2); - dcs.Add(DS4Controls.L3); - dcs.Add(DS4Controls.R3); - dcs.Add(DS4Controls.LXPos); - dcs.Add(DS4Controls.LXNeg); - dcs.Add(DS4Controls.LYPos); - dcs.Add(DS4Controls.LYNeg); - dcs.Add(DS4Controls.RXPos); - dcs.Add(DS4Controls.RXNeg); - dcs.Add(DS4Controls.RYPos); - dcs.Add(DS4Controls.RYNeg); - dcs.Add(DS4Controls.SwipeUp); - dcs.Add(DS4Controls.SwipeDown); - dcs.Add(DS4Controls.SwipeLeft); - dcs.Add(DS4Controls.SwipeRight); - } - - private async void WarnExclusiveModeFailure(DS4Device device) - { - if (DS4Devices.isExclusiveMode && !device.IsExclusive) - { - await System.Threading.Tasks.Task.Delay(5); - String message = Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.MacAddress) + " " + Properties.Resources.QuitOtherPrograms; - LogDebug(message, true); - Log.LogToTray(message, true); - } - } - public bool Start(bool showlog = true) - { - if (x360Bus.Open() && x360Bus.Start()) - { - if (showlog) - LogDebug(Properties.Resources.Starting); - DS4Devices.isExclusiveMode = UseExclusiveMode; - if (showlog) - { - LogDebug(Properties.Resources.SearchingController); - LogDebug(DS4Devices.isExclusiveMode ? Properties.Resources.UsingExclusive: Properties.Resources.UsingShared); - } - try - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - int ind = 0; - DS4LightBar.defualtLight = false; - foreach (DS4Device device in devices) - { - if (showlog) - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[ind] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[ind] = new Mouse(ind, device); - device.LightBarColor = MainColor[ind]; - if (!DinputOnly[ind]) - x360Bus.Plugin(ind); - device.Report += this.On_Report; - TouchPadOn(ind, device); - //string filename = ProfilePath[ind]; - ind++; - if (showlog) - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[ind-1] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", ind.ToString()).Replace("*Profile name*", ProfilePath[ind-1]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (ind).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - if (ind >= 4) // out of Xinput devices! - break; - } - } - catch (Exception e) - { - LogDebug(e.Message); - Log.LogToTray(e.Message); - } - running = true; - - } - return true; - } - - public bool Stop(bool showlog = true) - { - if (running) - { - running = false; - if (showlog) - LogDebug(Properties.Resources.StoppingX360); - bool anyUnplugged = false; - for (int i = 0; i < DS4Controllers.Length; i++) - { - if (DS4Controllers[i] != null) - { - if (DCBTatStop && !DS4Controllers[i].Charging && showlog) - DS4Controllers[i].DisconnectBT(); - else - { - DS4LightBar.forcelight[i] = false; - DS4LightBar.forcedFlash[i] = 0; - DS4LightBar.defualtLight = true; - DS4LightBar.updateLightBar(DS4Controllers[i], i, CurrentState[i], ExposedState[i], touchPad[i]); - System.Threading.Thread.Sleep(50); - } - CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(i); - anyUnplugged = true; - DS4Controllers[i] = null; - touchPad[i] = null; - } - } - if (anyUnplugged) - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - x360Bus.UnplugAll(); - x360Bus.Stop(); - if (showlog) - LogDebug(Properties.Resources.StoppingDS4); - DS4Devices.stopControllers(); - if (showlog) - LogDebug(Properties.Resources.StoppedDS4Windows); - ControllerStatusChanged(this); - } - return true; - } - - public bool HotPlug() - { - if (running) - { - DS4Devices.findControllers(); - IEnumerable devices = DS4Devices.getDS4Controllers(); - foreach (DS4Device device in devices) - { - if (device.IsDisconnecting) - continue; - if (((Func)delegate - { - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] != null && DS4Controllers[Index].MacAddress == device.MacAddress) - return true; - return false; - })()) - continue; - for (Int32 Index = 0; Index < DS4Controllers.Length; Index++) - if (DS4Controllers[Index] == null) - { - LogDebug(Properties.Resources.FoundController + device.MacAddress + " (" + device.ConnectionType + ")"); - WarnExclusiveModeFailure(device); - DS4Controllers[Index] = device; - device.Removal -= DS4Devices.On_Removal; - device.Removal += this.On_DS4Removal; - device.Removal += DS4Devices.On_Removal; - touchPad[Index] = new Mouse(Index, device); - device.LightBarColor = MainColor[Index]; - device.Report += this.On_Report; - if (!DinputOnly[Index]) - x360Bus.Plugin(Index); - TouchPadOn(Index, device); - //string filename = Path.GetFileName(ProfilePath[Index]); - if (System.IO.File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) - { - string prolog = Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); - LogDebug(prolog); - Log.LogToTray(prolog); - } - else - { - string prolog = Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); - LogDebug(prolog); - Log.LogToTray(prolog); - } - - break; - } - } - } - return true; - } - - public void TouchPadOn(int ind, DS4Device device) - { - ITouchpadBehaviour tPad = touchPad[ind]; - device.Touchpad.TouchButtonDown += tPad.touchButtonDown; - device.Touchpad.TouchButtonUp += tPad.touchButtonUp; - device.Touchpad.TouchesBegan += tPad.touchesBegan; - device.Touchpad.TouchesMoved += tPad.touchesMoved; - device.Touchpad.TouchesEnded += tPad.touchesEnded; - device.Touchpad.TouchUnchanged += tPad.touchUnchanged; - device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; - //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); - ControllerStatusChanged(this); - } - - public void TimeoutConnection(DS4Device d) - { - try - { - System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); - sw.Start(); - while (!d.IsAlive()) - { - if (sw.ElapsedMilliseconds < 1000) - System.Threading.Thread.SpinWait(500); - //If weve been waiting less than 1 second let the thread keep its processing chunk - else - System.Threading.Thread.Sleep(500); - //If weve been waiting more than 1 second give up some resources - - if (sw.ElapsedMilliseconds > 5000) throw new TimeoutException(); //Weve waited long enough - } - sw.Reset(); - } - catch (TimeoutException) - { - Stop(false); - Start(false); - } - } - - public string getDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - String battery; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Charged; - else - battery = Properties.Resources.Charging.Replace("*number*", d.Battery.ToString()); - } - else - { - battery = Properties.Resources.Battery.Replace("*number*", d.Battery.ToString()); - } - return d.MacAddress + " (" + d.ConnectionType + "), " + battery; - //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); - } - else - return String.Empty; - } - - public string getDS4MacAddress(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - if (!d.IsAlive()) - //return "Connecting..."; // awaiting the first battery charge indication - { - var TimeoutThread = new System.Threading.Thread(() => TimeoutConnection(d)); - TimeoutThread.IsBackground = true; - TimeoutThread.Name = "TimeoutFor" + d.MacAddress.ToString(); - TimeoutThread.Start(); - return Properties.Resources.Connecting; - } - return d.MacAddress; - } - else - return String.Empty; - } - - public string getShortDS4ControllerInfo(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return (d.ConnectionType + " " + battery); - } - else - return Properties.Resources.NoneText; - } - - public string getDS4Battery(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - String battery; - if (!d.IsAlive()) - battery = "..."; - if (d.Charging) - { - if (d.Battery >= 100) - battery = Properties.Resources.Full; - else - battery = d.Battery + "%+"; - } - else - { - battery = d.Battery + "%"; - } - return battery; - } - else - return Properties.Resources.NA; - } - - public string getDS4Status(int index) - { - if (DS4Controllers[index] != null) - { - DS4Device d = DS4Controllers[index]; - return d.ConnectionType+""; - } - else - return Properties.Resources.NoneText; - } - - - private int XINPUT_UNPLUG_SETTLE_TIME = 250; // Inhibit races that occur with the asynchronous teardown of ScpVBus -> X360 driver instance. - //Called when DS4 is disconnected or timed out - protected virtual void On_DS4Removal(object sender, EventArgs e) - { - DS4Device device = (DS4Device)sender; - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (DS4Controllers[i] != null && device.MacAddress == DS4Controllers[i].MacAddress) - ind = i; - if (ind != -1) - { - CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. - x360Bus.Unplug(ind); - string removed = Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind +1).ToString()); - if (DS4Controllers[ind].Battery <= 20 && - DS4Controllers[ind].ConnectionType == ConnectionType.BT && !DS4Controllers[ind].Charging) - removed += ". " + Properties.Resources.ChargeController; - LogDebug(removed); - Log.LogToTray(removed); - System.Threading.Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); - DS4Controllers[ind] = null; - touchPad[ind] = null; - ControllerStatusChanged(this); - } - } - public bool[] lag = { false, false, false, false }; - //Called every time the new input report has arrived - protected virtual void On_Report(object sender, EventArgs e) - { - - DS4Device device = (DS4Device)sender; - - int ind = -1; - for (int i = 0; i < DS4Controllers.Length; i++) - if (device == DS4Controllers[i]) - ind = i; - - if (ind != -1) - { - if (FlushHIDQueue[ind]) - device.FlushHID(); - if (!string.IsNullOrEmpty(device.error)) - { - LogDebug(device.error); - } - if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) - { - if (device.Latency >= FlashWhenLateAt && !lag[ind]) - LagFlashWarning(ind, true); - else if (device.Latency < FlashWhenLateAt && lag[ind]) - LagFlashWarning(ind, false); - } - device.getExposedState(ExposedState[ind], CurrentState[ind]); - DS4State cState = CurrentState[ind]; - device.getPreviousState(PreviousState[ind]); - DS4State pState = PreviousState[ind]; - if (pState.Battery != cState.Battery) - ControllerStatusChanged(this); - CheckForHotkeys(ind, cState, pState); - if (eastertime) - EasterTime(ind); - GetInputkeys(ind); - if (LSCurve[ind] != 0 || RSCurve[ind] != 0 || LSDeadzone[ind] != 0 || RSDeadzone[ind] != 0 || - L2Deadzone[ind] != 0 || R2Deadzone[ind] != 0 || LSSens[ind] != 0 || RSSens[ind] != 0 || - L2Sens[ind] != 0 || R2Sens[ind] != 0) //if a curve or deadzone is in place - cState = Mapping.SetCurveAndDeadzone(ind, cState); - if (!recordingMacro && (!string.IsNullOrEmpty(tempprofilename[ind]) || - HasCustomAction(ind) || HasCustomExtras(ind) || ProfileActions[ind].Count > 0)) - { - Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); - cState = MappedState[ind]; - } - //if (HasCustomExtras(ind)) - // DoExtras(ind); - - // Update the GUI/whatever. - DS4LightBar.updateLightBar(device, ind, cState, ExposedState[ind], touchPad[ind]); - - x360Bus.Parse(cState, processingData[ind].Report, ind); - // We push the translated Xinput state, and simultaneously we - // pull back any possible rumble data coming from Xinput consumers. - if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) - { - Byte Big = (Byte)(processingData[ind].Rumble[3]); - Byte Small = (Byte)(processingData[ind].Rumble[4]); - - if (processingData[ind].Rumble[1] == 0x08) - { - setRumble(Big, Small, ind); - } - } - - // Output any synthetic events. - Mapping.Commit(ind); - // Pull settings updates. - device.IdleTimeout = IdleDisconnectTimeout[ind]; - } - } - - public void LagFlashWarning(int ind, bool on) - { - if (on) - { - lag[ind] = true; - LogDebug(Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); - if (FlashWhenLate) - { - DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = 2; - DS4LightBar.forcelight[ind] = true; - } - } - else - { - lag[ind] = false; - LogDebug(Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - } - } - - /* private void DoExtras(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - DS4Controls helddown = DS4Controls.None; - foreach (KeyValuePair p in getCustomExtras(ind)) - { - if (Mapping.getBoolMapping(ind, p.Key, cState, eState, tp)) - { - helddown = p.Key; - break; - } - } - if (helddown != DS4Controls.None) - { - string p = getCustomExtras(ind)[helddown]; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[ind] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - setRumble((byte)extras[0], (byte)extras[1], ind); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[ind] = color; - DS4LightBar.forcedFlash[ind] = (byte)extras[6]; - DS4LightBar.forcelight[ind] = true; - } - if (extras[7] == 1) - { - if (oldmouse[ind] == -1) - oldmouse[ind] = ButtonMouseSensitivity[ind]; - ButtonMouseSensitivity[ind] = extras[8]; - } - } - catch { } - } - else if (held[ind]) - { - DS4LightBar.forcelight[ind] = false; - DS4LightBar.forcedFlash[ind] = 0; - ButtonMouseSensitivity[ind] = oldmouse[ind]; - oldmouse[ind] = -1; - setRumble(0, 0, ind); - held[ind] = false; - } - }*/ - - - - public void EasterTime(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - - bool pb = false; - foreach (DS4Controls dc in dcs) - { - if (Mapping.getBoolMapping(ind, dc, cState, eState, tp)) - { - pb = true; - break; - } - } - int temp = eCode; - //Looks like you found the easter egg code, since you're already cheating, - //I scrambled the code for you :) - if (pb && !buttonsdown[ind]) - { - if (cState.Cross && eCode == 9) - eCode++; - else if (!cState.Cross && eCode == 9) - eCode = 0; - else if (cState.DpadLeft && eCode == 6) - eCode++; - else if (!cState.DpadLeft && eCode == 6) - eCode = 0; - else if (cState.DpadRight && eCode == 7) - eCode++; - else if (!cState.DpadRight && eCode == 7) - eCode = 0; - else if (cState.DpadLeft && eCode == 4) - eCode++; - else if (!cState.DpadLeft && eCode == 4) - eCode = 0; - else if (cState.DpadDown && eCode == 2) - eCode++; - else if (!cState.DpadDown && eCode == 2) - eCode = 0; - else if (cState.DpadRight && eCode == 5) - eCode++; - else if (!cState.DpadRight && eCode == 5) - eCode = 0; - else if (cState.DpadUp && eCode == 1) - eCode++; - else if (!cState.DpadUp && eCode == 1) - eCode = 0; - else if (cState.DpadDown && eCode == 3) - eCode++; - else if (!cState.DpadDown && eCode == 3) - eCode = 0; - else if (cState.Circle && eCode == 8) - eCode++; - else if (!cState.Circle && eCode == 8) - eCode = 0; - - if (cState.DpadUp && eCode == 0) - eCode++; - - if (eCode == 10) - { - string message = "(!)"; - sp.Play(); - LogDebug(message, true); - eCode = 0; - } - - if (temp != eCode) - Console.WriteLine(eCode); - buttonsdown[ind] = true; - } - else if (!pb) - buttonsdown[ind] = false; - } - - public string GetInputkeys(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return "Cross"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return "Circle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return "Triangle"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return "Square"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return "L1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return "R1"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return "L2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return "R2"; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return "L3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return "R3"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return "Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return "Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return "Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return "Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return "Share"; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return "Options"; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return "PS"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return "LS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return "LS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return "LS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return "LS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return "RS Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return "RS Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return "RS Down"; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return "RS Up"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return "Touch Left"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return "Touch Right"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return "Touch Multi"; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return "Touch Upper"; - return "nothing"; - } - - public DS4Controls GetInputkeysDS4(int ind) - { - DS4State cState = CurrentState[ind]; - DS4StateExposed eState = ExposedState[ind]; - Mouse tp = touchPad[ind]; - if (DS4Controllers[ind] != null) - if (Mapping.getBoolMapping(ind, DS4Controls.Cross, cState, eState, tp)) return DS4Controls.Cross; - else if (Mapping.getBoolMapping(ind, DS4Controls.Circle, cState, eState, tp)) return DS4Controls.Circle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Triangle, cState, eState, tp)) return DS4Controls.Triangle; - else if (Mapping.getBoolMapping(ind, DS4Controls.Square, cState, eState, tp)) return DS4Controls.Square; - else if (Mapping.getBoolMapping(ind, DS4Controls.L1, cState, eState, tp)) return DS4Controls.L1; - else if (Mapping.getBoolMapping(ind, DS4Controls.R1, cState, eState, tp)) return DS4Controls.R1; - else if (Mapping.getBoolMapping(ind, DS4Controls.L2, cState, eState, tp)) return DS4Controls.L2; - else if (Mapping.getBoolMapping(ind, DS4Controls.R2, cState, eState, tp)) return DS4Controls.R2; - else if (Mapping.getBoolMapping(ind, DS4Controls.L3, cState, eState, tp)) return DS4Controls.L3; - else if (Mapping.getBoolMapping(ind, DS4Controls.R3, cState, eState, tp)) return DS4Controls.R3; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadUp, cState, eState, tp)) return DS4Controls.DpadUp; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadDown, cState, eState, tp)) return DS4Controls.DpadDown; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadLeft, cState, eState, tp)) return DS4Controls.DpadLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.DpadRight, cState, eState, tp)) return DS4Controls.DpadRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.Share, cState, eState, tp)) return DS4Controls.Share; - else if (Mapping.getBoolMapping(ind, DS4Controls.Options, cState, eState, tp)) return DS4Controls.Options; - else if (Mapping.getBoolMapping(ind, DS4Controls.PS, cState, eState, tp)) return DS4Controls.PS; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXPos, cState, eState, tp)) return DS4Controls.LXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LXNeg, cState, eState, tp)) return DS4Controls.LXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYPos, cState, eState, tp)) return DS4Controls.LYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.LYNeg, cState, eState, tp)) return DS4Controls.LYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXPos, cState, eState, tp)) return DS4Controls.RXPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RXNeg, cState, eState, tp)) return DS4Controls.RXNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYPos, cState, eState, tp)) return DS4Controls.RYPos; - else if (Mapping.getBoolMapping(ind, DS4Controls.RYNeg, cState, eState, tp)) return DS4Controls.RYNeg; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchLeft, cState, eState, tp)) return DS4Controls.TouchLeft; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchRight, cState, eState, tp)) return DS4Controls.TouchRight; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchMulti, cState, eState, tp)) return DS4Controls.TouchMulti; - else if (Mapping.getBoolMapping(ind, DS4Controls.TouchUpper, cState, eState, tp)) return DS4Controls.TouchUpper; - return DS4Controls.None; - } - - public bool[] touchreleased = { true, true, true, true }, touchslid = { false, false, false, false }; - public byte[] oldtouchvalue = { 0, 0, 0, 0 }; - public int[] oldscrollvalue = { 0, 0, 0, 0 }; - protected virtual void CheckForHotkeys(int deviceID, DS4State cState, DS4State pState) - { - if (!UseTPforControls[deviceID] && cState.Touch1 && pState.PS) - { - if (TouchSensitivity[deviceID] > 0 && touchreleased[deviceID]) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - else if (touchreleased[deviceID]) - { - TouchSensitivity[deviceID] = oldtouchvalue[deviceID]; - ScrollSensitivity[deviceID] = oldscrollvalue[deviceID]; - LogDebug(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - Log.LogToTray(TouchSensitivity[deviceID] > 0 ? Properties.Resources.TouchpadMovementOn : Properties.Resources.TouchpadMovementOff); - touchreleased[deviceID] = false; - } - } - else - touchreleased[deviceID] = true; - } - - public virtual void StartTPOff(int deviceID) - { - if (deviceID < 4) - { - oldtouchvalue[deviceID] = TouchSensitivity[deviceID]; - oldscrollvalue[deviceID] = ScrollSensitivity[deviceID]; - TouchSensitivity[deviceID] = 0; - ScrollSensitivity[deviceID] = 0; - } - } - - public virtual string TouchpadSlide(int ind) - { - DS4State cState = CurrentState[ind]; - string slidedir = "none"; - if (DS4Controllers[ind] != null && cState.Touch2 && !(touchPad[ind].dragging || touchPad[ind].dragging2)) - if (touchPad[ind].slideright && !touchslid[ind]) - { - slidedir = "right"; - touchslid[ind] = true; - } - else if (touchPad[ind].slideleft && !touchslid[ind]) - { - slidedir = "left"; - touchslid[ind] = true; - } - else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) - { - slidedir = ""; - touchslid[ind] = false; - } - return slidedir; - } - public virtual void LogDebug(String Data, bool warning = false) - { - Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); - if (Debug != null) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - OnDebug(this, args); - } - } - - public virtual void OnDebug(object sender, DebugEventArgs args) - { - if (Debug != null) - Debug(this, args); - } - - //sets the rumble adjusted with rumble boost - public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) - { - byte boost = RumbleBoost[deviceNum]; - uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; - if (lightBoosted > 255) - lightBoosted = 255; - uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; - if (heavyBoosted > 255) - heavyBoosted = 255; - if (deviceNum < 4) - if (DS4Controllers[deviceNum] != null) - DS4Controllers[deviceNum].setRumble((byte)lightBoosted, (byte)heavyBoosted); - } - - public DS4State getDS4State(int ind) - { - return CurrentState[ind]; - } - public DS4State getDS4StateMapped(int ind) - { - return MappedState[ind]; - } - } -} diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs new file mode 100644 index 0000000000..067e33d3c4 --- /dev/null +++ b/DS4Windows/DS4Control/ControlService.cs @@ -0,0 +1,1613 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System.Diagnostics; +using static DS4Windows.Global; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class ControlService + { + public ViGEmClient vigemTestClient = null; + public const int DS4_CONTROLLER_COUNT = 4; + public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT]; + public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT]; + public bool running = false; + private DS4State[] MappedState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] CurrentState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] PreviousState = new DS4State[DS4_CONTROLLER_COUNT]; + private DS4State[] TempState = new DS4State[DS4_CONTROLLER_COUNT]; + public DS4StateExposed[] ExposedState = new DS4StateExposed[DS4_CONTROLLER_COUNT]; + public ControllerSlotManager slotManager = new ControllerSlotManager(); + public bool recordingMacro = false; + public event EventHandler Debug = null; + bool[] buttonsdown = new bool[4] { false, false, false, false }; + bool[] held = new bool[DS4_CONTROLLER_COUNT]; + int[] oldmouse = new int[DS4_CONTROLLER_COUNT] { -1, -1, -1, -1 }; + public OutputDevice[] outputDevices = new OutputDevice[4] { null, null, null, null }; + Thread tempThread; + Thread tempBusThread; + public List affectedDevs = new List() + { + @"HID\VID_054C&PID_05C4", + @"HID\VID_054C&PID_09CC&MI_03", + @"HID\VID_054C&PID_0BA0&MI_03", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&05c4", + @"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&09cc", + }; + public bool suspending; + //SoundPlayer sp = new SoundPlayer(); + private UdpServer _udpServer; + private OutputSlotManager outputslotMan; + + public event EventHandler ServiceStarted; + public event EventHandler PreServiceStop; + public event EventHandler ServiceStopped; + public event EventHandler RunningChanged; + //public event EventHandler HotplugFinished; + public delegate void HotplugControllerHandler(ControlService sender, DS4Device device, int index); + public event HotplugControllerHandler HotplugController; + + private byte[][] udpOutBuffers = new byte[4][] + { + new byte[100], new byte[100], + new byte[100], new byte[100] + }; + + + void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta) + { + //meta = new DualShockPadMeta(); + meta.PadId = (byte) padIdx; + meta.Model = DsModel.DS4; + + var d = DS4Controllers[padIdx]; + if (d == null) + { + meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + meta.ConnectionType = DsConnection.None; + meta.Model = DsModel.None; + meta.BatteryStatus = 0; + meta.IsActive = false; + return; + //return meta; + } + + bool isValidSerial = false; + //if (d.isValidSerial()) + //{ + string stringMac = d.getMacAddress(); + if (!string.IsNullOrEmpty(stringMac)) + { + stringMac = string.Join("", stringMac.Split(':')); + //stringMac = stringMac.Replace(":", "").Trim(); + meta.PadMacAddress = System.Net.NetworkInformation.PhysicalAddress.Parse(stringMac); + isValidSerial = d.isValidSerial(); + } + //} + + if (!isValidSerial) + { + //meta.PadMacAddress = null; + meta.PadState = DsState.Disconnected; + } + else + { + if (d.isSynced() || d.IsAlive()) + meta.PadState = DsState.Connected; + else + meta.PadState = DsState.Reserved; + } + + meta.ConnectionType = (d.getConnectionType() == ConnectionType.USB) ? DsConnection.Usb : DsConnection.Bluetooth; + meta.IsActive = !d.isDS4Idle(); + + if (d.isCharging() && d.getBattery() >= 100) + meta.BatteryStatus = DsBattery.Charged; + else + { + if (d.getBattery() >= 95) + meta.BatteryStatus = DsBattery.Full; + else if (d.getBattery() >= 70) + meta.BatteryStatus = DsBattery.High; + else if (d.getBattery() >= 50) + meta.BatteryStatus = DsBattery.Medium; + else if (d.getBattery() >= 20) + meta.BatteryStatus = DsBattery.Low; + else if (d.getBattery() >= 5) + meta.BatteryStatus = DsBattery.Dying; + else + meta.BatteryStatus = DsBattery.None; + } + + //return meta; + } + + private object busThrLck = new object(); + private bool busThrRunning = false; + private Queue busEvtQueue = new Queue(); + private object busEvtQueueLock = new object(); + public ControlService() + { + Crc32Algorithm.InitializeTable(DS4Device.DefaultPolynomial); + + //sp.Stream = DS4WinWPF.Properties.Resources.EE; + // Cause thread affinity to not be tied to main GUI thread + tempBusThread = new Thread(() => + { + //_udpServer = new UdpServer(GetPadDetailForIdx); + busThrRunning = true; + + while (busThrRunning) + { + lock (busEvtQueueLock) + { + Action tempAct = null; + for (int actInd = 0, actLen = busEvtQueue.Count; actInd < actLen; actInd++) + { + tempAct = busEvtQueue.Dequeue(); + tempAct.Invoke(); + } + } + + lock (busThrLck) + Monitor.Wait(busThrLck); + } + }); + tempBusThread.Priority = ThreadPriority.Normal; + tempBusThread.IsBackground = true; + tempBusThread.Start(); + //while (_udpServer == null) + //{ + // Thread.SpinWait(500); + //} + + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + MappedState[i] = new DS4State(); + CurrentState[i] = new DS4State(); + TempState[i] = new DS4State(); + PreviousState[i] = new DS4State(); + ExposedState[i] = new DS4StateExposed(CurrentState[i]); + } + + outputslotMan = new OutputSlotManager(); + DS4Devices.RequestElevation += DS4Devices_RequestElevation; + } + + private void DS4Devices_RequestElevation(RequestElevationArgs args) + { + // Launches an elevated child process to re-enable device + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exelocation); + startInfo.Verb = "runas"; + startInfo.Arguments = "re-enabledevice " + args.InstanceId; + + try + { + Process child = Process.Start(startInfo); + if (!child.WaitForExit(30000)) + { + child.Kill(); + } + else + { + args.StatusCode = child.ExitCode; + } + child.Dispose(); + } + catch { } + } + + public void LaunchHidGuardHelper() + { + if (Global.hidguardInstalled) + { + LogDebug("HidGuardian in use. Launching HidGuardHelper."); + ProcessStartInfo startInfo = + new ProcessStartInfo(Global.exedirpath + "\\HidGuardHelper.exe"); + startInfo.Verb = "runas"; + startInfo.Arguments = Process.GetCurrentProcess().Id.ToString(); + startInfo.WorkingDirectory = Global.exedirpath; + try + { Process tempProc = Process.Start(startInfo); tempProc.Dispose(); } + catch { } + } + } + + private void TestQueueBus(Action temp) + { + lock (busEvtQueueLock) + { + busEvtQueue.Enqueue(temp); + } + + lock (busThrLck) + Monitor.Pulse(busThrLck); + } + + public void ChangeUDPStatus(bool state, bool openPort=true) + { + if (state && _udpServer == null) + { + udpChangeStatus = true; + TestQueueBus(() => + { + _udpServer = new UdpServer(GetPadDetailForIdx); + if (openPort) + { + // Change thread affinity of object to have normal priority + Task.Run(() => + { + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + }).Wait(); + } + + udpChangeStatus = false; + }); + } + else if (!state && _udpServer != null) + { + TestQueueBus(() => + { + udpChangeStatus = true; + _udpServer.Stop(); + _udpServer = null; + AppLogger.LogToGui("Closed UDP server", false); + udpChangeStatus = false; + }); + } + } + + public void ChangeMotionEventStatus(bool state) + { + IEnumerable devices = DS4Devices.getDS4Controllers(); + if (state) + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + } + else + { + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + } + } + + private bool udpChangeStatus = false; + public bool changingUDPPort = false; + public async void UseUDPPort() + { + changingUDPPort = true; + IEnumerable devices = DS4Devices.getDS4Controllers(); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report -= dev.MotionEvent; + }); + } + + await Task.Delay(100); + + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + foreach (DS4Device dev in devices) + { + dev.queueEvent(() => + { + dev.Report += dev.MotionEvent; + }); + } + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + + changingUDPPort = false; + } + + private void WarnExclusiveModeFailure(DS4Device device) + { + if (DS4Devices.isExclusiveMode && !device.isExclusive()) + { + string message = DS4WinWPF.Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.getMacAddress()) + " " + + DS4WinWPF.Properties.Resources.QuitOtherPrograms; + LogDebug(message, true); + AppLogger.LogToTray(message, true); + } + } + + private void startViGEm() + { + tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } }); + tempThread.Priority = ThreadPriority.AboveNormal; + tempThread.IsBackground = true; + tempThread.Start(); + while (tempThread.IsAlive) + { + Thread.SpinWait(500); + } + + tempThread = null; + } + + private void stopViGEm() + { + if (vigemTestClient != null) + { + vigemTestClient.Dispose(); + vigemTestClient = null; + } + } + + public void PluginOutDev(int index, DS4Device device) + { + OutContType contType = Global.OutContType[index]; + if (useDInputOnly[index]) + { + if (contType == OutContType.X360) + { + LogDebug("Plugging in X360 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.X360; + + //Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient); + Xbox360OutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, vigemTestClient) + as Xbox360OutDevice; + //outputDevices[index] = tempXbox; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) => + { + SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); + }; + tempXbox.cont.FeedbackReceived += p; + tempXbox.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempXbox, index, outputDevices); + //tempXbox.Connect(); + //LogDebug("X360 Controller #" + (index + 1) + " connected"); + } + else if (contType == OutContType.DS4) + { + LogDebug("Plugging in DS4 Controller for input #" + (index + 1)); + activeOutDevType[index] = OutContType.DS4; + //DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient); + DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient) + as DS4OutDevice; + //outputDevices[index] = tempDS4; + int devIndex = index; + Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => + { + //bool useRumble = false; bool useLight = false; + byte largeMotor = args.LargeMotor; + byte smallMotor = args.SmallMotor; + SetDevRumble(device, largeMotor, smallMotor, devIndex); + //DS4Color color = new DS4Color(args.LightbarColor.Red, + // args.LightbarColor.Green, + // args.LightbarColor.Blue); + ///*Console.WriteLine("IN EVENT"); + //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", + // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToLongTimeString()); + // */ + //if (largeMotor != 0 || smallMotor != 0) + //{ + // useRumble = true; + //} + + //if (color.red != 0 || color.green != 0 || color.blue != 0) + //{ + // useLight = true; + //} + + //if (!useRumble && !useLight) + //{ + // //Console.WriteLine("Fallback"); + // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) + // { + // useRumble = true; + // } + // /*else if (device.LightBarColor.red != 0 || + // device.LightBarColor.green != 0 || + // device.LightBarColor.blue != 0) + // { + // useLight = true; + // } + // */ + //} + + //if (useRumble) + //{ + // //Console.WriteLine("Perform rumble"); + // SetDevRumble(device, largeMotor, smallMotor, devIndex); + //} + + //if (useLight) + //{ + // //Console.WriteLine("Change lightbar color"); + // DS4HapticState haptics = new DS4HapticState + // { + // LightBarColor = color, + // }; + // device.SetHapticState(ref haptics); + //} + + //Console.WriteLine(); + }; + tempDS4.cont.FeedbackReceived += p; + tempDS4.forceFeedbackCall = p; + + outputslotMan.DeferredPlugin(tempDS4, index, outputDevices); + //tempDS4.Connect(); + //LogDebug("DS4 Controller #" + (index + 1) + " connected"); + } + } + + useDInputOnly[index] = false; + } + + public void UnplugOutDev(int index, DS4Device device, bool immediate = false) + { + if (!useDInputOnly[index]) + { + //OutContType contType = Global.OutContType[index]; + string tempType = outputDevices[index].GetDeviceType(); + LogDebug("Unplugging " + tempType + " Controller for input #" + (index + 1), false); + OutputDevice dev = outputDevices[index]; + outputDevices[index] = null; + activeOutDevType[index] = OutContType.None; + outputslotMan.DeferredRemoval(dev, index, outputDevices, immediate); + //dev.Disconnect(); + //LogDebug(tempType + " Controller # " + (index + 1) + " unplugged"); + useDInputOnly[index] = true; + } + } + + public bool Start(bool showlog = true) + { + startViGEm(); + if (vigemTestClient != null) + //if (x360Bus.Open() && x360Bus.Start()) + { + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.Starting); + + LogDebug($"Connection to ViGEmBus {Global.vigembusVersion} established"); + + DS4Devices.isExclusiveMode = getUseExclusiveMode(); + //uiContext = tempui as SynchronizationContext; + if (showlog) + { + LogDebug(DS4WinWPF.Properties.Resources.SearchingController); + LogDebug(DS4Devices.isExclusiveMode ? DS4WinWPF.Properties.Resources.UsingExclusive : DS4WinWPF.Properties.Resources.UsingShared); + } + + if (isUsingUDPServer() && _udpServer == null) + { + ChangeUDPStatus(true, false); + while (udpChangeStatus == true) + { + Thread.SpinWait(500); + } + } + + try + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //int ind = 0; + DS4LightBar.defaultLight = false; + //foreach (DS4Device device in devices) + + //for (int i = 0, devCount = devices.Count(); i < devCount; i++) + int i = 0; + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext(); i++) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + + DS4Controllers[i] = device; + slotManager.AddController(device, i); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[i] = new Mouse(i, device); + + device.SetMouseProxy(touchPad[i]); + + if (!useTempProfile[i]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[i] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[i] = true; + } + else + { + ProfilePath[i] = OlderProfilePath[i]; + Global.linkedProfileCheck[i] = false; + } + + LoadProfile(i, false, this, false, false); + } + + device.LightBarColor = getMainColor(i); + + if (!getDInputOnly(i) && device.isSynced()) + { + //useDInputOnly[i] = false; + PluginOutDev(i, device); + + } + else + { + useDInputOnly[i] = true; + Global.activeOutDevType[i] = OutContType.None; + } + + int tempIdx = i; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + TouchPadOn(i, device); + CheckProfileOptions(i, device, true); + device.StartUpdate(); + //string filename = ProfilePath[ind]; + //ind++; + if (showlog) + { + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[i] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (i + 1).ToString()).Replace("*Profile name*", ProfilePath[i]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (i + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + } + + if (i >= 4) // out of Xinput devices! + break; + } + } + catch (Exception e) + { + LogDebug(e.Message, true); + AppLogger.LogToTray(e.Message, true); + } + + running = true; + + if (_udpServer != null) + { + //var UDP_SERVER_PORT = 26760; + var UDP_SERVER_PORT = Global.getUDPServerPortNum(); + var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress(); + + try + { + _udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS); + LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}"); + } + catch (System.Net.Sockets.SocketException ex) + { + var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode); + + LogDebug(errMsg, true); + AppLogger.LogToTray(errMsg, true, true); + } + } + } + else + { + string logMessage = string.Empty; + if (!vigemInstalled) + { + logMessage = "ViGEmBus is not installed"; + } + else + { + logMessage = "Could not connect to ViGEmBus. Please check the status of the System device in Device Manager and if Visual C++ 2017 Redistributable is installed."; + } + + LogDebug(logMessage); + AppLogger.LogToTray(logMessage); + } + + runHotPlug = true; + ServiceStarted?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + private void CheckQuickCharge(object sender, EventArgs e) + { + DS4Device device = sender as DS4Device; + if (device.ConnectionType == ConnectionType.BT && getQuickCharge() && + device.Charging) + { + device.DisconnectBT(); + } + } + + public void PrepareAbort() + { + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + tempDevice.PrepareAbort(); + } + } + } + + public bool Stop(bool showlog = true) + { + if (running) + { + running = false; + runHotPlug = false; + PreServiceStop?.Invoke(this, EventArgs.Empty); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingX360); + + LogDebug("Closing connection to ViGEmBus"); + + bool anyUnplugged = false; + for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++) + { + DS4Device tempDevice = DS4Controllers[i]; + if (tempDevice != null) + { + if ((DCBTatStop && !tempDevice.isCharging()) || suspending) + { + if (tempDevice.getConnectionType() == ConnectionType.BT) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectBT(true); + } + else if (tempDevice.getConnectionType() == ConnectionType.SONYWA) + { + tempDevice.StopUpdate(); + tempDevice.DisconnectDongle(true); + } + else + { + tempDevice.StopUpdate(); + } + } + else + { + DS4LightBar.forcelight[i] = false; + DS4LightBar.forcedFlash[i] = 0; + DS4LightBar.defaultLight = true; + DS4LightBar.updateLightBar(DS4Controllers[i], i); + tempDevice.IsRemoved = true; + tempDevice.StopUpdate(); + DS4Devices.RemoveDevice(tempDevice); + Thread.Sleep(50); + } + + CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change. + OutputDevice tempout = outputDevices[i]; + if (tempout != null) + { + UnplugOutDev(i, tempDevice, true); + anyUnplugged = true; + } + + //outputDevices[i] = null; + //useDInputOnly[i] = true; + //Global.activeOutDevType[i] = OutContType.None; + DS4Controllers[i] = null; + touchPad[i] = null; + lag[i] = false; + inWarnMonitor[i] = false; + } + } + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppingDS4); + + DS4Devices.stopControllers(); + slotManager.ClearControllerList(); + + if (_udpServer != null) + ChangeUDPStatus(false); + //_udpServer.Stop(); + + if (showlog) + LogDebug(DS4WinWPF.Properties.Resources.StoppedDS4Windows); + + while (outputslotMan.RunningQueue) + { + Thread.SpinWait(500); + } + + if (anyUnplugged) + { + Thread.Sleep(OutputSlotManager.DELAY_TIME); + } + + stopViGEm(); + } + + runHotPlug = false; + ServiceStopped?.Invoke(this, EventArgs.Empty); + RunningChanged?.Invoke(this, EventArgs.Empty); + return true; + } + + public bool HotPlug() + { + if (running) + { + DS4Devices.findControllers(); + IEnumerable devices = DS4Devices.getDS4Controllers(); + //foreach (DS4Device device in devices) + //for (int i = 0, devlen = devices.Count(); i < devlen; i++) + for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext();) + { + DS4Device device = devEnum.Current; + //DS4Device device = devices.ElementAt(i); + + if (device.isDisconnectingStatus()) + continue; + + if (((Func)delegate + { + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] != null && + DS4Controllers[Index].getMacAddress() == device.getMacAddress()) + return true; + } + + return false; + })()) + { + continue; + } + + for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++) + { + if (DS4Controllers[Index] == null) + { + //LogDebug(DS4WinWPF.Properties.Resources.FoundController + device.getMacAddress() + " (" + device.getConnectionType() + ")"); + LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" + + device.DisplayName + ")"); + Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); }); + task.Start(); + DS4Controllers[Index] = device; + slotManager.AddController(device, Index); + device.Removal += this.On_DS4Removal; + device.Removal += DS4Devices.On_Removal; + device.SyncChange += this.On_SyncChange; + device.SyncChange += DS4Devices.UpdateSerial; + device.SerialChange += this.On_SerialChange; + device.ChargingChanged += CheckQuickCharge; + + touchPad[Index] = new Mouse(Index, device); + + device.SetMouseProxy(touchPad[Index]); + + if (!useTempProfile[Index]) + { + if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress())) + { + ProfilePath[Index] = getLinkedProfile(device.getMacAddress()); + Global.linkedProfileCheck[Index] = true; + } + else + { + ProfilePath[Index] = OlderProfilePath[Index]; + Global.linkedProfileCheck[Index] = false; + } + + LoadProfile(Index, false, this, false, false); + } + + device.LightBarColor = getMainColor(Index); + + int tempIdx = Index; + device.Report += (sender, e) => + { + this.On_Report(sender, e, tempIdx); + }; + + DS4Device.ReportHandler tempEvnt = (sender, args) => + { + DualShockPadMeta padDetail = new DualShockPadMeta(); + GetPadDetailForIdx(tempIdx, ref padDetail); + _udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]); + }; + device.MotionEvent = tempEvnt; + + if (_udpServer != null) + { + device.Report += tempEvnt; + } + + if (!getDInputOnly(Index) && device.isSynced()) + { + //useDInputOnly[Index] = false; + PluginOutDev(Index, device); + } + else + { + useDInputOnly[Index] = true; + Global.activeOutDevType[Index] = OutContType.None; + } + + TouchPadOn(Index, device); + CheckProfileOptions(Index, device); + device.StartUpdate(); + + //string filename = Path.GetFileName(ProfilePath[Index]); + if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml")) + { + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + else + { + string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString()); + LogDebug(prolog); + AppLogger.LogToTray(prolog); + } + + HotplugController?.Invoke(this, device, Index); + + break; + } + } + } + } + + return true; + } + + private void CheckProfileOptions(int ind, DS4Device device, bool startUp=false) + { + device.setIdleTimeout(getIdleDisconnectTimeout(ind)); + device.setBTPollRate(getBTPollRate(ind)); + touchPad[ind].ResetTrackAccel(getTrackballFriction(ind)); + + if (!startUp) + { + CheckLauchProfileOption(ind, device); + } + } + + private void CheckLauchProfileOption(int ind, DS4Device device) + { + string programPath = LaunchProgram[ind]; + if (programPath != string.Empty) + { + System.Diagnostics.Process[] localAll = System.Diagnostics.Process.GetProcesses(); + bool procFound = false; + for (int procInd = 0, procsLen = localAll.Length; !procFound && procInd < procsLen; procInd++) + { + try + { + string temp = localAll[procInd].MainModule.FileName; + if (temp == programPath) + { + procFound = true; + } + } + // Ignore any process for which this information + // is not exposed + catch { } + } + + if (!procFound) + { + Task processTask = new Task(() => + { + Thread.Sleep(5000); + System.Diagnostics.Process tempProcess = new System.Diagnostics.Process(); + tempProcess.StartInfo.FileName = programPath; + tempProcess.StartInfo.WorkingDirectory = new FileInfo(programPath).Directory.ToString(); + //tempProcess.StartInfo.UseShellExecute = false; + try { tempProcess.Start(); } + catch { } + }); + + processTask.Start(); + } + } + } + + public void TouchPadOn(int ind, DS4Device device) + { + Mouse tPad = touchPad[ind]; + //ITouchpadBehaviour tPad = touchPad[ind]; + device.Touchpad.TouchButtonDown += tPad.touchButtonDown; + device.Touchpad.TouchButtonUp += tPad.touchButtonUp; + device.Touchpad.TouchesBegan += tPad.touchesBegan; + device.Touchpad.TouchesMoved += tPad.touchesMoved; + device.Touchpad.TouchesEnded += tPad.touchesEnded; + device.Touchpad.TouchUnchanged += tPad.touchUnchanged; + //device.Touchpad.PreTouchProcess += delegate { touchPad[ind].populatePriorButtonStates(); }; + device.Touchpad.PreTouchProcess += (sender, args) => { touchPad[ind].populatePriorButtonStates(); }; + device.SixAxis.SixAccelMoved += tPad.sixaxisMoved; + //LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + //Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString()); + } + + public string getDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + string battery; + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Charged; + else + battery = DS4WinWPF.Properties.Resources.Charging.Replace("*number*", d.getBattery().ToString()); + } + else + { + battery = DS4WinWPF.Properties.Resources.Battery.Replace("*number*", d.getBattery().ToString()); + } + + return d.getMacAddress() + " (" + d.getConnectionType() + "), " + battery; + //return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString(); + } + else + return string.Empty; + } + + public string getDS4MacAddress(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + if (!d.IsAlive()) + { + return DS4WinWPF.Properties.Resources.Connecting; + } + + return d.getMacAddress(); + } + else + return string.Empty; + } + + public string getShortDS4ControllerInfo(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return (d.getConnectionType() + " " + battery); + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + public string getDS4Battery(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + string battery; + if (!d.IsAlive()) + battery = "..."; + + if (d.isCharging()) + { + if (d.getBattery() >= 100) + battery = DS4WinWPF.Properties.Resources.Full; + else + battery = d.getBattery() + "%+"; + } + else + { + battery = d.getBattery() + "%"; + } + + return battery; + } + else + return DS4WinWPF.Properties.Resources.NA; + } + + public string getDS4Status(int index) + { + DS4Device d = DS4Controllers[index]; + if (d != null) + { + return d.getConnectionType() + ""; + } + else + return DS4WinWPF.Properties.Resources.NoneText; + } + + protected void On_SerialChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + OnDeviceSerialChange(this, ind, device.getMacAddress()); + } + } + + protected void On_SyncChange(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++) + { + DS4Device tempDev = DS4Controllers[i]; + if (tempDev != null && device == tempDev) + ind = i; + } + + if (ind >= 0) + { + bool synced = device.isSynced(); + + if (!synced) + { + if (!useDInputOnly[ind]) + { + Global.activeOutDevType[ind] = OutContType.None; + UnplugOutDev(ind, device); + } + } + else + { + if (!getDInputOnly(ind)) + { + PluginOutDev(ind, device); + } + } + } + } + + //Called when DS4 is disconnected or timed out + protected virtual void On_DS4Removal(object sender, EventArgs e) + { + DS4Device device = (DS4Device)sender; + int ind = -1; + for (int i = 0, arlength = DS4Controllers.Length; ind == -1 && i < arlength; i++) + { + if (DS4Controllers[i] != null && device.getMacAddress() == DS4Controllers[i].getMacAddress()) + ind = i; + } + + if (ind != -1) + { + bool removingStatus = false; + lock (device.removeLocker) + { + if (!device.IsRemoving) + { + removingStatus = true; + device.IsRemoving = true; + } + } + + if (removingStatus) + { + CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change. + if (!useDInputOnly[ind]) + { + UnplugOutDev(ind, device); + } + + // Use Task to reset device synth state and commit it + Task.Run(() => + { + Mapping.Commit(ind); + }).Wait(); + + string removed = DS4WinWPF.Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind + 1).ToString()); + if (device.getBattery() <= 20 && + device.getConnectionType() == ConnectionType.BT && !device.isCharging()) + { + removed += ". " + DS4WinWPF.Properties.Resources.ChargeController; + } + + LogDebug(removed); + AppLogger.LogToTray(removed); + /*Stopwatch sw = new Stopwatch(); + sw.Start(); + while (sw.ElapsedMilliseconds < XINPUT_UNPLUG_SETTLE_TIME) + { + // Use SpinWait to keep control of current thread. Using Sleep could potentially + // cause other events to get run out of order + System.Threading.Thread.SpinWait(500); + } + sw.Stop(); + */ + + device.IsRemoved = true; + device.Synced = false; + DS4Controllers[ind] = null; + slotManager.RemoveController(device, ind); + touchPad[ind] = null; + lag[ind] = false; + inWarnMonitor[ind] = false; + useDInputOnly[ind] = true; + Global.activeOutDevType[ind] = OutContType.None; + /*uiContext?.Post(new SendOrPostCallback((state) => + { + OnControllerRemoved(this, ind); + }), null); + */ + //Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME); + } + } + } + + public bool[] lag = new bool[4] { false, false, false, false }; + public bool[] inWarnMonitor = new bool[4] { false, false, false, false }; + private byte[] currentBattery = new byte[4] { 0, 0, 0, 0 }; + private bool[] charging = new bool[4] { false, false, false, false }; + private string[] tempStrings = new string[4] { string.Empty, string.Empty, string.Empty, string.Empty }; + + // Called every time a new input report has arrived + //protected virtual void On_Report(object sender, EventArgs e, int ind) + protected virtual void On_Report(DS4Device device, EventArgs e, int ind) + { + //DS4Device device = (DS4Device)sender; + + if (ind != -1) + { + if (getFlushHIDQueue(ind)) + device.FlushHID(); + + string devError = tempStrings[ind] = device.error; + if (!string.IsNullOrEmpty(devError)) + { + LogDebug(devError); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LogDebug(devError); + }), null); + */ + } + + if (inWarnMonitor[ind]) + { + int flashWhenLateAt = getFlashWhenLateAt(); + if (!lag[ind] && device.Latency >= flashWhenLateAt) + { + lag[ind] = true; + LagFlashWarning(ind, true); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, true); + }), null); + */ + } + else if (lag[ind] && device.Latency < flashWhenLateAt) + { + lag[ind] = false; + LagFlashWarning(ind, false); + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + LagFlashWarning(ind, false); + }), null); + */ + } + } + else + { + if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5)) + { + inWarnMonitor[ind] = true; + } + } + + device.getCurrentState(CurrentState[ind]); + DS4State cState = CurrentState[ind]; + DS4State pState = device.getPreviousStateRef(); + //device.getPreviousState(PreviousState[ind]); + //DS4State pState = PreviousState[ind]; + + if (device.firstReport && device.IsAlive()) + { + device.firstReport = false; + /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + { + OnDeviceStatusChanged(this, ind); + }), null); + */ + } + //else if (pState.Battery != cState.Battery || device.oldCharging != device.isCharging()) + //{ + // byte tempBattery = currentBattery[ind] = cState.Battery; + // bool tempCharging = charging[ind] = device.isCharging(); + // /*uiContext?.Post(new SendOrPostCallback(delegate (object state) + // { + // OnBatteryStatusChange(this, ind, tempBattery, tempCharging); + // }), null); + // */ + //} + + if (getEnableTouchToggle(ind)) + CheckForTouchToggle(ind, cState, pState); + + cState = Mapping.SetCurveAndDeadzone(ind, cState, TempState[ind]); + + if (!recordingMacro && (useTempProfile[ind] || + containsCustomAction(ind) || containsCustomExtras(ind) || + getProfileActionCount(ind) > 0)) + { + Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this); + cState = MappedState[ind]; + } + + if (!useDInputOnly[ind]) + { + outputDevices[ind]?.ConvertandSendReport(cState, ind); + //testNewReport(ref x360reports[ind], cState, ind); + //x360controls[ind]?.SendReport(x360reports[ind]); + + //x360Bus.Parse(cState, processingData[ind].Report, ind); + // We push the translated Xinput state, and simultaneously we + // pull back any possible rumble data coming from Xinput consumers. + /*if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble)) + { + byte Big = processingData[ind].Rumble[3]; + byte Small = processingData[ind].Rumble[4]; + + if (processingData[ind].Rumble[1] == 0x08) + { + SetDevRumble(device, Big, Small, ind); + } + } + */ + } + else + { + // UseDInputOnly profile may re-map sixaxis gyro sensor values as a VJoy joystick axis (steering wheel emulation mode using VJoy output device). Handle this option because VJoy output works even in USeDInputOnly mode. + // If steering wheel emulation uses LS/RS/R2/L2 output axies then the profile should NOT use UseDInputOnly option at all because those require a virtual output device. + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(ind); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: break; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + break; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + break; + } + } + + // Output any synthetic events. + Mapping.Commit(ind); + + // Update the GUI/whatever. + DS4LightBar.updateLightBar(device, ind); + } + } + + public void LagFlashWarning(int ind, bool on) + { + if (on) + { + lag[ind] = true; + LogDebug(DS4WinWPF.Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true); + if (getFlashWhenLate()) + { + DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 }; + DS4LightBar.forcedColor[ind] = color; + DS4LightBar.forcedFlash[ind] = 2; + DS4LightBar.forcelight[ind] = true; + } + } + else + { + lag[ind] = false; + LogDebug(DS4WinWPF.Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString())); + DS4LightBar.forcelight[ind] = false; + DS4LightBar.forcedFlash[ind] = 0; + } + } + + public DS4Controls GetActiveInputControl(int ind) + { + DS4State cState = CurrentState[ind]; + DS4StateExposed eState = ExposedState[ind]; + Mouse tp = touchPad[ind]; + DS4Controls result = DS4Controls.None; + + if (DS4Controllers[ind] != null) + { + if (Mapping.getBoolButtonMapping(cState.Cross)) + result = DS4Controls.Cross; + else if (Mapping.getBoolButtonMapping(cState.Circle)) + result = DS4Controls.Circle; + else if (Mapping.getBoolButtonMapping(cState.Triangle)) + result = DS4Controls.Triangle; + else if (Mapping.getBoolButtonMapping(cState.Square)) + result = DS4Controls.Square; + else if (Mapping.getBoolButtonMapping(cState.L1)) + result = DS4Controls.L1; + else if (Mapping.getBoolTriggerMapping(cState.L2)) + result = DS4Controls.L2; + else if (Mapping.getBoolButtonMapping(cState.L3)) + result = DS4Controls.L3; + else if (Mapping.getBoolButtonMapping(cState.R1)) + result = DS4Controls.R1; + else if (Mapping.getBoolTriggerMapping(cState.R2)) + result = DS4Controls.R2; + else if (Mapping.getBoolButtonMapping(cState.R3)) + result = DS4Controls.R3; + else if (Mapping.getBoolButtonMapping(cState.DpadUp)) + result = DS4Controls.DpadUp; + else if (Mapping.getBoolButtonMapping(cState.DpadDown)) + result = DS4Controls.DpadDown; + else if (Mapping.getBoolButtonMapping(cState.DpadLeft)) + result = DS4Controls.DpadLeft; + else if (Mapping.getBoolButtonMapping(cState.DpadRight)) + result = DS4Controls.DpadRight; + else if (Mapping.getBoolButtonMapping(cState.Share)) + result = DS4Controls.Share; + else if (Mapping.getBoolButtonMapping(cState.Options)) + result = DS4Controls.Options; + else if (Mapping.getBoolButtonMapping(cState.PS)) + result = DS4Controls.PS; + else if (Mapping.getBoolAxisDirMapping(cState.LX, true)) + result = DS4Controls.LXPos; + else if (Mapping.getBoolAxisDirMapping(cState.LX, false)) + result = DS4Controls.LXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.LY, true)) + result = DS4Controls.LYPos; + else if (Mapping.getBoolAxisDirMapping(cState.LY, false)) + result = DS4Controls.LYNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RX, true)) + result = DS4Controls.RXPos; + else if (Mapping.getBoolAxisDirMapping(cState.RX, false)) + result = DS4Controls.RXNeg; + else if (Mapping.getBoolAxisDirMapping(cState.RY, true)) + result = DS4Controls.RYPos; + else if (Mapping.getBoolAxisDirMapping(cState.RY, false)) + result = DS4Controls.RYNeg; + else if (Mapping.getBoolTouchMapping(tp.leftDown)) + result = DS4Controls.TouchLeft; + else if (Mapping.getBoolTouchMapping(tp.rightDown)) + result = DS4Controls.TouchRight; + else if (Mapping.getBoolTouchMapping(tp.multiDown)) + result = DS4Controls.TouchMulti; + else if (Mapping.getBoolTouchMapping(tp.upperDown)) + result = DS4Controls.TouchUpper; + } + + return result; + } + + public bool[] touchreleased = new bool[4] { true, true, true, true }, + touchslid = new bool[4] { false, false, false, false }; + + protected virtual void CheckForTouchToggle(int deviceID, DS4State cState, DS4State pState) + { + if (!getUseTPforControls(deviceID) && cState.Touch1 && pState.PS) + { + if (GetTouchActive(deviceID) && touchreleased[deviceID]) + { + TouchActive[deviceID] = false; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOff); + touchreleased[deviceID] = false; + } + else if (touchreleased[deviceID]) + { + TouchActive[deviceID] = true; + LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOn); + touchreleased[deviceID] = false; + } + } + else + touchreleased[deviceID] = true; + } + + public virtual void StartTPOff(int deviceID) + { + if (deviceID < 4) + { + TouchActive[deviceID] = false; + } + } + + public virtual string TouchpadSlide(int ind) + { + DS4State cState = CurrentState[ind]; + string slidedir = "none"; + if (DS4Controllers[ind] != null && cState.Touch2 && + !(touchPad[ind].dragging || touchPad[ind].dragging2)) + { + if (touchPad[ind].slideright && !touchslid[ind]) + { + slidedir = "right"; + touchslid[ind] = true; + } + else if (touchPad[ind].slideleft && !touchslid[ind]) + { + slidedir = "left"; + touchslid[ind] = true; + } + else if (!touchPad[ind].slideleft && !touchPad[ind].slideright) + { + slidedir = ""; + touchslid[ind] = false; + } + } + + return slidedir; + } + + public virtual void LogDebug(String Data, bool warning = false) + { + //Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data); + if (Debug != null) + { + DebugEventArgs args = new DebugEventArgs(Data, warning); + OnDebug(this, args); + } + } + + public virtual void OnDebug(object sender, DebugEventArgs args) + { + if (Debug != null) + Debug(this, args); + } + + // sets the rumble adjusted with rumble boost. General use method + public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum) + { + if (deviceNum < 4) + { + DS4Device device = DS4Controllers[deviceNum]; + if (device != null) + SetDevRumble(device, heavyMotor, lightMotor, deviceNum); + //device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + } + + // sets the rumble adjusted with rumble boost. Method more used for + // report handling. Avoid constant checking for a device. + public void SetDevRumble(DS4Device device, + byte heavyMotor, byte lightMotor, int deviceNum) + { + byte boost = getRumbleBoost(deviceNum); + uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100; + if (lightBoosted > 255) + lightBoosted = 255; + uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100; + if (heavyBoosted > 255) + heavyBoosted = 255; + + device.setRumble((byte)lightBoosted, (byte)heavyBoosted); + } + + public DS4State getDS4State(int ind) + { + return CurrentState[ind]; + } + + public DS4State getDS4StateMapped(int ind) + { + return MappedState[ind]; + } + + public DS4State getDS4StateTemp(int ind) + { + return TempState[ind]; + } + } +} diff --git a/DS4Windows/DS4Control/ControllerSlotManager.cs b/DS4Windows/DS4Control/ControllerSlotManager.cs new file mode 100644 index 0000000000..df4cffbf83 --- /dev/null +++ b/DS4Windows/DS4Control/ControllerSlotManager.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace DS4Windows +{ + public class ControllerSlotManager + { + private List controllerColl; + public List ControllerColl { get => controllerColl; set => controllerColl = value; } + + private Dictionary controllerDict; + private Dictionary reverseControllerDict; + public Dictionary ControllerDict { get => controllerDict; } + public Dictionary ReverseControllerDict { get => reverseControllerDict; } + + public ControllerSlotManager() + { + controllerColl = new List(); + controllerDict = new Dictionary(); + reverseControllerDict = new Dictionary(); + } + + public void AddController(DS4Device device, int slotIdx) + { + controllerColl.Add(device); + controllerDict.Add(slotIdx, device); + reverseControllerDict.Add(device, slotIdx); + } + + public void RemoveController(DS4Device device, int slotIdx) + { + controllerColl.Remove(device); + controllerDict.Remove(slotIdx); + reverseControllerDict.Remove(device); + } + + public void ClearControllerList() + { + controllerColl.Clear(); + controllerDict.Clear(); + reverseControllerDict.Clear(); + } + } +} diff --git a/DS4Windows/DS4Control/DS4LightBar.cs b/DS4Windows/DS4Control/DS4LightBar.cs index 98483a38a7..c7fb7c005f 100644 --- a/DS4Windows/DS4Control/DS4LightBar.cs +++ b/DS4Windows/DS4Control/DS4LightBar.cs @@ -1,17 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Drawing; using static System.Math; using static DS4Windows.Global; +using System.Diagnostics; + namespace DS4Windows { public class DS4LightBar { private readonly static byte[/* Light On duration */, /* Light Off duration */] BatteryIndicatorDurations = { - { 0, 0 }, // 0 is for "charging" OR anything sufficiently-"charged" + { 28, 252 }, // on 10% of the time at 0 { 28, 252 }, { 56, 224 }, { 84, 196 }, @@ -19,195 +18,335 @@ public class DS4LightBar { 140, 140 }, { 168, 112 }, { 196, 84 }, - { 224, 56}, // on 80% of the time at 80, etc. - { 252, 28 } // on 90% of the time at 90 + { 224, 56 }, // on 80% of the time at 80, etc. + { 252, 28 }, // on 90% of the time at 90 + { 0, 0 } // use on 100%. 0 is for "charging" OR anything sufficiently-"charged" }; + static double[] counters = new double[4] { 0, 0, 0, 0 }; - public static double[] fadetimer = new double[4] { 0, 0, 0, 0 }; + public static Stopwatch[] fadewatches = new Stopwatch[4] + { new Stopwatch(), new Stopwatch(), new Stopwatch(), new Stopwatch() }; + static bool[] fadedirection = new bool[4] { false, false, false, false }; - static DateTime oldnow = DateTime.UtcNow; + static DateTime[] oldnow = new DateTime[4] + { DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow }; + public static bool[] forcelight = new bool[4] { false, false, false, false }; public static DS4Color[] forcedColor = new DS4Color[4]; public static byte[] forcedFlash = new byte[4]; - public static void updateLightBar(DS4Device device, int deviceNum, DS4State cState, DS4StateExposed eState, Mouse tp) + internal const int PULSE_FLASH_DURATION = 2000; + internal const double PULSE_FLASH_SEGMENTS = PULSE_FLASH_DURATION / 40; + internal const int PULSE_CHARGING_DURATION = 4000; + internal const double PULSE_CHARGING_SEGMENTS = (PULSE_CHARGING_DURATION / 40) - 2; + + public static void updateLightBar(DS4Device device, int deviceNum) { - DS4Color color; - if (!defualtLight && !forcelight[deviceNum]) + DS4Color color = new DS4Color(); + bool useForceLight = forcelight[deviceNum]; + LightbarSettingInfo lightbarSettingInfo = getLightbarSettingsInfo(deviceNum); + LightbarDS4WinInfo lightModeInfo = lightbarSettingInfo.ds4winSettings; + bool useLightRoutine = lightbarSettingInfo.mode == LightbarMode.DS4Win; + //bool useLightRoutine = false; + if (!defaultLight && !useForceLight && useLightRoutine) { - if (UseCustomLed[deviceNum]) + if (lightModeInfo.useCustomLed) { - if (LedAsBatteryIndicator[deviceNum]) + if (lightModeInfo.ledAsBattery) { - DS4Color fullColor = CustomColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, device.Battery); + ref DS4Color fullColor = ref lightModeInfo.m_CustomLed; // ref getCustomColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else - color = CustomColor[deviceNum]; + color = lightModeInfo.m_CustomLed; //getCustomColor(deviceNum); } else { - if (Rainbow[deviceNum] > 0) - {// Display rainbow + double rainbow = lightModeInfo.rainbow;// getRainbow(deviceNum); + if (rainbow > 0) + { + // Display rainbow DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion + if (now >= oldnow[deviceNum] + TimeSpan.FromMilliseconds(10)) //update by the millisecond that way it's a smooth transtion { - oldnow = now; - if (device.Charging) - counters[deviceNum] -= 1.5 * 3 / Rainbow[deviceNum]; + oldnow[deviceNum] = now; + if (device.isCharging()) + counters[deviceNum] -= 1.5 * 3 / rainbow; else - counters[deviceNum] += 1.5 * 3 / Rainbow[deviceNum]; + counters[deviceNum] += 1.5 * 3 / rainbow; } + if (counters[deviceNum] < 0) counters[deviceNum] = 180000; - if (counters[deviceNum] > 180000) + else if (counters[deviceNum] > 180000) counters[deviceNum] = 0; - if (LedAsBatteryIndicator[deviceNum]) - color = HuetoRGB((float)counters[deviceNum] % 360, (byte)(2.55 * device.Battery)); + + double maxSat = lightModeInfo.maxRainbowSat; // GetMaxSatRainbow(deviceNum); + if (lightModeInfo.ledAsBattery) + { + byte useSat = (byte)(maxSat == 1.0 ? + device.getBattery() * 2.55 : + device.getBattery() * 2.55 * maxSat); + color = HuetoRGB((float)counters[deviceNum] % 360, useSat); + } else - color = HuetoRGB((float)counters[deviceNum] % 360, 255); + color = HuetoRGB((float)counters[deviceNum] % 360, + (byte)(maxSat == 1.0 ? 255 : 255 * maxSat)); } - else if (LedAsBatteryIndicator[deviceNum]) + else if (lightModeInfo.ledAsBattery) { - //if (device.Charging == false || device.Battery >= 100) // when charged, don't show the charging animation - { - DS4Color fullColor = MainColor[deviceNum]; - DS4Color lowColor = LowColor[deviceNum]; - - color = getTransitionedColor(lowColor, fullColor, (uint)device.Battery); - } + ref DS4Color fullColor = ref lightModeInfo.m_Led; //ref getMainColor(deviceNum); + ref DS4Color lowColor = ref lightModeInfo.m_LowLed; //ref getLowColor(deviceNum); + color = getTransitionedColor(ref lowColor, ref fullColor, device.getBattery()); } else { - color = MainColor[deviceNum]; + color = getMainColor(deviceNum); } - } - if (device.Battery <= FlashAt[deviceNum] && !defualtLight && !device.Charging) + if (device.getBattery() <= lightModeInfo.flashAt && !defaultLight && !device.isCharging()) { - if (!(FlashColor[deviceNum].red == 0 && - FlashColor[deviceNum].green == 0 && - FlashColor[deviceNum].blue == 0)) - color = FlashColor[deviceNum]; - if (FlashType[deviceNum] == 1) + ref DS4Color flashColor = ref lightModeInfo.m_FlashLed; //ref getFlashColor(deviceNum); + if (!(flashColor.red == 0 && + flashColor.green == 0 && + flashColor.blue == 0)) + color = flashColor; + + if (lightModeInfo.flashType == 1) { - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 100) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += 1; + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= 1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = 100.0 * (elapsed / PULSE_FLASH_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_FLASH_DURATION) + { + elapsed = elapsed / 40; + ratio = (0 - 100.0) * (elapsed / PULSE_FLASH_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color tempCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref tempCol, ratio); } } - if (IdleDisconnectTimeout[deviceNum] > 0 && LedAsBatteryIndicator[deviceNum] && (!device.Charging || device.Battery >= 100)) - {//Fade lightbar by idle time + int idleDisconnectTimeout = getIdleDisconnectTimeout(deviceNum); + if (idleDisconnectTimeout > 0 && lightModeInfo.ledAsBattery && + (!device.isCharging() || device.getBattery() >= 100)) + { + // Fade lightbar by idle time TimeSpan timeratio = new TimeSpan(DateTime.UtcNow.Ticks - device.lastActive.Ticks); double botratio = timeratio.TotalMilliseconds; - double topratio = TimeSpan.FromSeconds(IdleDisconnectTimeout[deviceNum]).TotalMilliseconds; - double ratio = ((botratio / topratio) * 100); - if (ratio >= 50 && ratio <= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), (uint)((ratio - 50) * 2)); - else if (ratio >= 100) - color = getTransitionedColor(color, new DS4Color(0, 0, 0), 100); + double topratio = TimeSpan.FromSeconds(idleDisconnectTimeout).TotalMilliseconds; + double ratio = 100.0 * (botratio / topratio), elapsed = ratio; + if (ratio >= 50.0 && ratio < 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, + (uint)(-100.0 * (elapsed = 0.02 * (ratio - 50.0)) * (elapsed - 2.0))); + } + else if (ratio >= 100.0) + { + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, 100.0); + } + } - if (device.Charging && device.Battery < 100) - switch (ChargingType[deviceNum]) + + if (device.isCharging() && device.getBattery() < 100) + { + switch (lightModeInfo.chargingType) { case 1: - if (fadetimer[deviceNum] <= 0) - fadedirection[deviceNum] = true; - else if (fadetimer[deviceNum] >= 105) - fadedirection[deviceNum] = false; - if (fadedirection[deviceNum]) - fadetimer[deviceNum] += .1; + { + double ratio = 0.0; + + if (!fadewatches[deviceNum].IsRunning) + { + bool temp = fadedirection[deviceNum]; + fadedirection[deviceNum] = !temp; + fadewatches[deviceNum].Restart(); + ratio = temp ? 100.0 : 0.0; + } else - fadetimer[deviceNum] -= .1; - color = getTransitionedColor(color, new DS4Color(0, 0, 0), fadetimer[deviceNum]); + { + long elapsed = fadewatches[deviceNum].ElapsedMilliseconds; + + if (fadedirection[deviceNum]) + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = 100.0 * (elapsed / PULSE_CHARGING_SEGMENTS); + } + else + { + ratio = 100.0; + fadewatches[deviceNum].Stop(); + } + } + else + { + if (elapsed < PULSE_CHARGING_DURATION) + { + elapsed = elapsed / 40; + if (elapsed > PULSE_CHARGING_SEGMENTS) + elapsed = (long)PULSE_CHARGING_SEGMENTS; + ratio = (0 - 100.0) * (elapsed / PULSE_CHARGING_SEGMENTS) + 100.0; + } + else + { + ratio = 0.0; + fadewatches[deviceNum].Stop(); + } + } + } + + DS4Color emptyCol = new DS4Color(0, 0, 0); + color = getTransitionedColor(ref color, ref emptyCol, ratio); break; + } case 2: - counters[deviceNum] += .167; + { + counters[deviceNum] += 0.167; color = HuetoRGB((float)counters[deviceNum] % 360, 255); break; + } case 3: - color = ChargingColor[deviceNum]; - break; - default: + { + color = lightModeInfo.m_ChargingLed; //getChargingColor(deviceNum); break; + } + default: break; } + } } - else if (forcelight[deviceNum]) + else if (useForceLight) { color = forcedColor[deviceNum]; + useLightRoutine = true; } else if (shuttingdown) + { color = new DS4Color(0, 0, 0); - else + useLightRoutine = true; + } + else if (useLightRoutine) { - if (device.ConnectionType == ConnectionType.BT) + if (device.getConnectionType() == ConnectionType.BT) color = new DS4Color(32, 64, 64); else color = new DS4Color(0, 0, 0); } - bool distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); - if (distanceprofile && !defualtLight) - { //Thing I did for Distance - float rumble = device.LeftHeavySlowRumble / 2.55f; - byte max = Max(color.red, Max(color.green, color.blue)); - if (device.LeftHeavySlowRumble > 100) - color = getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), rumble); - else - color = getTransitionedColor(color, getTransitionedColor(new DS4Color(max, max, 0), new DS4Color(255, 0, 0), 39.6078f), device.LeftHeavySlowRumble); - } - DS4HapticState haptics = new DS4HapticState - { - LightBarColor = color - }; - if (haptics.IsLightBarSet()) + + if (useLightRoutine) { - if (forcelight[deviceNum] && forcedFlash[deviceNum] > 0) + bool distanceprofile = DistanceProfiles[deviceNum] || tempprofileDistance[deviceNum]; + //distanceprofile = (ProfilePath[deviceNum].ToLower().Contains("distance") || tempprofilename[deviceNum].ToLower().Contains("distance")); + if (distanceprofile && !defaultLight) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); - haptics.LightBarExplicitlyOff = true; + // Thing I did for Distance + float rumble = device.getLeftHeavySlowRumble() / 2.55f; + byte max = Max(color.red, Max(color.green, color.blue)); + if (device.getLeftHeavySlowRumble() > 100) + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + color = getTransitionedColor(ref maxCol, ref redCol, rumble); + } + else + { + DS4Color maxCol = new DS4Color(max, max, 0); + DS4Color redCol = new DS4Color(255, 0, 0); + DS4Color tempCol = getTransitionedColor(ref maxCol, + ref redCol, 39.6078f); + color = getTransitionedColor(ref color, ref tempCol, + device.getLeftHeavySlowRumble()); + } } - else if (device.Battery <= FlashAt[deviceNum] && FlashType[deviceNum] == 0 && !defualtLight && !device.Charging) + + DS4HapticState haptics = new DS4HapticState { - int level = device.Battery / 10; - //if (level >= 10) - //level = 0; // all values of ~0% or >~100% are rendered the same - haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; - haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; - } - else if (distanceprofile && device.LeftHeavySlowRumble > 155) //also part of Distance + LightBarColor = color + }; + + if (haptics.IsLightBarSet()) { - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.LeftHeavySlowRumble + 265)); - haptics.LightBarExplicitlyOff = true; + if (useForceLight && forcedFlash[deviceNum] > 0) + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)(25 - forcedFlash[deviceNum]); + haptics.LightBarExplicitlyOff = true; + } + else if (device.getBattery() <= lightModeInfo.flashAt && lightModeInfo.flashType == 0 && !defaultLight && !device.isCharging()) + { + int level = device.getBattery() / 10; + if (level >= 10) + level = 10; // all values of >~100% are rendered the same + + haptics.LightBarFlashDurationOn = BatteryIndicatorDurations[level, 0]; + haptics.LightBarFlashDurationOff = BatteryIndicatorDurations[level, 1]; + } + else if (distanceprofile && device.getLeftHeavySlowRumble() > 155) //also part of Distance + { + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = (byte)((-device.getLeftHeavySlowRumble() + 265)); + haptics.LightBarExplicitlyOff = true; + } + else + { + //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; + haptics.LightBarExplicitlyOff = true; + } } else { - //haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 0; haptics.LightBarExplicitlyOff = true; } + + byte tempLightBarOnDuration = device.getLightBarOnDuration(); + if (tempLightBarOnDuration != haptics.LightBarFlashDurationOn && tempLightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) + haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; + + device.SetHapticState(ref haptics); + //device.pushHapticState(ref haptics); } - else - { - haptics.LightBarExplicitlyOff = true; - } - if (device.LightBarOnDuration != haptics.LightBarFlashDurationOn && device.LightBarOnDuration != 1 && haptics.LightBarFlashDurationOn == 0) - haptics.LightBarFlashDurationOff = haptics.LightBarFlashDurationOn = 1; - if (device.LightBarOnDuration == 1) //helps better reset the color - System.Threading.Thread.Sleep(5); - device.pushHapticState(haptics); } - public static bool defualtLight = false, shuttingdown = false; + public static bool defaultLight = false, shuttingdown = false; public static DS4Color HuetoRGB(float hue, byte sat) { diff --git a/DS4Windows/DS4Control/DS4OutDevice.cs b/DS4Windows/DS4Control/DS4OutDevice.cs new file mode 100644 index 0000000000..33cbdb8caf --- /dev/null +++ b/DS4Windows/DS4Control/DS4OutDevice.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; +using Nefarius.ViGEm.Client.Targets; +using Nefarius.ViGEm.Client.Targets.DualShock4; + +namespace DS4Windows +{ + class DS4OutDevice : OutputDevice + { + public const string devtype = "DS4"; + + public DualShock4Controller cont; + private DualShock4Report report; + public DualShock4FeedbackReceivedEventHandler forceFeedbackCall; + + public DS4OutDevice(ViGEmClient client) + { + cont = new DualShock4Controller(client); + report = new DualShock4Report(); + } + + public override void ConvertandSendReport(DS4State state, int device) + { + DualShock4Buttons tempButtons = 0; + DualShock4DPadValues tempDPad = DualShock4DPadValues.None; + DualShock4SpecialButtons tempSpecial = 0; + + unchecked + { + if (state.Share) tempButtons |= DualShock4Buttons.Share; + if (state.L3) tempButtons |= DualShock4Buttons.ThumbLeft; + if (state.R3) tempButtons |= DualShock4Buttons.ThumbRight; + if (state.Options) tempButtons |= DualShock4Buttons.Options; + + if (state.DpadUp && state.DpadRight) tempDPad = DualShock4DPadValues.Northeast; + else if (state.DpadUp && state.DpadLeft) tempDPad = DualShock4DPadValues.Northwest; + else if (state.DpadUp) tempDPad = DualShock4DPadValues.North; + else if (state.DpadRight && state.DpadDown) tempDPad = DualShock4DPadValues.Southeast; + else if (state.DpadRight) tempDPad = DualShock4DPadValues.East; + else if (state.DpadDown && state.DpadLeft) tempDPad = DualShock4DPadValues.Southwest; + else if (state.DpadDown) tempDPad = DualShock4DPadValues.South; + else if (state.DpadLeft) tempDPad = DualShock4DPadValues.West; + + /*if (state.DpadUp) tempDPad = (state.DpadRight) ? DualShock4DPadValues.Northeast : DualShock4DPadValues.North; + if (state.DpadRight) tempDPad = (state.DpadDown) ? DualShock4DPadValues.Southeast : DualShock4DPadValues.East; + if (state.DpadDown) tempDPad = (state.DpadLeft) ? DualShock4DPadValues.Southwest : DualShock4DPadValues.South; + if (state.DpadLeft) tempDPad = (state.DpadUp) ? DualShock4DPadValues.Northwest : DualShock4DPadValues.West; + */ + + if (state.L1) tempButtons |= DualShock4Buttons.ShoulderLeft; + if (state.R1) tempButtons |= DualShock4Buttons.ShoulderRight; + //if (state.L2Btn) tempButtons |= DualShock4Buttons.TriggerLeft; + //if (state.R2Btn) tempButtons |= DualShock4Buttons.TriggerRight; + if (state.L2 > 0) tempButtons |= DualShock4Buttons.TriggerLeft; + if (state.R2 > 0) tempButtons |= DualShock4Buttons.TriggerRight; + + if (state.Triangle) tempButtons |= DualShock4Buttons.Triangle; + if (state.Circle) tempButtons |= DualShock4Buttons.Circle; + if (state.Cross) tempButtons |= DualShock4Buttons.Cross; + if (state.Square) tempButtons |= DualShock4Buttons.Square; + if (state.PS) tempSpecial |= DualShock4SpecialButtons.Ps; + if (state.TouchButton) tempSpecial |= DualShock4SpecialButtons.Touchpad; + //report.SetButtonsFull(tempButtons); + report.Buttons = (ushort)tempButtons; + report.SetDPad(tempDPad); + report.SpecialButtons = (byte)tempSpecial; + } + + + report.LeftTrigger = state.L2; + report.RightTrigger = state.R2; + + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device); + switch (steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LX: + report.LeftThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.LY: + report.LeftThumbX = state.LX; + report.LeftThumbY = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbX = state.RX; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RX: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = (byte)state.SASteeringWheelEmulationUnit; + report.RightThumbY = state.RY; + break; + + case SASteeringWheelEmulationAxisType.RY: + report.LeftThumbX = state.LX; + report.LeftThumbY = state.LY; + report.RightThumbX = state.RX; + report.RightThumbY = (byte)state.SASteeringWheelEmulationUnit; + break; + + case SASteeringWheelEmulationAxisType.L2R2: + report.LeftTrigger = report.RightTrigger = 0; + if (state.SASteeringWheelEmulationUnit >= 0) report.LeftTrigger = (Byte)state.SASteeringWheelEmulationUnit; + else report.RightTrigger = (Byte)state.SASteeringWheelEmulationUnit; + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + goto case SASteeringWheelEmulationAxisType.None; + + default: + // Should never come here but just in case use the NONE case as default handler.... + goto case SASteeringWheelEmulationAxisType.None; + } + + cont.SendReport(report); + } + + public override void Connect() => cont.Connect(); + public override void Disconnect() + { + if (forceFeedbackCall != null) + { + cont.FeedbackReceived -= forceFeedbackCall; + forceFeedbackCall = null; + } + + cont.Disconnect(); + cont.Dispose(); + cont = null; + } + public override string GetDeviceType() => devtype; + } +} diff --git a/DS4Windows/DS4Control/DS4StateFieldMapping.cs b/DS4Windows/DS4Control/DS4StateFieldMapping.cs new file mode 100644 index 0000000000..d1c12adf10 --- /dev/null +++ b/DS4Windows/DS4Control/DS4StateFieldMapping.cs @@ -0,0 +1,164 @@ + +namespace DS4Windows +{ + public class DS4StateFieldMapping + { + public enum ControlType: int { Unknown = 0, Button, AxisDir, Trigger, Touch, GyroDir, SwipeDir } + + public bool[] buttons = new bool[(int)DS4Controls.SwipeDown + 1]; + public byte[] axisdirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public byte[] triggers = new byte[(int)DS4Controls.SwipeDown + 1]; + public int[] gryodirs = new int[(int)DS4Controls.SwipeDown + 1]; + public byte[] swipedirs = new byte[(int)DS4Controls.SwipeDown + 1]; + public bool[] swipedirbools = new bool[(int)DS4Controls.SwipeDown + 1]; + public bool touchButton = false; + + public static ControlType[] mappedType = new ControlType[38] { ControlType.Unknown, // DS4Controls.None + ControlType.AxisDir, // DS4Controls.LXNeg + ControlType.AxisDir, // DS4Controls.LXPos + ControlType.AxisDir, // DS4Controls.LYNeg + ControlType.AxisDir, // DS4Controls.LYPos + ControlType.AxisDir, // DS4Controls.RXNeg + ControlType.AxisDir, // DS4Controls.RXPos + ControlType.AxisDir, // DS4Controls.RYNeg + ControlType.AxisDir, // DS4Controls.RYPos + ControlType.Button, // DS4Controls.L1 + ControlType.Trigger, // DS4Controls.L2 + ControlType.Button, // DS4Controls.L3 + ControlType.Button, // DS4Controls.R1 + ControlType.Trigger, // DS4Controls.R2 + ControlType.Button, // DS4Controls.R3 + ControlType.Button, // DS4Controls.Square + ControlType.Button, // DS4Controls.Triangle + ControlType.Button, // DS4Controls.Circle + ControlType.Button, // DS4Controls.Cross + ControlType.Button, // DS4Controls.DpadUp + ControlType.Button, // DS4Controls.DpadRight + ControlType.Button, // DS4Controls.DpadDown + ControlType.Button, // DS4Controls.DpadLeft + ControlType.Button, // DS4Controls.PS + ControlType.Touch, // DS4Controls.TouchLeft + ControlType.Touch, // DS4Controls.TouchUpper + ControlType.Touch, // DS4Controls.TouchMulti + ControlType.Touch, // DS4Controls.TouchRight + ControlType.Button, // DS4Controls.Share + ControlType.Button, // DS4Controls.Options + ControlType.GyroDir, // DS4Controls.GyroXPos + ControlType.GyroDir, // DS4Controls.GyroXNeg + ControlType.GyroDir, // DS4Controls.GyroZPos + ControlType.GyroDir, // DS4Controls.GyroZNeg + ControlType.SwipeDir, // DS4Controls.SwipeLeft + ControlType.SwipeDir, // DS4Controls.SwipeRight + ControlType.SwipeDir, // DS4Controls.SwipeUp + ControlType.SwipeDir, // DS4Controls.SwipeDown + }; + + public DS4StateFieldMapping() + { + } + + public DS4StateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse=false) + { + populateFieldMapping(cState, exposeState, tp, priorMouse); + } + + public void populateFieldMapping(DS4State cState, DS4StateExposed exposeState, Mouse tp, bool priorMouse = false) + { + unchecked + { + axisdirs[(int)DS4Controls.LXNeg] = cState.LX; + axisdirs[(int)DS4Controls.LXPos] = cState.LX; + axisdirs[(int)DS4Controls.LYNeg] = cState.LY; + axisdirs[(int)DS4Controls.LYPos] = cState.LY; + + axisdirs[(int)DS4Controls.RXNeg] = cState.RX; + axisdirs[(int)DS4Controls.RXPos] = cState.RX; + axisdirs[(int)DS4Controls.RYNeg] = cState.RY; + axisdirs[(int)DS4Controls.RYPos] = cState.RY; + + triggers[(int)DS4Controls.L2] = cState.L2; + triggers[(int)DS4Controls.R2] = cState.R2; + + buttons[(int)DS4Controls.L1] = cState.L1; + buttons[(int)DS4Controls.L3] = cState.L3; + buttons[(int)DS4Controls.R1] = cState.R1; + buttons[(int)DS4Controls.R3] = cState.R3; + + buttons[(int)DS4Controls.Cross] = cState.Cross; + buttons[(int)DS4Controls.Triangle] = cState.Triangle; + buttons[(int)DS4Controls.Circle] = cState.Circle; + buttons[(int)DS4Controls.Square] = cState.Square; + buttons[(int)DS4Controls.PS] = cState.PS; + buttons[(int)DS4Controls.Options] = cState.Options; + buttons[(int)DS4Controls.Share] = cState.Share; + + buttons[(int)DS4Controls.DpadUp] = cState.DpadUp; + buttons[(int)DS4Controls.DpadRight] = cState.DpadRight; + buttons[(int)DS4Controls.DpadDown] = cState.DpadDown; + buttons[(int)DS4Controls.DpadLeft] = cState.DpadLeft; + + buttons[(int)DS4Controls.TouchLeft] = tp != null ? (!priorMouse ? tp.leftDown : tp.priorLeftDown) : false; + buttons[(int)DS4Controls.TouchRight] = tp != null ? (!priorMouse ? tp.rightDown : tp.priorRightDown) : false; + buttons[(int)DS4Controls.TouchUpper] = tp != null ? (!priorMouse ? tp.upperDown : tp.priorUpperDown) : false; + buttons[(int)DS4Controls.TouchMulti] = tp != null ? (!priorMouse ? tp.multiDown : tp.priorMultiDown) : false; + + int sixAxisX = -exposeState.getOutputAccelX(); + gryodirs[(int)DS4Controls.GyroXPos] = sixAxisX > 0 ? sixAxisX : 0; + gryodirs[(int)DS4Controls.GyroXNeg] = sixAxisX < 0 ? sixAxisX : 0; + + int sixAxisZ = exposeState.getOutputAccelZ(); + gryodirs[(int)DS4Controls.GyroZPos] = sixAxisZ > 0 ? sixAxisZ : 0; + gryodirs[(int)DS4Controls.GyroZNeg] = sixAxisZ < 0 ? sixAxisZ : 0; + + swipedirs[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeftB : tp.priorSwipeLeftB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRightB : tp.priorSwipeRightB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUpB : tp.priorSwipeUpB) : (byte)0; + swipedirs[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDownB : tp.priorSwipeDownB) : (byte)0; + + swipedirbools[(int)DS4Controls.SwipeLeft] = tp != null ? (!priorMouse ? tp.swipeLeft : tp.priorSwipeLeft) : false; + swipedirbools[(int)DS4Controls.SwipeRight] = tp != null ? (!priorMouse ? tp.swipeRight : tp.priorSwipeRight) : false; + swipedirbools[(int)DS4Controls.SwipeUp] = tp != null ? (!priorMouse ? tp.swipeUp : tp.priorSwipeUp) : false; + swipedirbools[(int)DS4Controls.SwipeDown] = tp != null ? (!priorMouse ? tp.swipeDown : tp.priorSwipeDown) : false; + touchButton = cState.TouchButton; + } + } + + public void populateState(DS4State state) + { + unchecked + { + state.LX = axisdirs[(int)DS4Controls.LXNeg]; + state.LX = axisdirs[(int)DS4Controls.LXPos]; + state.LY = axisdirs[(int)DS4Controls.LYNeg]; + state.LY = axisdirs[(int)DS4Controls.LYPos]; + + state.RX = axisdirs[(int)DS4Controls.RXNeg]; + state.RX = axisdirs[(int)DS4Controls.RXPos]; + state.RY = axisdirs[(int)DS4Controls.RYNeg]; + state.RY = axisdirs[(int)DS4Controls.RYPos]; + + state.L2 = triggers[(int)DS4Controls.L2]; + state.R2 = triggers[(int)DS4Controls.R2]; + + state.L1 = buttons[(int)DS4Controls.L1]; + state.L3 = buttons[(int)DS4Controls.L3]; + state.R1 = buttons[(int)DS4Controls.R1]; + state.R3 = buttons[(int)DS4Controls.R3]; + + state.Cross = buttons[(int)DS4Controls.Cross]; + state.Triangle = buttons[(int)DS4Controls.Triangle]; + state.Circle = buttons[(int)DS4Controls.Circle]; + state.Square = buttons[(int)DS4Controls.Square]; + state.PS = buttons[(int)DS4Controls.PS]; + state.Options = buttons[(int)DS4Controls.Options]; + state.Share = buttons[(int)DS4Controls.Share]; + + state.DpadUp = buttons[(int)DS4Controls.DpadUp]; + state.DpadRight = buttons[(int)DS4Controls.DpadRight]; + state.DpadDown = buttons[(int)DS4Controls.DpadDown]; + state.DpadLeft = buttons[(int)DS4Controls.DpadLeft]; + state.TouchButton = touchButton; + } + } + } +} diff --git a/DS4Windows/DS4Control/ITouchpadBehaviour.cs b/DS4Windows/DS4Control/ITouchpadBehaviour.cs index 24a5ee74ff..80a4343c44 100644 --- a/DS4Windows/DS4Control/ITouchpadBehaviour.cs +++ b/DS4Windows/DS4Control/ITouchpadBehaviour.cs @@ -1,18 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { interface ITouchpadBehaviour { - void touchesBegan(object sender, TouchpadEventArgs arg); - void touchesMoved(object sender, TouchpadEventArgs arg); - void touchButtonUp(object sender, TouchpadEventArgs arg); - void touchButtonDown(object sender, TouchpadEventArgs arg); - void touchesEnded(object sender, TouchpadEventArgs arg); - void sixaxisMoved(object sender, SixAxisEventArgs unused); - void touchUnchanged(object sender, EventArgs unused); + void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg); + void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg); + void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg); + void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs unused); + void touchUnchanged(DS4Touchpad sender, EventArgs unused); } } diff --git a/DS4Windows/DS4Control/InputMethods.cs b/DS4Windows/DS4Control/InputMethods.cs index 278f86e312..09f4c845f5 100644 --- a/DS4Windows/DS4Control/InputMethods.cs +++ b/DS4Windows/DS4Control/InputMethods.cs @@ -1,210 +1,209 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Runtime.InteropServices; +using System.Security; + namespace DS4Windows { + [SuppressUnmanagedCodeSecurity] class InputMethods { - private static INPUT[] sendInputs = new INPUT[2]; // will allow for keyboard + mouse/tablet input within one SendInput call, or two mouse events - private static object lockob = new object(); public static void MoveCursorBy(int x, int y) { - lock (lockob) + if (x != 0 || y != 0) { - if (x != 0 || y != 0) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = MOUSEEVENTF_MOVE; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = x; - sendInputs[0].Data.Mouse.Y = y; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_MOVE; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = x; + temp.Data.Mouse.Y = y; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } } public static void MouseWheel(int vertical, int horizontal) { - lock (lockob) + INPUT[] tempInput = new INPUT[2]; + uint inputs = 0; + ref INPUT temp = ref tempInput[inputs]; + if (vertical != 0) { - uint inputs = 0; - if (vertical != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_WHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)vertical; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - if (horizontal != 0) - { - sendInputs[inputs].Type = INPUT_MOUSE; - sendInputs[inputs].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[inputs].Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; - sendInputs[inputs].Data.Mouse.MouseData = (uint)horizontal; - sendInputs[inputs].Data.Mouse.Time = 0; - sendInputs[inputs].Data.Mouse.X = 0; - sendInputs[inputs].Data.Mouse.Y = 0; - inputs++; - } - SendInput(inputs, sendInputs, (int)inputs * Marshal.SizeOf(sendInputs[0])); + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_WHEEL; + temp.Data.Mouse.MouseData = (uint)vertical; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } - } - public static void MouseEvent(uint mouseButton) - { - lock (lockob) + if (horizontal != 0) { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + temp = ref tempInput[inputs]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = MOUSEEVENTF_HWHEEL; + temp.Data.Mouse.MouseData = (uint)horizontal; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + inputs++; } + + SendInput(inputs, tempInput, (int)inputs * Marshal.SizeOf(tempInput[0])); } - public static void MouseEvent(uint mouseButton, int type) + public static void MouseEvent(uint mouseButton) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = mouseButton; - sendInputs[0].Data.Mouse.MouseData = (uint)type; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = 0; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performLeftClick() + public static void MouseEvent(uint mouseButton, int type) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + temp.Type = INPUT_MOUSE; + temp.Data.Mouse.ExtraInfo = IntPtr.Zero; + temp.Data.Mouse.Flags = mouseButton; + temp.Data.Mouse.MouseData = (uint)type; + temp.Data.Mouse.Time = 0; + temp.Data.Mouse.X = 0; + temp.Data.Mouse.Y = 0; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performRightClick() + public static void performSCKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performMiddleClick() + public static void performKeyPress(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP; - sendInputs[0].Data.Mouse.MouseData = 0; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = 1; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performFourthClick() + public static void performSCKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_MOUSE; - sendInputs[0].Data.Mouse.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Mouse.Flags = 0; - sendInputs[0].Data.Mouse.Flags |= MOUSEEVENTF_XBUTTONDOWN | MOUSEEVENTF_XBUTTONUP; - sendInputs[0].Data.Mouse.MouseData = 1; - sendInputs[0].Data.Mouse.Time = 0; - sendInputs[0].Data.Mouse.X = 0; - sendInputs[0].Data.Mouse.Y = 0; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | curflags; + temp.Data.Keyboard.Scan = scancode; + temp.Data.Keyboard.Time = 0; + //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performSCKeyPress(ushort key) + + public static void performKeyRelease(ushort key) { - lock (lockob) - { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); - } + INPUT[] tempInput = new INPUT[1]; + ref INPUT temp = ref tempInput[0]; + ushort scancode = scancodeFromVK(key); + bool extended = (scancode & 0x100) != 0; + uint curflags = extended ? KEYEVENTF_EXTENDEDKEY : 0; + + temp.Type = INPUT_KEYBOARD; + temp.Data.Keyboard.ExtraInfo = IntPtr.Zero; + temp.Data.Keyboard.Flags = curflags | KEYEVENTF_KEYUP; + temp.Data.Keyboard.Scan = scancode; + //sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; + //sendInputs[0].Data.Keyboard.Scan = 0; + temp.Data.Keyboard.Time = 0; + temp.Data.Keyboard.Vk = key; + uint result = SendInput(1, tempInput, Marshal.SizeOf(tempInput[0])); } - public static void performKeyPress(ushort key) + private static ushort scancodeFromVK(uint vkey) { - lock (lockob) + ushort scancode = 0; + if (vkey == VK_PAUSE) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = 1; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + // MapVirtualKey does not work with VK_PAUSE + scancode = 0x45; } - } - - public static void performSCKeyRelease(ushort key) - { - lock (lockob) + else { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = MapVirtualKey(key, MAPVK_VK_TO_VSC); - sendInputs[0].Data.Keyboard.Time = 0; - //sendInputs[0].Data.Keyboard.Vk = MapVirtualKey(key, MAPVK_VK_TO_VSC); - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + scancode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC); } - } - public static void performKeyRelease(ushort key) - { - lock (lockob) + switch (vkey) { - sendInputs[0].Type = INPUT_KEYBOARD; - sendInputs[0].Data.Keyboard.ExtraInfo = IntPtr.Zero; - sendInputs[0].Data.Keyboard.Flags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; - sendInputs[0].Data.Keyboard.Scan = 0; - sendInputs[0].Data.Keyboard.Time = 0; - sendInputs[0].Data.Keyboard.Vk = key; - uint result = SendInput(1, sendInputs, Marshal.SizeOf(sendInputs[0])); + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_INSERT: + case VK_DELETE: + case VK_DIVIDE: + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_VOLUME_MUTE: + case VK_VOLUME_DOWN: + case VK_VOLUME_UP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_LAUNCH_MEDIA_SELECT: + case VK_BROWSER_HOME: + case VK_LAUNCH_MAIL: + case VK_LAUNCH_APP1: + case VK_LAUNCH_APP2: + case VK_APPS: + { + scancode |= (ushort)EXTENDED_FLAG; // set extended bit + break; + } } + + return scancode; } /// @@ -277,7 +276,14 @@ internal struct MOUSEINPUT MOUSEEVENTF_XBUTTONDOWN = 128, MOUSEEVENTF_XBUTTONUP = 256, KEYEVENTF_EXTENDEDKEY = 1, KEYEVENTF_KEYUP = 2, MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x1000, MOUSEEVENTF_MIDDLEWDOWN = 0x0020, MOUSEEVENTF_MIDDLEWUP = 0x0040, - KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004; + KEYEVENTF_SCANCODE = 0x0008, MAPVK_VK_TO_VSC = 0, KEYEVENTF_UNICODE = 0x0004, EXTENDED_FLAG = 0x100; + + internal const uint VK_PAUSE = 0x13, VK_LEFT = 0x25, VK_UP = 0x26, VK_RIGHT = 0x27, VK_DOWN = 0x28, + VK_PRIOR = 0x21, VK_NEXT = 0x22, VK_END = 0x23, VK_HOME = 0x24, VK_INSERT = 0x2D, VK_DELETE = 0x2E, VK_APPS = 0x5D, + VK_DIVIDE = 0x6F, VK_NUMLOCK = 0x90, VK_RCONTROL = 0xA3, VK_RMENU = 0xA5, VK_BROWSER_HOME = 0xAC, + VK_VOLUME_MUTE = 0xAD, VK_VOLUME_DOWN = 0xAE, VK_VOLUME_UP = 0xAF, + VK_MEDIA_NEXT_TRACK = 0xB0, VK_MEDIA_PREV_TRACK = 0xB1, VK_MEDIA_STOP = 0xB2, VK_MEDIA_PLAY_PAUSE = 0xB3, + VK_LAUNCH_MAIL = 0xB4, VK_LAUNCH_MEDIA_SELECT = 0xB5, VK_LAUNCH_APP1 = 0xB6, VK_LAUNCH_APP2 = 0xB7; [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputs); @@ -285,6 +291,7 @@ internal struct MOUSEINPUT private static extern ushort MapVirtualKey(uint uCode, uint uMapType); [DllImport("user32.dll", SetLastError = true)] static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); + //Not used, just here public static void DownKeys(ushort key) { @@ -301,5 +308,5 @@ public static void ReleaseKeys(ushort key) keybd_event((byte)key, 0, (int)KEYEVENTF_KEYUP, 0); } } - } + diff --git a/DS4Windows/DS4Control/Log.cs b/DS4Windows/DS4Control/Log.cs index f581f77e1f..a2d8815d08 100644 --- a/DS4Windows/DS4Control/Log.cs +++ b/DS4Windows/DS4Control/Log.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace DS4Windows { - public class Log + public class AppLogger { public static event EventHandler TrayIconLog; public static event EventHandler GuiLog; diff --git a/DS4Windows/DS4Control/MacroParser.cs b/DS4Windows/DS4Control/MacroParser.cs new file mode 100644 index 0000000000..0950eb7863 --- /dev/null +++ b/DS4Windows/DS4Control/MacroParser.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace DS4Windows +{ + public class MacroParser + { + private bool loaded; + private List macroSteps; + private int[] inputMacro; + private Dictionary keydown = new Dictionary(); + public static Dictionary macroInputNames = new Dictionary() + { + [256] = "Left Mouse Button", [257] = "Right Mouse Button", + [258] = "Middle Mouse Button", [259] = "4th Mouse Button", + [260] = "5th Mouse Button", [261] = "A Button", + [262] = "B Button", [263] = "X Button", + [264] = "Y Button", [265] = "Start", + [266] = "Back", [267] = "Up Button", + [268] = "Down Button", [269] = "Left Button", + [270] = "Right Button", [271] = "Guide", + [272] = "Left Bumper", [273] = "Right Bumper", + [274] = "Left Trigger", [275] = "Right Trigger", + [276] = "Left Stick", [277] = "Right Stick", + [278] = "LS Right", [279] = "LS Left", + [280] = "LS Down", [281] = "LS Up", + [282] = "RS Right", [283] = "RS Left", + [284] = "RS Down", [285] = "RS Up", + }; + + public List MacroSteps { get => macroSteps; } + + public MacroParser(int[] macro) + { + macroSteps = new List(); + inputMacro = macro; + } + + public void LoadMacro() + { + if (loaded) + { + return; + } + + keydown.Clear(); + for(int i = 0; i < inputMacro.Length; i++) + { + int value = inputMacro[i]; + MacroStep step = ParseStep(value); + macroSteps.Add(step); + } + + loaded = true; + } + + public List GetMacroStrings() + { + if (!loaded) + { + LoadMacro(); + } + + List result = new List(); + foreach(MacroStep step in macroSteps) + { + result.Add(step.Name); + } + + return result; + } + + private MacroStep ParseStep(int value) + { + string name = string.Empty; + MacroStep.StepType type = MacroStep.StepType.ActDown; + MacroStep.StepOutput outType = MacroStep.StepOutput.Key; + + if (value >= 1000000000) + { + outType = MacroStep.StepOutput.Lightbar; + if (value > 1000000000) + { + type = MacroStep.StepType.ActDown; + string lb = value.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + name = $"Lightbar Color: {r},{g},{b}"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Reset Lightbar"; + } + } + else if (value >= 1000000) + { + outType = MacroStep.StepOutput.Rumble; + if (value > 1000000) + { + type = MacroStep.StepType.ActDown; + string r = value.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + name = $"Rumble {heavy}, {light} ({Math.Round((heavy * .75f + light * .25f) / 2.55f, 1)}%)"; + } + else + { + type = MacroStep.StepType.ActUp; + name = "Stop Rumble"; + } + } + else if (value >= 300) // ints over 300 used to delay + { + type = MacroStep.StepType.Wait; + outType = MacroStep.StepOutput.None; + name = $"Wait {(value - 300).ToString()} ms"; + } + else + { + // anything above 255 is not a key value + outType = value <= 255 ? MacroStep.StepOutput.Key : MacroStep.StepOutput.Button; + keydown.TryGetValue(value, out bool isdown); + if (!isdown) + { + type = MacroStep.StepType.ActDown; + keydown.Add(value, true); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + else + { + type = MacroStep.StepType.ActUp; + keydown.Remove(value); + if (outType == MacroStep.StepOutput.Key) + { + name = KeyInterop.KeyFromVirtualKey(value).ToString(); + } + else + { + macroInputNames.TryGetValue(value, out name); + } + } + } + + MacroStep step = new MacroStep(value, name, type, outType); + return step; + } + + public void Reset() + { + loaded = false; + } + } + + public class MacroStep + { + public enum StepType : uint + { + ActDown, + ActUp, + Wait, + } + + public enum StepOutput : uint + { + None, + Key, + Button, + Rumble, + Lightbar, + } + + private string name; + private int value; + private StepType actType; + private StepOutput outputType; + + public string Name + { + get => name; + set + { + name = value; + NameChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler NameChanged; + public int Value + { + get => value; + set + { + this.value = value; + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ValueChanged; + public StepType ActType { get => actType; } + public StepOutput OutputType { get => outputType; } + + public MacroStep(int value, string name, StepType act, StepOutput output) + { + this.value = value; + this.name = name; + actType = act; + outputType = output; + + ValueChanged += MacroStep_ValueChanged; + } + + private void MacroStep_ValueChanged(object sender, EventArgs e) + { + if (actType == StepType.Wait) + { + Name = $"Wait {value-300}ms"; + } + else if (outputType == StepOutput.Rumble) + { + int result = value; + result -= 1000000; + int curHeavy = result / 1000; + int curLight = result - (curHeavy * 1000); + Name = $"Rumble {curHeavy},{curLight}"; + } + else if (outputType == StepOutput.Lightbar) + { + int temp = value - 1000000000; + int r = temp / 1000000; + temp -= (r * 1000000); + int g = temp / 1000; + temp -= (g * 1000); + int b = temp; + Name = $"Lightbar Color: {r},{g},{b}"; + } + } + } +} diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index 24b06ed4d9..3c057c08c1 100644 --- a/DS4Windows/DS4Control/Mapping.cs +++ b/DS4Windows/DS4Control/Mapping.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; using System.Diagnostics; using static DS4Windows.Global; +using System.Drawing; // Point struct + namespace DS4Windows { public class Mapping @@ -33,13 +34,19 @@ public class KeyPresses } public Dictionary keyPresses = new Dictionary(); - public void SavePrevious(bool performClear) + public void SaveToPrevious(bool performClear) { previousClicks = currentClicks; if (performClear) currentClicks.leftCount = currentClicks.middleCount = currentClicks.rightCount = currentClicks.fourthCount = currentClicks.fifthCount = currentClicks.wUpCount = currentClicks.wDownCount = currentClicks.toggleCount = 0; - foreach (KeyPresses kp in keyPresses.Values) + + //foreach (KeyPresses kp in keyPresses.Values) + Dictionary.ValueCollection keyValues = keyPresses.Values; + for (var keyEnum = keyValues.GetEnumerator(); keyEnum.MoveNext();) + //for (int i = 0, kpCount = keyValues.Count; i < kpCount; i++) { + //KeyPresses kp = keyValues.ElementAt(i); + KeyPresses kp = keyEnum.Current; kp.previous = kp.current; if (performClear) { @@ -55,8 +62,128 @@ public class ActionState public bool[] dev = new bool[4]; } + struct ControlToXInput + { + public DS4Controls ds4input; + public DS4Controls xoutput; + + public ControlToXInput(DS4Controls input, DS4Controls output) + { + ds4input = input; xoutput = output; + } + } + + static Queue[] customMapQueue = new Queue[4] + { + new Queue(), new Queue(), + new Queue(), new Queue() + }; + + struct DS4Vector2 + { + public double x; + public double y; + + public DS4Vector2(double x, double y) + { + this.x = x; + this.y = y; + } + } + + class DS4SquareStick + { + public DS4Vector2 current; + public DS4Vector2 squared; + + public DS4SquareStick() + { + current = new DS4Vector2(0.0, 0.0); + squared = new DS4Vector2(0.0, 0.0); + } + + public void CircleToSquare(double roundness) + { + const double PiOverFour = Math.PI / 4.0; + + // Determine the theta angle + double angle = Math.Atan2(current.y, -current.x); + angle += Math.PI; + double cosAng = Math.Cos(angle); + // Scale according to which wall we're clamping to + // X+ wall + if (angle <= PiOverFour || angle > 7.0 * PiOverFour) + { + double tempVal = 1.0 / cosAng; + //Console.WriteLine("1 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y+ wall + else if (angle > PiOverFour && angle <= 3.0 * PiOverFour) + { + double tempVal = 1.0 / Math.Sin(angle); + //Console.WriteLine("2 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // X- wall + else if (angle > 3.0 * PiOverFour && angle <= 5.0 * PiOverFour) + { + double tempVal = -1.0 / cosAng; + //Console.WriteLine("3 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + // Y- wall + else if (angle > 5.0 * PiOverFour && angle <= 7.0 * PiOverFour) + { + double tempVal = -1.0 / Math.Sin(angle); + //Console.WriteLine("4 ANG: {0} | TEMP: {1}", angle, tempVal); + squared.x = current.x * tempVal; + squared.y = current.y * tempVal; + } + else return; + + //double lengthOld = Math.Sqrt((x * x) + (y * y)); + double length = current.x / cosAng; + //Console.WriteLine("LENGTH TEST ({0}) ({1}) {2}", lengthOld, length, (lengthOld == length).ToString()); + double factor = Math.Pow(length, roundness); + //double ogX = current.x, ogY = current.y; + current.x += (squared.x - current.x) * factor; + current.y += (squared.y - current.y) * factor; + //Console.WriteLine("INPUT: {0} {1} | {2} {3} | {4} {5} | {6} {7}", + // ogX, ogY, current.x, current.y, squared.x, squared.y, length, factor); + } + } + + static DS4SquareStick[] outSqrStk = new DS4SquareStick[4] { new DS4SquareStick(), + new DS4SquareStick(), new DS4SquareStick(), new DS4SquareStick()}; + + public static byte[] gyroStickX = new byte[4] { 128, 128, 128, 128 }; + public static byte[] gyroStickY = new byte[4] { 128, 128, 128, 128 }; + + static ReaderWriterLockSlim syncStateLock = new ReaderWriterLockSlim(); + public static SyntheticState globalState = new SyntheticState(); - public static SyntheticState[] deviceState = { new SyntheticState(), new SyntheticState(), new SyntheticState(), new SyntheticState() }; + public static SyntheticState[] deviceState = new SyntheticState[4] + { new SyntheticState(), new SyntheticState(), new SyntheticState(), + new SyntheticState() }; + + public static DS4StateFieldMapping[] fieldMappings = new DS4StateFieldMapping[4] { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] outputFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; + public static DS4StateFieldMapping[] previousFieldMappings = new DS4StateFieldMapping[4] + { + new DS4StateFieldMapping(), new DS4StateFieldMapping(), new DS4StateFieldMapping(), + new DS4StateFieldMapping() + }; // TODO When we disconnect, process a null/dead state to release any keys or buttons. public static DateTime oldnow = DateTime.UtcNow; @@ -64,232 +191,299 @@ public class ActionState private static int wheel = 0, keyshelddown = 0; //mapcustom - public static bool[] pressedonce = new bool[261], macrodone = new bool[34]; + public static bool[] pressedonce = new bool[261], macrodone = new bool[38]; static bool[] macroControl = new bool[25]; + static uint macroCount = 0; + static Dictionary[] macroTaskQueue = new Dictionary[4] { new Dictionary(), new Dictionary(), new Dictionary(), new Dictionary() }; //actions - public static int[] fadetimer = { 0, 0, 0, 0 }; - public static int[] prevFadetimer = { 0, 0, 0, 0 }; + public static int[] fadetimer = new int[4] { 0, 0, 0, 0 }; + public static int[] prevFadetimer = new int[4] { 0, 0, 0, 0 }; public static DS4Color[] lastColor = new DS4Color[4]; public static List actionDone = new List(); - //public static List[] actionDone = { new List(), new List(), new List(), new List() }; - //public static bool[,] actionDone = new bool[4, 50]; public static SpecialAction[] untriggeraction = new SpecialAction[4]; public static DateTime[] nowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; public static DateTime[] oldnowAction = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - public static int[] untriggerindex = { -1, -1, -1, -1 }; - public static DateTime[] oldnowKeyAct = { DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; - private static bool tappedOnce = false, firstTouch = false, secondtouchbegin = false; - private static DateTime pastTime, firstTap, TimeofEnd; + public static int[] untriggerindex = new int[4] { -1, -1, -1, -1 }; + public static DateTime[] oldnowKeyAct = new DateTime[4] { DateTime.MinValue, + DateTime.MinValue, DateTime.MinValue, DateTime.MinValue }; + + private static DS4Controls[] shiftTriggerMapping = new DS4Controls[26] { DS4Controls.None, DS4Controls.Cross, DS4Controls.Circle, DS4Controls.Square, + DS4Controls.Triangle, DS4Controls.Options, DS4Controls.Share, DS4Controls.DpadUp, DS4Controls.DpadDown, + DS4Controls.DpadLeft, DS4Controls.DpadRight, DS4Controls.PS, DS4Controls.L1, DS4Controls.R1, DS4Controls.L2, + DS4Controls.R2, DS4Controls.L3, DS4Controls.R3, DS4Controls.TouchLeft, DS4Controls.TouchUpper, DS4Controls.TouchMulti, + DS4Controls.TouchRight, DS4Controls.GyroZNeg, DS4Controls.GyroZPos, DS4Controls.GyroXPos, DS4Controls.GyroXNeg, + }; + + private static int[] ds4ControlMapping = new int[38] { 0, // DS4Control.None + 16, // DS4Controls.LXNeg + 20, // DS4Controls.LXPos + 17, // DS4Controls.LYNeg + 21, // DS4Controls.LYPos + 18, // DS4Controls.RXNeg + 22, // DS4Controls.RXPos + 19, // DS4Controls.RYNeg + 23, // DS4Controls.RYPos + 3, // DS4Controls.L1 + 24, // DS4Controls.L2 + 5, // DS4Controls.L3 + 4, // DS4Controls.R1 + 25, // DS4Controls.R2 + 6, // DS4Controls.R3 + 13, // DS4Controls.Square + 14, // DS4Controls.Triangle + 15, // DS4Controls.Circle + 12, // DS4Controls.Cross + 7, // DS4Controls.DpadUp + 10, // DS4Controls.DpadRight + 8, // DS4Controls.DpadDown + 9, // DS4Controls.DpadLeft + 11, // DS4Controls.PS + 27, // DS4Controls.TouchLeft + 29, // DS4Controls.TouchUpper + 26, // DS4Controls.TouchMulti + 28, // DS4Controls.TouchRight + 1, // DS4Controls.Share + 2, // DS4Controls.Options + 31, // DS4Controls.GyroXPos + 30, // DS4Controls.GyroXNeg + 33, // DS4Controls.GyroZPos + 32, // DS4Controls.GyroZNeg + 34, // DS4Controls.SwipeLeft + 35, // DS4Controls.SwipeRight + 36, // DS4Controls.SwipeUp + 37 // DS4Controls.SwipeDown + }; + + // Define here to save some time processing. + // It is enough to feel a difference during gameplay. + // 201907: Commented out these temp variables because those were not actually used anymore (value was assigned but it was never used anywhere) + //private static int[] rsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //private static int[] lsOutCurveModeArray = new int[4] { 0, 0, 0, 0 }; + //static bool tempBool = false; + //private static double[] tempDoubleArray = new double[4] { 0.0, 0.0, 0.0, 0.0 }; + //private static int[] tempIntArray = new int[4] { 0, 0, 0, 0 }; - //special macros + // Special macros static bool altTabDone = true; - static DateTime altTabNow = DateTime.UtcNow, oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); + static DateTime altTabNow = DateTime.UtcNow, + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromSeconds(1); - //mouse + // Mouse public static int mcounter = 34; public static int mouseaccel = 0; public static int prevmouseaccel = 0; private static double horizontalRemainder = 0.0, verticalRemainder = 0.0; + public const int MOUSESPEEDFACTOR = 48; + private const double MOUSESTICKOFFSET = 0.54; public static void Commit(int device) { SyntheticState state = deviceState[device]; - lock (globalState) - { - globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; - globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; - globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; - globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; - globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; - globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; - globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; - globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; - globalState.currentClicks.toggle = state.currentClicks.toggle; - - if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - } - else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - } - - if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) - { - if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); - else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); - - if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - - if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - - if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - - if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - - if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 100); - oldnow = DateTime.UtcNow; - wheel = 100; - } - else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) - wheel = 0; + syncStateLock.EnterWriteLock(); - if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) - { - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -100); - oldnow = DateTime.UtcNow; - wheel = -100; - } - if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) - wheel = 0; + globalState.currentClicks.leftCount += state.currentClicks.leftCount - state.previousClicks.leftCount; + globalState.currentClicks.middleCount += state.currentClicks.middleCount - state.previousClicks.middleCount; + globalState.currentClicks.rightCount += state.currentClicks.rightCount - state.previousClicks.rightCount; + globalState.currentClicks.fourthCount += state.currentClicks.fourthCount - state.previousClicks.fourthCount; + globalState.currentClicks.fifthCount += state.currentClicks.fifthCount - state.previousClicks.fifthCount; + globalState.currentClicks.wUpCount += state.currentClicks.wUpCount - state.previousClicks.wUpCount; + globalState.currentClicks.wDownCount += state.currentClicks.wDownCount - state.previousClicks.wDownCount; + globalState.currentClicks.toggleCount += state.currentClicks.toggleCount - state.previousClicks.toggleCount; + globalState.currentClicks.toggle = state.currentClicks.toggle; + + if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + } + else if (globalState.currentClicks.toggleCount != 0 && globalState.previousClicks.toggleCount == 0 && !globalState.currentClicks.toggle) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + } + + if (globalState.currentClicks.toggleCount == 0 && globalState.previousClicks.toggleCount == 0) + { + if (globalState.currentClicks.leftCount != 0 && globalState.previousClicks.leftCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); + else if (globalState.currentClicks.leftCount == 0 && globalState.previousClicks.leftCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); + + if (globalState.currentClicks.middleCount != 0 && globalState.previousClicks.middleCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + else if (globalState.currentClicks.middleCount == 0 && globalState.previousClicks.middleCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + + if (globalState.currentClicks.rightCount != 0 && globalState.previousClicks.rightCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + else if (globalState.currentClicks.rightCount == 0 && globalState.previousClicks.rightCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + + if (globalState.currentClicks.fourthCount != 0 && globalState.previousClicks.fourthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + else if (globalState.currentClicks.fourthCount == 0 && globalState.previousClicks.fourthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + + if (globalState.currentClicks.fifthCount != 0 && globalState.previousClicks.fifthCount == 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + else if (globalState.currentClicks.fifthCount == 0 && globalState.previousClicks.fifthCount != 0) + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + + if (globalState.currentClicks.wUpCount != 0 && globalState.previousClicks.wUpCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, 120); + oldnow = DateTime.UtcNow; + wheel = 120; + } + else if (globalState.currentClicks.wUpCount == 0 && globalState.previousClicks.wUpCount != 0) + wheel = 0; + + if (globalState.currentClicks.wDownCount != 0 && globalState.previousClicks.wDownCount == 0) + { + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, -120); + oldnow = DateTime.UtcNow; + wheel = -120; } + if (globalState.currentClicks.wDownCount == 0 && globalState.previousClicks.wDownCount != 0) + wheel = 0; + } - if (wheel != 0) //Continue mouse wheel movement + if (wheel != 0) //Continue mouse wheel movement + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(100) && !pressagain) - { - oldnow = now; - InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); - } + oldnow = now; + InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_WHEEL, wheel); } + } + + // Merge and synthesize all key presses/releases that are present in this device's mapping. + // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule + Dictionary.KeyCollection kvpKeys = state.keyPresses.Keys; + //foreach (KeyValuePair kvp in state.keyPresses) + //for (int i = 0, keyCount = kvpKeys.Count; i < keyCount; i++) + for (var keyEnum = kvpKeys.GetEnumerator(); keyEnum.MoveNext();) + { + //UInt16 kvpKey = kvpKeys.ElementAt(i); + UInt16 kvpKey = keyEnum.Current; + SyntheticState.KeyPresses kvpValue = state.keyPresses[kvpKey]; - // Merge and synthesize all key presses/releases that are present in this device's mapping. - // TODO what about the rest? e.g. repeat keys really ought to be on some set schedule - foreach (KeyValuePair kvp in state.keyPresses) + SyntheticState.KeyPresses gkp; + if (globalState.keyPresses.TryGetValue(kvpKey, out gkp)) { - SyntheticState.KeyPresses gkp; - if (globalState.keyPresses.TryGetValue(kvp.Key, out gkp)) - { - gkp.current.vkCount += kvp.Value.current.vkCount - kvp.Value.previous.vkCount; - gkp.current.scanCodeCount += kvp.Value.current.scanCodeCount - kvp.Value.previous.scanCodeCount; - gkp.current.repeatCount += kvp.Value.current.repeatCount - kvp.Value.previous.repeatCount; - gkp.current.toggle = kvp.Value.current.toggle; - gkp.current.toggleCount += kvp.Value.current.toggleCount - kvp.Value.previous.toggleCount; - } + gkp.current.vkCount += kvpValue.current.vkCount - kvpValue.previous.vkCount; + gkp.current.scanCodeCount += kvpValue.current.scanCodeCount - kvpValue.previous.scanCodeCount; + gkp.current.repeatCount += kvpValue.current.repeatCount - kvpValue.previous.repeatCount; + gkp.current.toggle = kvpValue.current.toggle; + gkp.current.toggleCount += kvpValue.current.toggleCount - kvpValue.previous.toggleCount; + } + else + { + gkp = new SyntheticState.KeyPresses(); + gkp.current = kvpValue.current; + globalState.keyPresses[kvpKey] = gkp; + } + if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) + { + if (gkp.current.scanCodeCount != 0) + InputMethods.performSCKeyPress(kvpKey); else + InputMethods.performKeyPress(kvpKey); + } + else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC + InputMethods.performSCKeyRelease(kvpKey); + else + InputMethods.performKeyRelease(kvpKey); + } + else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + { + if (gkp.current.scanCodeCount != 0) { - gkp = new SyntheticState.KeyPresses(); - gkp.current = kvp.Value.current; - globalState.keyPresses[kvp.Key] = gkp; - } - if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && gkp.current.toggle) - { - if (gkp.current.scanCodeCount != 0) - InputMethods.performSCKeyPress(kvp.Key); - else - InputMethods.performKeyPress(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performSCKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.toggleCount != 0 && gkp.previous.toggleCount == 0 && !gkp.current.toggle) + else { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - InputMethods.performSCKeyRelease(kvp.Key); - else - InputMethods.performKeyRelease(kvp.Key); + oldnow = DateTime.UtcNow; + InputMethods.performKeyPress(kvpKey); + pressagain = false; + keyshelddown = kvpKey; } - else if (gkp.current.vkCount + gkp.current.scanCodeCount != 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount == 0) + } + else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition + ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms + { + if (keyshelddown == kvpKey) { - if (gkp.current.scanCodeCount != 0) - { - oldnow = DateTime.UtcNow; - InputMethods.performSCKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; - } - else + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) { - oldnow = DateTime.UtcNow; - InputMethods.performKeyPress(kvp.Key); - pressagain = false; - keyshelddown = kvp.Key; + oldnow = now; + pressagain = true; } - } - else if (gkp.current.toggleCount != 0 || gkp.previous.toggleCount != 0 || gkp.current.repeatCount != 0 || // repeat or SC/VK transition - ((gkp.previous.scanCodeCount == 0) != (gkp.current.scanCodeCount == 0))) //repeat keystroke after 500ms - { - if (keyshelddown == kvp.Key) + if (pressagain && gkp.current.scanCodeCount != 0) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(500) && !pressagain) + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { oldnow = now; - pressagain = true; - } - if (pressagain && gkp.current.scanCodeCount != 0) - { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performSCKeyPress(kvp.Key); - } + InputMethods.performSCKeyPress(kvpKey); } - else if (pressagain) + } + else if (pressagain) + { + now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) { - now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(25) && pressagain) - { - oldnow = now; - InputMethods.performKeyPress(kvp.Key); - } + oldnow = now; + InputMethods.performKeyPress(kvpKey); } } } - if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + } + if ((gkp.current.toggleCount == 0 && gkp.previous.toggleCount == 0) && gkp.current.vkCount + gkp.current.scanCodeCount == 0 && gkp.previous.vkCount + gkp.previous.scanCodeCount != 0) + { + if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC { - if (gkp.previous.scanCodeCount != 0) // use the last type of VK/SC - { - InputMethods.performSCKeyRelease(kvp.Key); - pressagain = false; - } - else - { - InputMethods.performKeyRelease(kvp.Key); - pressagain = false; - } + InputMethods.performSCKeyRelease(kvpKey); + pressagain = false; + } + else + { + InputMethods.performKeyRelease(kvpKey); + pressagain = false; } } - globalState.SavePrevious(false); } - state.SavePrevious(true); + globalState.SaveToPrevious(false); + + syncStateLock.ExitWriteLock(); + state.SaveToPrevious(true); } + public enum Click { None, Left, Middle, Right, Fourth, Fifth, WUP, WDOWN }; public static void MapClick(int device, Click mouseClick) { @@ -316,48 +510,19 @@ public static void MapClick(int device, Click mouseClick) case Click.WDOWN: deviceState[device].currentClicks.wDownCount++; break; + default: break; } } public static int DS4ControltoInt(DS4Controls ctrl) { - switch (ctrl) - { - case DS4Controls.Share: return 1; - case DS4Controls.Options: return 2; - case DS4Controls.L1: return 3; - case DS4Controls.R1: return 4; - case DS4Controls.L3: return 5; - case DS4Controls.R3: return 6; - case DS4Controls.DpadUp: return 7; - case DS4Controls.DpadDown: return 8; - case DS4Controls.DpadLeft: return 9; - case DS4Controls.DpadRight: return 10; - case DS4Controls.PS: return 11; - case DS4Controls.Cross: return 12; - case DS4Controls.Square: return 13; - case DS4Controls.Triangle: return 14; - case DS4Controls.Circle: return 15; - case DS4Controls.LXNeg: return 16; - case DS4Controls.LYNeg: return 17; - case DS4Controls.RXNeg: return 18; - case DS4Controls.RYNeg: return 19; - case DS4Controls.LXPos: return 20; - case DS4Controls.LYPos: return 21; - case DS4Controls.RXPos: return 22; - case DS4Controls.RYPos: return 23; - case DS4Controls.L2: return 24; - case DS4Controls.R2: return 25; - case DS4Controls.TouchMulti: return 26; - case DS4Controls.TouchLeft: return 27; - case DS4Controls.TouchRight: return 28; - case DS4Controls.TouchUpper: return 29; - case DS4Controls.GyroXNeg: return 30; - case DS4Controls.GyroXPos: return 31; - case DS4Controls.GyroZNeg: return 32; - case DS4Controls.GyroZPos: return 33; - } - return 0; + int result = 0; + if (ctrl >= DS4Controls.None && ctrl <= DS4Controls.SwipeDown) + { + result = ds4ControlMapping[(int)ctrl]; + } + + return result; } static double TValue(double value1, double value2, double percent) @@ -365,37 +530,44 @@ static double TValue(double value1, double value2, double percent) percent /= 100f; return value1 * percent + value2 * (1 - percent); } - static double Clamp(double min, double value, double max) + + private static int ClampInt(int min, int value, int max) { - if (value > max) - return max; - else if (value < min) - return min; - else - return value; + return (value < min) ? min : (value > max) ? max : value; } - public static DS4State SetCurveAndDeadzone(int device, DS4State cState) + public static DS4State SetCurveAndDeadzone(int device, DS4State cState, DS4State dState) { - DS4State dState = new DS4State(cState); + double rotation = /*tempDoubleArray[device] =*/ getLSRotation(device); + if (rotation > 0.0 || rotation < 0.0) + cState.rotateLSCoordinates(rotation); + + double rotationRS = /*tempDoubleArray[device] =*/ getRSRotation(device); + if (rotationRS > 0.0 || rotationRS < 0.0) + cState.rotateRSCoordinates(rotationRS); + + cState.CopyTo(dState); + //DS4State dState = new DS4State(cState); int x; int y; int curve; - if (LSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int lsCurve = getLSCurve(device); + if (lsCurve > 0) { x = cState.LX; y = cState.LY; float max = x + y; double curvex; double curvey; - curve = LSCurve[device]; + curve = lsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) { curvex = (x > 127.5f ? Math.Min(x, (x / max) * multimax) : Math.Max(x, (x / max) * multimin)); curvey = (y > 127.5f ? Math.Min(y, (y / max) * multimax) : Math.Max(y, (y / max) * multimin)); - //btnLSTrack.Location = new Point((int)(dpix * curvex / 2.09 + lbLSTrack.Location.X), (int)(dpiy * curvey / 2.09 + lbLSTrack.Location.Y)); } else { @@ -410,17 +582,21 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.LX = (byte)Math.Round(curvex, 0); dState.LY = (byte)Math.Round(curvey, 0); } - if (RSCurve[device] > 0) + + /* TODO: Look into curve options and make sure maximum axes values are being respected */ + int rsCurve = getRSCurve(device); + if (rsCurve > 0) { x = cState.RX; y = cState.RY; float max = x + y; double curvex; double curvey; - curve = RSCurve[device]; + curve = rsCurve; double multimax = TValue(382.5, max, curve); double multimin = TValue(127.5, max, curve); if ((x > 127.5f && y > 127.5f) || (x < 127.5f && y < 127.5f)) @@ -441,1587 +617,3826 @@ public static DS4State SetCurveAndDeadzone(int device, DS4State cState) curvey = Math.Min(y, (y / max) * multimax); } } + dState.RX = (byte)Math.Round(curvex, 0); dState.RY = (byte)Math.Round(curvey, 0); } - double ls = Math.Sqrt(Math.Pow(cState.LX - 127.5f, 2) + Math.Pow(cState.LY - 127.5f, 2)); - //deadzones - if (LSDeadzone[device] > 0 && ls < LSDeadzone[device]) - { - dState.LX = 127; - dState.LY = 127; - } - else if (LSDeadzone[device] < 0 && ls > 127.5f + LSDeadzone[device]) - { - double r = Math.Atan2((dState.LY - 127.5f), (dState.LX - 127.5f)); - dState.LX = (byte)(Math.Cos(r) * (127.5f + LSDeadzone[device]) + 127.5f); - dState.LY = (byte)(Math.Sin(r) * (127.5f + LSDeadzone[device]) + 127.5f); - } - //Console.WriteLine - double rs = Math.Sqrt(Math.Pow(cState.RX - 127.5f, 2) + Math.Pow(cState.RY - 127.5f, 2)); - if (RSDeadzone[device] > 0 && rs < LSDeadzone[device]) - { - dState.RX = 127; - dState.RY = 127; - } - else if (RSDeadzone[device] < 0 && rs > 127.5f + RSDeadzone[device]) - { - double r = Math.Atan2((dState.RY - 127.5f), (dState.RX - 127.5f)); - dState.RX = (byte)(Math.Cos(r) * (127.5f + RSDeadzone[device]) + 127.5f); - dState.RY = (byte)(Math.Sin(r) * (127.5f + RSDeadzone[device]) + 127.5f); - } - if (L2Deadzone[device] > 0 && cState.L2 < L2Deadzone[device]) - dState.L2 = 0; - if (R2Deadzone[device] > 0 && cState.R2 < R2Deadzone[device]) - dState.R2 = 0; - if (LSSens[device] != 1) - { - dState.LX = (byte)Clamp(0, LSSens[device] * (dState.LX - 127) + 127, 255); - dState.LY = (byte)Clamp(0, LSSens[device] * (dState.LY - 127) + 127, 255); - } - if (RSSens[device] != 1) - { - dState.RX = (byte)Clamp(0, RSSens[device] * (dState.RX - 127) + 127, 255); - dState.RY = (byte)Clamp(0, RSSens[device] * (dState.RY - 127) + 127, 255); - } - if (L2Sens[device] != 1) - dState.L2 = (byte)Clamp(0, L2Sens[device] * dState.L2, 255); - if (R2Sens[device] != 1) - dState.R2 = (byte)Clamp(0, R2Sens[device] * dState.R2, 255); - return dState; - } - private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) - { - switch (trigger) - { - case 1: return getBoolMapping(device, DS4Controls.Cross, cState, eState, tp); - case 2: return getBoolMapping(device, DS4Controls.Circle, cState, eState, tp); - case 3: return getBoolMapping(device, DS4Controls.Square, cState, eState, tp); - case 4: return getBoolMapping(device, DS4Controls.Triangle, cState, eState, tp); - case 5: return getBoolMapping(device, DS4Controls.Options, cState, eState, tp); - case 6: return getBoolMapping(device, DS4Controls.Share, cState, eState, tp); - case 7: return getBoolMapping(device, DS4Controls.DpadUp, cState, eState, tp); - case 8: return getBoolMapping(device, DS4Controls.DpadDown, cState, eState, tp); - case 9: return getBoolMapping(device, DS4Controls.DpadLeft, cState, eState, tp); - case 10: return getBoolMapping(device, DS4Controls.DpadRight, cState, eState, tp); - case 11: return getBoolMapping(device, DS4Controls.PS, cState, eState, tp); - case 12: return getBoolMapping(device, DS4Controls.L1, cState, eState, tp); - case 13: return getBoolMapping(device, DS4Controls.R1, cState, eState, tp); - case 14: return getBoolMapping(device, DS4Controls.L2, cState, eState, tp); - case 15: return getBoolMapping(device, DS4Controls.R2, cState, eState, tp); - case 16: return getBoolMapping(device, DS4Controls.L3, cState, eState, tp); - case 17: return getBoolMapping(device, DS4Controls.R3, cState, eState, tp); - case 18: return getBoolMapping(device, DS4Controls.TouchLeft, cState, eState, tp); - case 19: return getBoolMapping(device, DS4Controls.TouchUpper, cState, eState, tp); - case 20: return getBoolMapping(device, DS4Controls.TouchMulti, cState, eState, tp); - case 21: return getBoolMapping(device, DS4Controls.TouchRight, cState, eState, tp); - case 22: return getBoolMapping(device, DS4Controls.GyroZNeg, cState, eState, tp); - case 23: return getBoolMapping(device, DS4Controls.GyroZPos, cState, eState, tp); - case 24: return getBoolMapping(device, DS4Controls.GyroXPos, cState, eState, tp); - case 25: return getBoolMapping(device, DS4Controls.GyroXNeg, cState, eState, tp); - case 26: return cState.Touch1; - default: return false; - } - } - private static X360Controls getX360ControlsByName(string key) - { - X360Controls x3c; - if (Enum.TryParse(key, true, out x3c)) - return x3c; - switch (key) + /*int lsDeadzone = getLSDeadzone(device); + int lsAntiDead = getLSAntiDeadzone(device); + int lsMaxZone = getLSMaxzone(device); + */ + StickDeadZoneInfo lsMod = GetLSDeadInfo(device); + int lsDeadzone = lsMod.deadZone; + int lsAntiDead = lsMod.antiDeadZone; + int lsMaxZone = lsMod.maxZone; + double lsMaxOutput = lsMod.maxOutput; + + if (lsDeadzone > 0 || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) { - case "Back": return X360Controls.Back; - case "Left Stick": return X360Controls.LS; - case "Right Stick": return X360Controls.RS; - case "Start": return X360Controls.Start; - case "Up Button": return X360Controls.DpadUp; - case "Right Button": return X360Controls.DpadRight; - case "Down Button": return X360Controls.DpadDown; - case "Left Button": return X360Controls.DpadLeft; + double lsSquared = Math.Pow(cState.LX - 128f, 2) + Math.Pow(cState.LY - 128f, 2); + double lsDeadzoneSquared = Math.Pow(lsDeadzone, 2); + if (lsDeadzone > 0 && lsSquared <= lsDeadzoneSquared) + { + dState.LX = 128; + dState.LY = 128; + } + else if ((lsDeadzone > 0 && lsSquared > lsDeadzoneSquared) || lsAntiDead > 0 || lsMaxZone != 100 || lsMaxOutput != 100.0) + { + double r = Math.Atan2(-(dState.LY - 128.0), (dState.LX - 128.0)); + double maxXValue = dState.LX >= 128.0 ? 127.0 : -128; + double maxYValue = dState.LY >= 128.0 ? 127.0 : -128; + double ratio = lsMaxZone / 100.0; + double maxOutRatio = lsMaxOutput / 100.0; - case "Left Bumper": return X360Controls.LB; - case "Right Bumper": return X360Controls.RB; - case "Y Button": return X360Controls.Y; - case "B Button": return X360Controls.B; - case "A Button": return X360Controls.A; - case "X Button": return X360Controls.X; + double maxZoneXNegValue = (ratio * -128) + 128; + double maxZoneXPosValue = (ratio * 127) + 128; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.LX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.LY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); - case "Guide": return X360Controls.Guide; - case "Left X-Axis-": return X360Controls.LXNeg; - case "Left Y-Axis-": return X360Controls.LYNeg; - case "Right X-Axis-": return X360Controls.RXNeg; - case "Right Y-Axis-": return X360Controls.RYNeg; + double tempLsXDead = 0.0, tempLsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (lsDeadzone > 0) + { + tempLsXDead = Math.Abs(Math.Cos(r)) * (lsDeadzone / 127.0) * maxXValue; + tempLsYDead = Math.Abs(Math.Sin(r)) * (lsDeadzone / 127.0) * maxYValue; - case "Left X-Axis+": return X360Controls.LXPos; - case "Left Y-Axis+": return X360Controls.LYPos; - case "Right X-Axis+": return X360Controls.RXPos; - case "Right Y-Axis+": return X360Controls.RYPos; - case "Left Trigger": return X360Controls.LT; - case "Right Trigger": return X360Controls.RT; + if (lsSquared > lsDeadzoneSquared) + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = ((currentX - 128.0 - tempLsXDead) / (maxZoneX - tempLsXDead)); + tempOutputY = ((currentY - 128.0 - tempLsYDead) / (maxZoneY - tempLsYDead)); + } + } + else + { + double currentX = Global.Clamp(maxZoneXNegValue, dState.LX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.LY, maxZoneYPosValue); + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; + } - case "Left Mouse Button": return X360Controls.LeftMouse; - case "Right Mouse Button": return X360Controls.RightMouse; - case "Middle Mouse Button": return X360Controls.MiddleMouse; - case "4th Mouse Button": return X360Controls.FourthMouse; - case "5th Mouse Button": return X360Controls.FifthMouse; - case "Mouse Wheel Up": return X360Controls.WUP; - case "Mouse Wheel Down": return X360Controls.WDOWN; - case "Mouse Up": return X360Controls.MouseUp; - case "Mouse Down": return X360Controls.MouseDown; - case "Mouse Left": return X360Controls.MouseLeft; - case "Mouse Right": return X360Controls.MouseRight; - case "Unbound": return X360Controls.Unbound; + if (lsMaxOutput != 100.0) + { + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempLsXAntiDeadPercent = 0.0, tempLsYAntiDeadPercent = 0.0; + if (lsAntiDead > 0) + { + tempLsXAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempLsYAntiDeadPercent = (lsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + if (tempOutputX > 0.0) + { + dState.LX = (byte)((((1.0 - tempLsXAntiDeadPercent) * tempOutputX + tempLsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.LX = 128; + } + + if (tempOutputY > 0.0) + { + dState.LY = (byte)((((1.0 - tempLsYAntiDeadPercent) * tempOutputY + tempLsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.LY = 128; + } + } } - return X360Controls.Unbound; - } - /// - /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. - /// - static bool[] held = new bool[4]; - static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; - public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - - MappedState.LX = 127; - MappedState.LY = 127; - MappedState.RX = 127; - MappedState.RY = 127; - int MouseDeltaX = 0; - int MouseDeltaY = 0; - - SyntheticState deviceState = Mapping.deviceState[device]; - if (GetActions().Count > 0 && (ProfileActions[device].Count > 0 || !string.IsNullOrEmpty(tempprofilename[device]))) - MapCustomAction(device, cState, MappedState, eState, tp, ctrl); - if (ctrl.DS4Controllers[device] == null) return; - - cState.CopyTo(MappedState); - - List Cross = new List(); - List Circle = new List(); - List Square = new List(); - List Triangle = new List(); - List Options = new List(); - List Share = new List(); - List DpadUp = new List(); - List DpadDown = new List(); - List DpadLeft = new List(); - List DpadRight = new List(); - List PS = new List(); - List L1 = new List(); - List R1 = new List(); - List L2 = new List(); - List R2 = new List(); - List L3 = new List(); - List R3 = new List(); - List LXN = new List(); - List LXP = new List(); - List LYN = new List(); - List LYP = new List(); - List RXN = new List(); - List RXP = new List(); - List RYN = new List(); - List RYP = new List(); - DS4Controls usingExtra = DS4Controls.None; - foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + /*int rsDeadzone = getRSDeadzone(device); + int rsAntiDead = getRSAntiDeadzone(device); + int rsMaxZone = getRSMaxzone(device); + */ + StickDeadZoneInfo rsMod = GetRSDeadInfo(device); + int rsDeadzone = rsMod.deadZone; + int rsAntiDead = rsMod.antiDeadZone; + int rsMaxZone = rsMod.maxZone; + double rsMaxOutput = rsMod.maxOutput; + + if (rsDeadzone > 0 || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - object action = null; - DS4ControlSettings.ActionType actionType = 0; - DS4KeyType keyType = DS4KeyType.None; - if (dcs.shiftAction != null && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp)) - { - action = dcs.shiftAction; - actionType = dcs.shiftActionType; - keyType = dcs.shiftKeyType; - } - else if (dcs.action != null) + double rsSquared = Math.Pow(cState.RX - 128.0, 2) + Math.Pow(cState.RY - 128.0, 2); + double rsDeadzoneSquared = Math.Pow(rsDeadzone, 2); + if (rsDeadzone > 0 && rsSquared <= rsDeadzoneSquared) { - action = dcs.action; - actionType = dcs.actionType; - keyType = dcs.keyType; + dState.RX = 128; + dState.RY = 128; } - if (action != null) + else if ((rsDeadzone > 0 && rsSquared > rsDeadzoneSquared) || rsAntiDead > 0 || rsMaxZone != 100 || rsMaxOutput != 100.0) { - if (actionType == DS4ControlSettings.ActionType.Macro) + double r = Math.Atan2(-(dState.RY - 128.0), (dState.RX - 128.0)); + double maxXValue = dState.RX >= 128.0 ? 127 : -128; + double maxYValue = dState.RY >= 128.0 ? 127 : -128; + double ratio = rsMaxZone / 100.0; + double maxOutRatio = rsMaxOutput / 100.0; + + double maxZoneXNegValue = (ratio * -128.0) + 128.0; + double maxZoneXPosValue = (ratio * 127.0) + 128.0; + double maxZoneYNegValue = maxZoneXNegValue; + double maxZoneYPosValue = maxZoneXPosValue; + double maxZoneX = dState.RX >= 128.0 ? (maxZoneXPosValue - 128.0) : (maxZoneXNegValue - 128.0); + double maxZoneY = dState.RY >= 128.0 ? (maxZoneYPosValue - 128.0) : (maxZoneYNegValue - 128.0); + + double tempRsXDead = 0.0, tempRsYDead = 0.0; + double tempOutputX = 0.0, tempOutputY = 0.0; + if (rsDeadzone > 0) { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - PlayMacro(device, macroControl, string.Join("/", (int[])action), dcs.control, keyType); - } - else if (!getBoolMapping(device, dcs.control, cState, eState, tp)) + tempRsXDead = Math.Abs(Math.Cos(r)) * (rsDeadzone / 127.0) * maxXValue; + tempRsYDead = Math.Abs(Math.Sin(r)) * (rsDeadzone / 127.0) * maxYValue; + + if (rsSquared > rsDeadzoneSquared) { - EndMacro(device, macroControl, string.Join("/", (int[])action), dcs.control); + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = ((currentX - 128.0 - tempRsXDead) / (maxZoneX - tempRsXDead)); + tempOutputY = ((currentY - 128.0 - tempRsYDead) / (maxZoneY - tempRsYDead)); } } - else if (actionType == DS4ControlSettings.ActionType.Key) + else { - ushort value = ushort.Parse(action.ToString()); - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - SyntheticState.KeyPresses kp; - if (!deviceState.keyPresses.TryGetValue(value, out kp)) - deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); - if (keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (!pressedonce[value]) - { - kp.current.toggle = !kp.current.toggle; - pressedonce[value] = true; - } - kp.current.toggleCount++; - } - kp.current.repeatCount++; - } - else - pressedonce[value] = false; + double currentX = Global.Clamp(maxZoneXNegValue, dState.RX, maxZoneXPosValue); + double currentY = Global.Clamp(maxZoneYNegValue, dState.RY, maxZoneYPosValue); + + tempOutputX = (currentX - 128.0) / maxZoneX; + tempOutputY = (currentY - 128.0) / maxZoneY; } - else if (actionType == DS4ControlSettings.ActionType.Button) + + if (rsMaxOutput != 100.0) { - int keyvalue = 0; - bool isAnalog = dcs.control.ToString().Contains("LX") || - dcs.control.ToString().Contains("RX") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("LY") || - dcs.control.ToString().Contains("R2") || - dcs.control.ToString().Contains("L2") || - dcs.control.ToString().Contains("Gyro"); - switch (getX360ControlsByName(action.ToString())) - { - case X360Controls.A: Cross.Add(dcs.control); break; - case X360Controls.B: Circle.Add(dcs.control); break; - case X360Controls.X: Square.Add(dcs.control); break; - case X360Controls.Y: Triangle.Add(dcs.control); break; - case X360Controls.LB: L1.Add(dcs.control); break; - case X360Controls.LS: L3.Add(dcs.control); break; - case X360Controls.RB: R1.Add(dcs.control); break; - case X360Controls.RS: R3.Add(dcs.control); break; - case X360Controls.DpadUp: DpadUp.Add(dcs.control); break; - case X360Controls.DpadDown: DpadDown.Add(dcs.control); break; - case X360Controls.DpadLeft: DpadLeft.Add(dcs.control); break; - case X360Controls.DpadRight: DpadRight.Add(dcs.control); break; - case X360Controls.Start: Options.Add(dcs.control); break; - case X360Controls.Guide: PS.Add(dcs.control); break; - case X360Controls.Back: Share.Add(dcs.control); break; - case X360Controls.LXNeg: LXN.Add(dcs.control); break; - case X360Controls.LYNeg: LYN.Add(dcs.control); break; - case X360Controls.RXNeg: RXN.Add(dcs.control); break; - case X360Controls.RYNeg: RYN.Add(dcs.control); break; - case X360Controls.LXPos: LXP.Add(dcs.control); break; - case X360Controls.LYPos: LYP.Add(dcs.control); break; - case X360Controls.RXPos: RXP.Add(dcs.control); break; - case X360Controls.RYPos: RYP.Add(dcs.control); break; - case X360Controls.LT: L2.Add(dcs.control); break; - case X360Controls.RT: R2.Add(dcs.control); break; - case X360Controls.LeftMouse: - keyvalue = 256; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.leftCount++; - break; - case X360Controls.RightMouse: - keyvalue = 257; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.rightCount++; - break; - case X360Controls.MiddleMouse: - keyvalue = 258; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.middleCount++; - break; - case X360Controls.FourthMouse: - keyvalue = 259; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fourthCount++; - break; - case X360Controls.FifthMouse: - keyvalue = 260; - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - deviceState.currentClicks.fifthCount++; - break; - case X360Controls.WUP: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); - else - deviceState.currentClicks.wUpCount++; - break; - case X360Controls.WDOWN: - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - if (isAnalog) - getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); - else - deviceState.currentClicks.wDownCount++; - break; - case X360Controls.MouseUp: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 0); - MouseDeltaY = -Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseDown: - if (MouseDeltaY == 0) - { - MouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, 1); - MouseDeltaY = Math.Abs((MouseDeltaY == -2147483648 ? 0 : MouseDeltaY)); - } - break; - case X360Controls.MouseLeft: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 2); - MouseDeltaX = -Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - case X360Controls.MouseRight: - if (MouseDeltaX == 0) - { - MouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, 3); - MouseDeltaX = Math.Abs((MouseDeltaX == -2147483648 ? 0 : MouseDeltaX)); - } - break; - } - if (keyType.HasFlag(DS4KeyType.Toggle)) - { - if (getBoolMapping(device, dcs.control, cState, eState, tp)) - { - resetToDefaultValue(dcs.control, MappedState); - if (!pressedonce[keyvalue]) - { - deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; - pressedonce[keyvalue] = true; - } - deviceState.currentClicks.toggleCount++; - } - else - { - pressedonce[keyvalue] = false; - } - } - resetToDefaultValue(dcs.control, MappedState); // erase default mappings for things that are remapped + double maxOutXRatio = Math.Abs(Math.Cos(r)) * maxOutRatio; + double maxOutYRatio = Math.Abs(Math.Sin(r)) * maxOutRatio; + tempOutputX = Math.Min(Math.Max(tempOutputX, 0.0), maxOutXRatio); + tempOutputY = Math.Min(Math.Max(tempOutputY, 0.0), maxOutYRatio); + } + + double tempRsXAntiDeadPercent = 0.0, tempRsYAntiDeadPercent = 0.0; + if (rsAntiDead > 0) + { + tempRsXAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Cos(r)); + tempRsYAntiDeadPercent = (rsAntiDead * 0.01) * Math.Abs(Math.Sin(r)); + } + + if (tempOutputX > 0.0) + { + dState.RX = (byte)((((1.0 - tempRsXAntiDeadPercent) * tempOutputX + tempRsXAntiDeadPercent)) * maxXValue + 128.0); + } + else + { + dState.RX = 128; + } + + if (tempOutputY > 0.0) + { + dState.RY = (byte)((((1.0 - tempRsYAntiDeadPercent) * tempOutputY + tempRsYAntiDeadPercent)) * maxYValue + 128.0); + } + else + { + dState.RY = 128; } } + } - if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + /*byte l2Deadzone = getL2Deadzone(device); + int l2AntiDeadzone = getL2AntiDeadzone(device); + int l2Maxzone = getL2Maxzone(device); + */ + + TriggerDeadZoneZInfo l2ModInfo = GetL2ModInfo(device); + byte l2Deadzone = l2ModInfo.deadZone; + int l2AntiDeadzone = l2ModInfo.antiDeadZone; + int l2Maxzone = l2ModInfo.maxZone; + double l2MaxOutput = l2ModInfo.maxOutput; + if (l2Deadzone > 0 || l2AntiDeadzone > 0 || l2Maxzone != 100 || l2MaxOutput != 100.0) + { + double tempL2Output = cState.L2 / 255.0; + double tempL2AntiDead = 0.0; + double ratio = l2Maxzone / 100.0; + double maxValue = 255.0 * ratio; + + if (l2Deadzone > 0) { - bool shiftE = dcs.shiftExtras != "0,0,0,0,0,0,0,0" && dcs.shiftExtras != "" && ShiftTrigger(dcs.shiftTrigger, device, cState, eState, tp); - bool regE = dcs.extras != "0,0,0,0,0,0,0,0" && dcs.extras != ""; - if ((regE || shiftE) && getBoolMapping(device, dcs.control, cState, eState, tp)) + if (cState.L2 > l2Deadzone) { - usingExtra = dcs.control; - string p; - if (shiftE) - p = dcs.shiftExtras; - else - p = dcs.extras; - string[] extraS = p.Split(','); - int[] extras = new int[extraS.Length]; - for (int i = 0; i < extraS.Length; i++) - { - int b; - if (int.TryParse(extraS[i], out b)) - extras[i] = b; - } - held[device] = true; - try - { - if (!(extras[0] == extras[1] && extras[1] == 0)) - ctrl.setRumble((byte)extras[0], (byte)extras[1], device); - if (extras[2] == 1) - { - DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; - DS4LightBar.forcedColor[device] = color; - DS4LightBar.forcedFlash[device] = (byte)extras[6]; - DS4LightBar.forcelight[device] = true; - } - if (extras[7] == 1) - { - if (oldmouse[device] == -1) - oldmouse[device] = ButtonMouseSensitivity[device]; - ButtonMouseSensitivity[device] = extras[8]; - } - } - catch { } + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = (current - l2Deadzone) / (maxValue - l2Deadzone); } - else if ((regE || shiftE) && held[device]) + else { - DS4LightBar.forcelight[device] = false; - DS4LightBar.forcedFlash[device] = 0; - ButtonMouseSensitivity[device] = oldmouse[device]; - oldmouse[device] = -1; - ctrl.setRumble(0, 0, device); - held[device] = false; - usingExtra = DS4Controls.None; + tempL2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.L2, maxValue); + tempL2Output = current / maxValue; + } + + if (l2MaxOutput != 100.0) + { + double maxOutRatio = l2MaxOutput / 100.0; + tempL2Output = Math.Min(Math.Max(tempL2Output, 0.0), maxOutRatio); + } + + if (l2AntiDeadzone > 0) + { + tempL2AntiDead = l2AntiDeadzone * 0.01; + } + + if (tempL2Output > 0.0) + { + dState.L2 = (byte)(((1.0 - tempL2AntiDead) * tempL2Output + tempL2AntiDead) * 255.0); + } + else + { + dState.L2 = 0; + } + } + + /*byte r2Deadzone = getR2Deadzone(device); + int r2AntiDeadzone = getR2AntiDeadzone(device); + int r2Maxzone = getR2Maxzone(device); + */ + TriggerDeadZoneZInfo r2ModInfo = GetR2ModInfo(device); + byte r2Deadzone = r2ModInfo.deadZone; + int r2AntiDeadzone = r2ModInfo.antiDeadZone; + int r2Maxzone = r2ModInfo.maxZone; + double r2MaxOutput = r2ModInfo.maxOutput; + if (r2Deadzone > 0 || r2AntiDeadzone > 0 || r2Maxzone != 100 || r2MaxOutput != 100.0) + { + double tempR2Output = cState.R2 / 255.0; + double tempR2AntiDead = 0.0; + double ratio = r2Maxzone / 100.0; + double maxValue = 255 * ratio; + + if (r2Deadzone > 0) + { + if (cState.R2 > r2Deadzone) + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = (current - r2Deadzone) / (maxValue - r2Deadzone); + } + else + { + tempR2Output = 0.0; + } + } + else + { + double current = Global.Clamp(0, dState.R2, maxValue); + tempR2Output = current / maxValue; + } + + if (r2MaxOutput != 100.0) + { + double maxOutRatio = r2MaxOutput / 100.0; + tempR2Output = Math.Min(Math.Max(tempR2Output, 0.0), maxOutRatio); + } + + if (r2AntiDeadzone > 0) + { + tempR2AntiDead = r2AntiDeadzone * 0.01; + } + + if (tempR2Output > 0.0) + { + dState.R2 = (byte)(((1.0 - tempR2AntiDead) * tempR2Output + tempR2AntiDead) * 255.0); + } + else + { + dState.R2 = 0; + } + } + + double lsSens = getLSSens(device); + if (lsSens != 1.0) + { + dState.LX = (byte)Global.Clamp(0, lsSens * (dState.LX - 128.0) + 128.0, 255); + dState.LY = (byte)Global.Clamp(0, lsSens * (dState.LY - 128.0) + 128.0, 255); + } + + double rsSens = getRSSens(device); + if (rsSens != 1.0) + { + dState.RX = (byte)Global.Clamp(0, rsSens * (dState.RX - 128.0) + 128.0, 255); + dState.RY = (byte)Global.Clamp(0, rsSens * (dState.RY - 128.0) + 128.0, 255); + } + + double l2Sens = getL2Sens(device); + if (l2Sens != 1.0) + dState.L2 = (byte)Global.Clamp(0, l2Sens * dState.L2, 255); + + double r2Sens = getR2Sens(device); + if (r2Sens != 1.0) + dState.R2 = (byte)Global.Clamp(0, r2Sens * dState.R2, 255); + + SquareStickInfo squStk = GetSquareStickInfo(device); + if (squStk.lsMode && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.lsRoundness); + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + dState.LX = (byte)(tempX * capX + 128.0); + dState.LY = (byte)(tempY * capY + 128.0); + } + + int lsOutCurveMode = getLsOutCurveMode(device); + if (lsOutCurveMode > 0 && (dState.LX != 128 || dState.LY != 128)) + { + double capX = dState.LX >= 128 ? 127.0 : 128.0; + double capY = dState.LY >= 128 ? 127.0 : 128.0; + double tempX = (dState.LX - 128.0) / capX; + double tempY = (dState.LY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (lsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; + } + + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.LX = (byte)(outputX * signX * capX + 128.0); + dState.LY = (byte)(outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.LX = (byte)(outputX * capX + 128.0); + dState.LY = (byte)(outputY * capY + 128.0); + } + else if (lsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.LX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.LX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.LY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (lsOutCurveMode == 6) + { + dState.LX = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LX]; + dState.LY = lsOutBezierCurveObj[device].arrayBezierLUT[dState.LY]; + } + } + + if (squStk.rsMode && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + DS4SquareStick sqstick = outSqrStk[device]; + sqstick.current.x = tempX; sqstick.current.y = tempY; + sqstick.CircleToSquare(squStk.rsRoundness); + tempX = sqstick.current.x < -1.0 ? -1.0 : sqstick.current.x > 1.0 + ? 1.0 : sqstick.current.x; + tempY = sqstick.current.y < -1.0 ? -1.0 : sqstick.current.y > 1.0 + ? 1.0 : sqstick.current.y; + //Console.WriteLine("Input ({0}) | Output ({1})", tempY, sqstick.current.y); + dState.RX = (byte)(tempX * capX + 128.0); + dState.RY = (byte)(tempY * capY + 128.0); + } + + int rsOutCurveMode = getRsOutCurveMode(device); + if (rsOutCurveMode > 0 && (dState.RX != 128 || dState.RY != 128)) + { + double capX = dState.RX >= 128 ? 127.0 : 128.0; + double capY = dState.RY >= 128 ? 127.0 : 128.0; + double tempX = (dState.RX - 128.0) / capX; + double tempY = (dState.RY - 128.0) / capY; + double signX = tempX >= 0.0 ? 1.0 : -1.0; + double signY = tempY >= 0.0 ? 1.0 : -1.0; + + if (rsOutCurveMode == 1) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = 0.0; + double outputY = 0.0; + + if (absX <= 0.4) + { + outputX = 0.8 * absX; + } + else if (absX <= 0.75) + { + outputX = absX - 0.08; + } + else if (absX > 0.75) + { + outputX = (absX * 1.32) - 0.32; + } + + if (absY <= 0.4) + { + outputY = 0.8 * absY; + } + else if (absY <= 0.75) + { + outputY = absY - 0.08; + } + else if (absY > 0.75) + { + outputY = (absY * 1.32) - 0.32; } + + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 2) + { + double outputX = tempX * tempX; + double outputY = tempY * tempY; + dState.RX = (byte)(outputX * signX * capX + 128.0); + dState.RY = (byte)(outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 3) + { + double outputX = tempX * tempX * tempX; + double outputY = tempY * tempY * tempY; + dState.RX = (byte)(outputX * capX + 128.0); + dState.RY = (byte)(outputY * capY + 128.0); + } + else if (rsOutCurveMode == 4) + { + double absX = Math.Abs(tempX); + double absY = Math.Abs(tempY); + double outputX = absX * (absX - 2.0); + double outputY = absY * (absY - 2.0); + dState.RX = (byte)(-1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(-1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 5) + { + double innerX = Math.Abs(tempX) - 1.0; + double innerY = Math.Abs(tempY) - 1.0; + double outputX = innerX * innerX * innerX + 1.0; + double outputY = innerY * innerY * innerY + 1.0; + dState.RX = (byte)(1.0 * outputX * signX * capX + 128.0); + dState.RY = (byte)(1.0 * outputY * signY * capY + 128.0); + } + else if (rsOutCurveMode == 6) + { + dState.RX = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RX]; + dState.RY = rsOutBezierCurveObj[device].arrayBezierLUT[dState.RY]; + } + } + + int l2OutCurveMode = getL2OutCurveMode(device); + if (l2OutCurveMode > 0 && dState.L2 != 0) + { + double temp = dState.L2 / 255.0; + if (l2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 2) + { + double output = temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.L2 = (byte)(output * 255.0); + } + else if (l2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.L2 = (byte)(-1.0 * output * 255.0); + } + else if (l2OutCurveMode == 6) + { + dState.L2 = l2OutBezierCurveObj[device].arrayBezierLUT[dState.L2]; + } + } + + int r2OutCurveMode = getR2OutCurveMode(device); + if (r2OutCurveMode > 0 && dState.R2 != 0) + { + double temp = dState.R2 / 255.0; + if (r2OutCurveMode == 1) + { + double output; + + if (temp <= 0.4) + output = 0.55 * temp; + else if (temp <= 0.75) + output = temp - 0.18; + else // if (temp > 0.75) + output = (temp * 1.72) - 0.72; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 2) + { + double output = temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 3) + { + double output = temp * temp * temp; + dState.R2 = (byte)(output * 255.0); + } + else if (r2OutCurveMode == 4) + { + double output = temp * (temp - 2.0); + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.R2 = (byte)(-1.0 * output * 255.0); + } + else if (r2OutCurveMode == 6) + { + dState.R2 = r2OutBezierCurveObj[device].arrayBezierLUT[dState.R2]; + } + } + + + bool sOff = /*tempBool =*/ isUsingSAforMouse(device); + if (sOff == false) + { + int SXD = (int)(128d * getSXDeadzone(device)); + int SZD = (int)(128d * getSZDeadzone(device)); + double SXMax = getSXMaxzone(device); + double SZMax = getSZMaxzone(device); + double sxAntiDead = getSXAntiDeadzone(device); + double szAntiDead = getSZAntiDeadzone(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + int result = 0; + + int gyroX = cState.Motion.accelX, gyroZ = cState.Motion.accelZ; + int absx = Math.Abs(gyroX), absz = Math.Abs(gyroZ); + + if (SXD > 0 || SXMax < 1.0 || sxAntiDead > 0) + { + int maxValue = (int)(SXMax * 128d); + if (absx > SXD) + { + double ratioX = absx < maxValue ? (absx - SXD) / (double)(maxValue - SXD) : 1.0; + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * ((1.0 - sxAntiDead) * ratioX + sxAntiDead)); + } + else + { + dState.Motion.outputAccelX = 0; + } + } + else + { + dState.Motion.outputAccelX = Math.Sign(gyroX) * + (int)Math.Min(128d, sxsens * 128d * (absx / 128d)); + } + + if (SZD > 0 || SZMax < 1.0 || szAntiDead > 0) + { + int maxValue = (int)(SZMax * 128d); + if (absz > SZD) + { + double ratioZ = absz < maxValue ? (absz - SZD) / (double)(maxValue - SZD) : 1.0; + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * ((1.0 - szAntiDead) * ratioZ + szAntiDead)); + } + else + { + dState.Motion.outputAccelZ = 0; + } + } + else + { + dState.Motion.outputAccelZ = Math.Sign(gyroZ) * + (int)Math.Min(128d, szsens * 128d * (absz / 128d)); + } + + int sxOutCurveMode = getSXOutCurveMode(device); + if (sxOutCurveMode > 0) + { + double temp = dState.Motion.outputAccelX / 128.0; + double sign = Math.Sign(temp); + if (sxOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelX = (int)(output * sign * 128.0); + } + else if (sxOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelX = result; + } + else if (sxOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelX = (int)(-1.0 * output * + sign * 128.0); + } + else if (sxOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelX = (int)(output * + sign * 128.0); + } + else if (sxOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelX); + dState.Motion.outputAccelX = sxOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelX), 128)] * signSA; + } + } + + int szOutCurveMode = getSZOutCurveMode(device); + if (szOutCurveMode > 0 && dState.Motion.outputAccelZ != 0) + { + double temp = dState.Motion.outputAccelZ / 128.0; + double sign = Math.Sign(temp); + if (szOutCurveMode == 1) + { + double output; + double abs = Math.Abs(temp); + + if (abs <= 0.4) + output = 0.55 * abs; + else if (abs <= 0.75) + output = abs - 0.18; + else // if (abs > 0.75) + output = (abs * 1.72) - 0.72; + dState.Motion.outputAccelZ = (int)(output * sign * 128.0); + } + else if (szOutCurveMode == 2) + { + double output = temp * temp; + result = (int)(output * sign * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 3) + { + double output = temp * temp * temp; + result = (int)(output * 128.0); + dState.Motion.outputAccelZ = result; + } + else if (szOutCurveMode == 4) + { + double abs = Math.Abs(temp); + double output = abs * (abs - 2.0); + dState.Motion.outputAccelZ = (int)(-1.0 * output * + sign * 128.0); + } + else if (szOutCurveMode == 5) + { + double inner = Math.Abs(temp) - 1.0; + double output = inner * inner * inner + 1.0; + dState.Motion.outputAccelZ = (int)(output * + sign * 128.0); + } + else if (szOutCurveMode == 6) + { + int signSA = Math.Sign(dState.Motion.outputAccelZ); + dState.Motion.outputAccelZ = szOutBezierCurveObj[device].arrayBezierLUT[Math.Min(Math.Abs(dState.Motion.outputAccelZ), 128)] * signSA; + } + } + } + + return dState; + } + + /* TODO: Possibly remove usage of this version of the method */ + private static bool ShiftTrigger(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping(device, ds, cState, eState, tp); + } + + return result; + } + + private static bool ShiftTrigger2(int trigger, int device, DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMapping) + { + bool result = false; + if (trigger == 0) + { + result = false; + } + else if (trigger < 26) + { + DS4Controls ds = shiftTriggerMapping[trigger]; + result = getBoolMapping2(device, ds, cState, eState, tp, fieldMapping); + } + else if (trigger == 26) + { + result = cState.Touch1Finger; + } + + return result; + } + + private static X360Controls getX360ControlsByName(string key) + { + X360Controls x3c; + if (Enum.TryParse(key, true, out x3c)) + return x3c; + + switch (key) + { + case "Back": return X360Controls.Back; + case "Left Stick": return X360Controls.LS; + case "Right Stick": return X360Controls.RS; + case "Start": return X360Controls.Start; + case "Up Button": return X360Controls.DpadUp; + case "Right Button": return X360Controls.DpadRight; + case "Down Button": return X360Controls.DpadDown; + case "Left Button": return X360Controls.DpadLeft; + + case "Left Bumper": return X360Controls.LB; + case "Right Bumper": return X360Controls.RB; + case "Y Button": return X360Controls.Y; + case "B Button": return X360Controls.B; + case "A Button": return X360Controls.A; + case "X Button": return X360Controls.X; + + case "Guide": return X360Controls.Guide; + case "Left X-Axis-": return X360Controls.LXNeg; + case "Left Y-Axis-": return X360Controls.LYNeg; + case "Right X-Axis-": return X360Controls.RXNeg; + case "Right Y-Axis-": return X360Controls.RYNeg; + + case "Left X-Axis+": return X360Controls.LXPos; + case "Left Y-Axis+": return X360Controls.LYPos; + case "Right X-Axis+": return X360Controls.RXPos; + case "Right Y-Axis+": return X360Controls.RYPos; + case "Left Trigger": return X360Controls.LT; + case "Right Trigger": return X360Controls.RT; + + case "Left Mouse Button": return X360Controls.LeftMouse; + case "Right Mouse Button": return X360Controls.RightMouse; + case "Middle Mouse Button": return X360Controls.MiddleMouse; + case "4th Mouse Button": return X360Controls.FourthMouse; + case "5th Mouse Button": return X360Controls.FifthMouse; + case "Mouse Wheel Up": return X360Controls.WUP; + case "Mouse Wheel Down": return X360Controls.WDOWN; + case "Mouse Up": return X360Controls.MouseUp; + case "Mouse Down": return X360Controls.MouseDown; + case "Mouse Left": return X360Controls.MouseLeft; + case "Mouse Right": return X360Controls.MouseRight; + case "Unbound": return X360Controls.Unbound; + default: break; + } + + return X360Controls.Unbound; + } + + /// + /// Map DS4 Buttons/Axes to other DS4 Buttons/Axes (largely the same as Xinput ones) and to keyboard and mouse buttons. + /// + static bool[] held = new bool[4]; + static int[] oldmouse = new int[4] { -1, -1, -1, -1 }; + public static void MapCustom(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, + Mouse tp, ControlService ctrl) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + double tempMouseDeltaX = 0.0; + double tempMouseDeltaY = 0.0; + int mouseDeltaX = 0; + int mouseDeltaY = 0; + + cState.calculateStickAngles(); + DS4StateFieldMapping fieldMapping = fieldMappings[device]; + fieldMapping.populateFieldMapping(cState, eState, tp); + DS4StateFieldMapping outputfieldMapping = outputFieldMappings[device]; + outputfieldMapping.populateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping fieldMapping = new DS4StateFieldMapping(cState, eState, tp); + //DS4StateFieldMapping outputfieldMapping = new DS4StateFieldMapping(cState, eState, tp); + + SyntheticState deviceState = Mapping.deviceState[device]; + if (getProfileActionCount(device) > 0 || useTempProfile[device]) + MapCustomAction(device, cState, MappedState, eState, tp, ctrl, fieldMapping, outputfieldMapping); + //if (ctrl.DS4Controllers[device] == null) return; + + //cState.CopyTo(MappedState); + + //Dictionary tempControlDict = new Dictionary(); + //MultiValueDict tempControlDict = new MultiValueDict(); + DS4Controls usingExtra = DS4Controls.None; + List tempSettingsList = getDS4CSettings(device); + //foreach (DS4ControlSettings dcs in getDS4CSettings(device)) + //for (int settingIndex = 0, arlen = tempSettingsList.Count; settingIndex < arlen; settingIndex++) + for (var settingEnum = tempSettingsList.GetEnumerator(); settingEnum.MoveNext();) + { + //DS4ControlSettings dcs = tempSettingsList[settingIndex]; + DS4ControlSettings dcs = settingEnum.Current; + object action = null; + DS4ControlSettings.ActionType actionType = 0; + DS4KeyType keyType = DS4KeyType.None; + if (dcs.shiftAction != null && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping)) + { + action = dcs.shiftAction; + actionType = dcs.shiftActionType; + keyType = dcs.shiftKeyType; + } + else if (dcs.action != null) + { + action = dcs.action; + actionType = dcs.actionType; + keyType = dcs.keyType; + } + + if (usingExtra == DS4Controls.None || usingExtra == dcs.control) + { + bool shiftE = !string.IsNullOrEmpty(dcs.shiftExtras) && ShiftTrigger2(dcs.shiftTrigger, device, cState, eState, tp, fieldMapping); + bool regE = !string.IsNullOrEmpty(dcs.extras); + if ((regE || shiftE) && getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + usingExtra = dcs.control; + string p; + if (shiftE) + p = dcs.shiftExtras; + else + p = dcs.extras; + + string[] extraS = p.Split(','); + int extrasSLen = extraS.Length; + int[] extras = new int[extrasSLen]; + for (int i = 0; i < extrasSLen; i++) + { + int b; + if (int.TryParse(extraS[i], out b)) + extras[i] = b; + } + + held[device] = true; + try + { + if (!(extras[0] == extras[1] && extras[1] == 0)) + ctrl.setRumble((byte)extras[0], (byte)extras[1], device); + + if (extras[2] == 1) + { + DS4Color color = new DS4Color { red = (byte)extras[3], green = (byte)extras[4], blue = (byte)extras[5] }; + DS4LightBar.forcedColor[device] = color; + DS4LightBar.forcedFlash[device] = (byte)extras[6]; + DS4LightBar.forcelight[device] = true; + } + + if (extras[7] == 1) + { + if (oldmouse[device] == -1) + oldmouse[device] = ButtonMouseSensitivity[device]; + ButtonMouseSensitivity[device] = extras[8]; + } + } + catch { } + } + else if ((regE || shiftE) && held[device]) + { + DS4LightBar.forcelight[device] = false; + DS4LightBar.forcedFlash[device] = 0; + if (oldmouse[device] != -1) + { + ButtonMouseSensitivity[device] = oldmouse[device]; + oldmouse[device] = -1; + } + + ctrl.setRumble(0, 0, device); + held[device] = false; + usingExtra = DS4Controls.None; + } + } + + if (action != null) + { + if (actionType == DS4ControlSettings.ActionType.Macro) + { + bool active = getBoolMapping2(device, dcs.control, cState, eState, tp, fieldMapping); + if (active) + { + PlayMacro(device, macroControl, String.Empty, null, (int[])action, dcs.control, keyType); + } + else + { + EndMacro(device, macroControl, (int[])action, dcs.control); + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Key) + { + ushort value = Convert.ToUInt16(action); + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + SyntheticState.KeyPresses kp; + if (!deviceState.keyPresses.TryGetValue(value, out kp)) + deviceState.keyPresses[value] = kp = new SyntheticState.KeyPresses(); + + if (keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (!pressedonce[value]) + { + kp.current.toggle = !kp.current.toggle; + pressedonce[value] = true; + } + kp.current.toggleCount++; + } + kp.current.repeatCount++; + } + else + pressedonce[value] = false; + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + else if (actionType == DS4ControlSettings.ActionType.Button) + { + int keyvalue = 0; + bool isAnalog = false; + + if (dcs.control >= DS4Controls.LXNeg && dcs.control <= DS4Controls.RYPos) + { + isAnalog = true; + } + else if (dcs.control == DS4Controls.L2 || dcs.control == DS4Controls.R2) + { + isAnalog = true; + } + else if (dcs.control >= DS4Controls.GyroXPos && dcs.control <= DS4Controls.GyroZNeg) + { + isAnalog = true; + } + + X360Controls xboxControl = X360Controls.None; + if (action is X360Controls) + { + xboxControl = (X360Controls)action; + } + else if (action is string) + { + xboxControl = getX360ControlsByName(action.ToString()); + } + + if (xboxControl >= X360Controls.LXNeg && xboxControl <= X360Controls.Start) + { + DS4Controls tempDS4Control = reverseX360ButtonMapping[(int)xboxControl]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, tempDS4Control)); + //tempControlDict.Add(dcs.control, tempDS4Control); + } + else if (xboxControl >= X360Controls.LeftMouse && xboxControl <= X360Controls.WDOWN) + { + switch (xboxControl) + { + case X360Controls.LeftMouse: + { + keyvalue = 256; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.leftCount++; + + break; + } + case X360Controls.RightMouse: + { + keyvalue = 257; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.rightCount++; + + break; + } + case X360Controls.MiddleMouse: + { + keyvalue = 258; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.middleCount++; + + break; + } + case X360Controls.FourthMouse: + { + keyvalue = 259; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fourthCount++; + + break; + } + case X360Controls.FifthMouse: + { + keyvalue = 260; + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + deviceState.currentClicks.fifthCount++; + + break; + } + case X360Controls.WUP: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, false); + else + deviceState.currentClicks.wUpCount++; + } + + break; + } + case X360Controls.WDOWN: + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (isAnalog) + getMouseWheelMapping(device, dcs.control, cState, eState, tp, true); + else + deviceState.currentClicks.wDownCount++; + } + + break; + } + + default: break; + } + } + else if (xboxControl >= X360Controls.MouseUp && xboxControl <= X360Controls.MouseRight) + { + switch (xboxControl) + { + case X360Controls.MouseUp: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 0, ctrl); + tempMouseDeltaY = -Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseDown: + { + if (tempMouseDeltaY == 0) + { + tempMouseDeltaY = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 1, ctrl); + tempMouseDeltaY = Math.Abs((tempMouseDeltaY == -2147483648 ? 0 : tempMouseDeltaY)); + } + + break; + } + case X360Controls.MouseLeft: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 2, ctrl); + tempMouseDeltaX = -Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + case X360Controls.MouseRight: + { + if (tempMouseDeltaX == 0) + { + tempMouseDeltaX = getMouseMapping(device, dcs.control, cState, eState, fieldMapping, 3, ctrl); + tempMouseDeltaX = Math.Abs((tempMouseDeltaX == -2147483648 ? 0 : tempMouseDeltaX)); + } + + break; + } + + default: break; + } + } + + if (keyType.HasFlag(DS4KeyType.Toggle)) + { + if (getBoolActionMapping2(device, dcs.control, cState, eState, tp, fieldMapping)) + { + if (!pressedonce[keyvalue]) + { + deviceState.currentClicks.toggle = !deviceState.currentClicks.toggle; + pressedonce[keyvalue] = true; + } + deviceState.currentClicks.toggleCount++; + } + else + { + pressedonce[keyvalue] = false; + } + } + + // erase default mappings for things that are remapped + resetToDefaultValue2(dcs.control, MappedState, outputfieldMapping); + } + } + else + { + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[(int)dcs.control]; + if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + //if (dcs.control > DS4Controls.None && dcs.control < DS4Controls.L1) + { + //int current = (int)dcs.control; + //outputfieldMapping.axisdirs[current] = fieldMapping.axisdirs[current]; + customMapQueue[device].Enqueue(new ControlToXInput(dcs.control, dcs.control)); + } + } + } + + Queue tempControl = customMapQueue[device]; + unchecked + { + for (int i = 0, len = tempControl.Count; i < len; i++) + //while(tempControl.Any()) + { + ControlToXInput tempMap = tempControl.Dequeue(); + int controlNum = (int)tempMap.ds4input; + int tempOutControl = (int)tempMap.xoutput; + if (tempMap.xoutput >= DS4Controls.LXNeg && tempMap.xoutput <= DS4Controls.RYPos) + { + const byte axisDead = 128; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[tempOutControl]; + bool alt = controlType == DS4StateFieldMapping.ControlType.AxisDir && tempOutControl % 2 == 0 ? true : false; + byte axisMapping = getXYAxisMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping, alt); + if (axisMapping != axisDead) + { + int controlRelation = tempOutControl % 2 == 0 ? tempOutControl - 1 : tempOutControl + 1; + outputfieldMapping.axisdirs[tempOutControl] = axisMapping; + outputfieldMapping.axisdirs[controlRelation] = axisMapping; + } + } + else + { + if (tempMap.xoutput == DS4Controls.L2 || tempMap.xoutput == DS4Controls.R2) + { + const byte axisZero = 0; + byte axisMapping = getByteMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (axisMapping != axisZero) + outputfieldMapping.triggers[tempOutControl] = axisMapping; + } + else + { + bool value = getBoolMapping2(device, tempMap.ds4input, cState, eState, tp, fieldMapping); + if (value) + outputfieldMapping.buttons[tempOutControl] = value; + } + } + } + } + + outputfieldMapping.populateState(MappedState); + + if (macroCount > 0) + { + if (macroControl[00]) MappedState.Cross = true; + if (macroControl[01]) MappedState.Circle = true; + if (macroControl[02]) MappedState.Square = true; + if (macroControl[03]) MappedState.Triangle = true; + if (macroControl[04]) MappedState.Options = true; + if (macroControl[05]) MappedState.Share = true; + if (macroControl[06]) MappedState.DpadUp = true; + if (macroControl[07]) MappedState.DpadDown = true; + if (macroControl[08]) MappedState.DpadLeft = true; + if (macroControl[09]) MappedState.DpadRight = true; + if (macroControl[10]) MappedState.PS = true; + if (macroControl[11]) MappedState.L1 = true; + if (macroControl[12]) MappedState.R1 = true; + if (macroControl[13]) MappedState.L2 = 255; + if (macroControl[14]) MappedState.R2 = 255; + if (macroControl[15]) MappedState.L3 = true; + if (macroControl[16]) MappedState.R3 = true; + if (macroControl[17]) MappedState.LX = 255; + if (macroControl[18]) MappedState.LX = 0; + if (macroControl[19]) MappedState.LY = 255; + if (macroControl[20]) MappedState.LY = 0; + if (macroControl[21]) MappedState.RX = 255; + if (macroControl[22]) MappedState.RX = 0; + if (macroControl[23]) MappedState.RY = 255; + if (macroControl[24]) MappedState.RY = 0; + } + + if (GetSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None) + { + MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl); + } + + ref byte gyroTempX = ref gyroStickX[device]; + if (gyroTempX != 128) + { + if (MappedState.RX != 128) + MappedState.RX = Math.Abs(gyroTempX - 128) > Math.Abs(MappedState.RX - 128) ? + gyroTempX : MappedState.RX; + else + MappedState.RX = gyroTempX; + } + + ref byte gyroTempY = ref gyroStickY[device]; + if (gyroTempY != 128) + { + if (MappedState.RY != 128) + MappedState.RY = Math.Abs(gyroTempY - 128) > Math.Abs(MappedState.RY - 128) ? + gyroTempY : MappedState.RY; + else + MappedState.RY = gyroTempY; + } + + gyroTempX = gyroTempY = 128; + + calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY, + out mouseDeltaX, out mouseDeltaY); + if (mouseDeltaX != 0 || mouseDeltaY != 0) + { + InputMethods.MoveCursorBy(mouseDeltaX, mouseDeltaY); + } + } + + private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) + { + return shift ? false : GetDS4Action(device, dc, false) == null; + } + + private static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, + DS4StateExposed eState, Mouse tp, ControlService ctrl, DS4StateFieldMapping fieldMapping, DS4StateFieldMapping outputfieldMapping) + { + /* TODO: This method is slow sauce. Find ways to speed up action execution */ + try + { + int actionDoneCount = actionDone.Count; + int totalActionCount = GetActions().Count; + DS4StateFieldMapping previousFieldMapping = null; + List profileActions = getProfileActions(device); + //foreach (string actionname in profileActions) + for (int actionIndex = 0, profileListLen = profileActions.Count; + actionIndex < profileListLen; actionIndex++) + { + //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); + //SpecialAction action = GetAction(actionname); + //int index = GetActionIndexOf(actionname); + string actionname = profileActions[actionIndex]; + SpecialAction action = GetProfileAction(device, actionname); + int index = GetProfileActionIndexOf(device, actionname); + + if (actionDoneCount < index + 1) + { + actionDone.Add(new ActionState()); + actionDoneCount++; + } + else if (actionDoneCount > totalActionCount) + { + actionDone.RemoveAt(actionDoneCount - 1); + actionDoneCount--; + } + + if (action == null) + { + continue; + } + + double time = 0.0; + //If a key or button is assigned to the trigger, a key special action is used like + //a quick tap to use and hold to use the regular custom button/key + bool triggerToBeTapped = action.typeID == SpecialAction.ActionTypeId.None && action.trigger.Count == 1 && + GetDS4Action(device, action.trigger[0], false) == null; + if (!(action.typeID == SpecialAction.ActionTypeId.None || index < 0)) + { + bool triggeractivated = true; + if (action.delayTime > 0.0) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + time = action.delayTime; + nowAction[device] = DateTime.UtcNow; + if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) + triggeractivated = true; + } + else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) + oldnowAction[device] = DateTime.UtcNow; + } + else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + if (subtriggeractivated) + { + oldnowKeyAct[device] = DateTime.UtcNow; + } + } + else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) + { + triggeractivated = false; + bool subtriggeractivated = true; + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + subtriggeractivated = false; + break; + } + } + DateTime now = DateTime.UtcNow; + if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) + { + await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up + triggeractivated = true; + oldnowKeyAct[device] = DateTime.MinValue; + } + else if (!subtriggeractivated) + oldnowKeyAct[device] = DateTime.MinValue; + } + else + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + triggeractivated = false; + break; + } + } + + // If special action macro is set to run on key release then activate the trigger status only when the trigger key is released + if (action.typeID == SpecialAction.ActionTypeId.Macro && action.pressRelease && action.firstTouch) + triggeractivated = !triggeractivated; + } + + bool utriggeractivated = true; + int uTriggerCount = action.uTrigger.Count; + if (action.typeID == SpecialAction.ActionTypeId.Key && uTriggerCount > 0) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + if (action.pressRelease) utriggeractivated = !utriggeractivated; + } + + bool actionFound = false; + if (triggeractivated) + { + if (action.typeID == SpecialAction.ActionTypeId.Program) + { + actionFound = true; + + if (!actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + if (!string.IsNullOrEmpty(action.extra)) + Process.Start(action.details, action.extra); + else + Process.Start(action.details); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Profile) + { + actionFound = true; + + if (!actionDone[index].dev[device] && (!useTempProfile[device] || untriggeraction[device] == null || untriggeraction[device].typeID != SpecialAction.ActionTypeId.Profile) ) + { + actionDone[index].dev[device] = true; + // If Loadprofile special action doesn't have untrigger keys or automatic untrigger option is not set then don't set untrigger status. This way the new loaded profile allows yet another loadProfile action key event. + if (action.uTrigger.Count > 0 || action.automaticUntrigger) + { + untriggeraction[device] = action; + untriggerindex[device] = index; + + // If the existing profile is a temp profile then store its name, because automaticUntrigger needs to know where to go back (empty name goes back to default regular profile) + untriggeraction[device].prevProfileName = (useTempProfile[device] ? tempprofilename[device] : string.Empty); + } + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease(ushort.Parse(dcs.action.ToString())); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", action.details); + AppLogger.LogToGui(prolog, false); + LoadTempProfile(device, action.details, true, ctrl); + + if (action.uTrigger.Count == 0 && !action.automaticUntrigger) + { + // If the new profile has any actions with the same action key (controls) than this action (which doesn't have untrigger keys) then set status of those actions to wait for the release of the existing action key. + List profileActionsNext = getProfileActions(device); + for (int actionIndexNext = 0, profileListLenNext = profileActionsNext.Count; actionIndexNext < profileListLenNext; actionIndexNext++) + { + string actionnameNext = profileActionsNext[actionIndexNext]; + SpecialAction actionNext = GetProfileAction(device, actionnameNext); + int indexNext = GetProfileActionIndexOf(device, actionnameNext); + + if (actionNext.controls == action.controls) + actionDone[indexNext].dev[device] = true; + } + } + + return; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Macro) + { + actionFound = true; + if (!action.pressRelease) + { + // Macro run when trigger keys are pressed down (the default behaviour) + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, actionDone[index]); + } + else + { + if (!action.keyType.HasFlag(DS4KeyType.RepeatMacro)) + EndMacro(device, macroControl, action.macro, DS4Controls.None); + } + } + else + { + // Macro is run when trigger keys are released (optional behaviour of macro special action)) + if (action.firstTouch) + { + action.firstTouch = false; + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, null); + } + } + else + action.firstTouch = true; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (uTriggerCount == 0 || (uTriggerCount > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = index; + ushort key; + ushort.TryParse(action.details, out key); + if (uTriggerCount == 0) + { + SyntheticState.KeyPresses kp; + if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) + deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + kp.current.scanCodeCount++; + else + kp.current.vkCount++; + kp.current.repeatCount++; + } + else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyPress(key); + else + InputMethods.performKeyPress(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + bool synced = /*tempBool =*/ d.isSynced(); + if (synced && !d.isCharging()) + { + ConnectionType deviceConn = d.getConnectionType(); + //bool exclusive = /*tempBool =*/ d.isExclusive(); + if (deviceConn == ConnectionType.BT) + { + d.DisconnectBT(); + ReleaseActionKeys(action, device); + return; + } + else if (deviceConn == ConnectionType.SONYWA) + { + action.pressRelease = true; + } + } + } + else if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + + string[] dets = action.details.Split('|'); + if (dets.Length == 1) + dets = action.details.Split(','); + if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) + { + AppLogger.LogToTray("Controller " + (device + 1) + ": " + + ctrl.getDS4Battery(device), true); + } + if (bool.Parse(dets[2])) + { + DS4Device d = ctrl.DS4Controllers[device]; + if (!actionDone[index].dev[device]) + { + lastColor[device] = d.LightBarColor; + DS4LightBar.forcelight[device] = true; + } + DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); + DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); + DS4Color trans = getTransitionedColor(ref empty, ref full, d.Battery); + if (fadetimer[device] < 100) + DS4LightBar.forcedColor[device] = getTransitionedColor(ref lastColor[device], ref trans, fadetimer[device] += 2); + } + actionDone[index].dev[device] = true; + } + else if (action.typeID == SpecialAction.ActionTypeId.SASteeringWheelEmulationCalibrate) + { + actionFound = true; + + DS4Device d = ctrl.DS4Controllers[device]; + // If controller is not already in SASteeringWheelCalibration state then enable it now. If calibration is active then complete it (commit calibration values) + if (d.WheelRecalibrateActiveState == 0 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 1; // Start calibration process + } + else if (d.WheelRecalibrateActiveState == 2 && DateTime.UtcNow > (action.firstTap + TimeSpan.FromMilliseconds(3000))) + { + action.firstTap = DateTime.UtcNow; + d.WheelRecalibrateActiveState = 3; // Complete calibration process + } + + actionDone[index].dev[device] = true; + } + } + else + { + if (action.typeID == SpecialAction.ActionTypeId.BatteryCheck) + { + actionFound = true; + if (actionDone[index].dev[device]) + { + fadetimer[device] = 0; + /*if (prevFadetimer[device] == fadetimer[device]) + { + prevFadetimer[device] = 0; + fadetimer[device] = 0; + } + else + prevFadetimer[device] = fadetimer[device];*/ + DS4LightBar.forcelight[device] = false; + actionDone[index].dev[device] = false; + } + } + else if (action.typeID == SpecialAction.ActionTypeId.DisconnectBT && action.pressRelease) + { + actionFound = true; + DS4Device d = ctrl.DS4Controllers[device]; + ConnectionType deviceConn = d.getConnectionType(); + if (deviceConn == ConnectionType.SONYWA && d.isSynced()) + { + if (d.isDS4Idle()) + { + d.DisconnectDongle(); + ReleaseActionKeys(action, device); + actionDone[index].dev[device] = false; + action.pressRelease = false; + } + } + } + else if (action.typeID != SpecialAction.ActionTypeId.Key && + action.typeID != SpecialAction.ActionTypeId.XboxGameDVR && + action.typeID != SpecialAction.ActionTypeId.MultiAction) + { + // Ignore + actionFound = true; + actionDone[index].dev[device] = false; + } + } + + if (!actionFound) + { + if (uTriggerCount > 0 && utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Key) + { + actionFound = true; + + if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) + { + actionDone[index].dev[device] = true; + untriggerindex[device] = -1; + ushort key; + ushort.TryParse(action.details, out key); + if (action.keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyRelease(key); + else + InputMethods.performKeyRelease(key); + } + } + else if (action.typeID == SpecialAction.ActionTypeId.XboxGameDVR || action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + actionFound = true; + + bool tappedOnce = action.tappedOnce, firstTouch = action.firstTouch, + secondtouchbegin = action.secondtouchbegin; + //DateTime pastTime = action.pastTime, firstTap = action.firstTap, + // TimeofEnd = action.TimeofEnd; + + /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) + getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; + if (getCustomMacro(device, action.trigger[0]) != "0") + getCustomMacros(device).Remove(action.trigger[0]); + if (getCustomKey(device, action.trigger[0]) != 0) + getCustomMacros(device).Remove(action.trigger[0]);*/ + string[] dets = action.details.Split(','); + DS4Device d = ctrl.DS4Controllers[device]; + //cus + + DS4State tempPrevState = d.getPreviousStateRef(); + // Only create one instance of previous DS4StateFieldMapping in case more than one multi-action + // button is assigned + if (previousFieldMapping == null) + { + previousFieldMapping = previousFieldMappings[device]; + previousFieldMapping.populateFieldMapping(tempPrevState, eState, tp, true); + //previousFieldMapping = new DS4StateFieldMapping(tempPrevState, eState, tp, true); + } + + bool activeCur = getBoolSpecialActionMapping(device, action.trigger[0], cState, eState, tp, fieldMapping); + bool activePrev = getBoolSpecialActionMapping(device, action.trigger[0], tempPrevState, eState, tp, previousFieldMapping); + if (activeCur && !activePrev) + { + // pressed down + action.pastTime = DateTime.UtcNow; + if (action.pastTime <= (action.firstTap + TimeSpan.FromMilliseconds(150))) + { + action.tappedOnce = tappedOnce = false; + action.secondtouchbegin = secondtouchbegin = true; + //tappedOnce = false; + //secondtouchbegin = true; + } + else + action.firstTouch = firstTouch = true; + //firstTouch = true; + } + else if (!activeCur && activePrev) + { + // released + if (secondtouchbegin) + { + action.firstTouch = firstTouch = false; + action.secondtouchbegin = secondtouchbegin = false; + //firstTouch = false; + //secondtouchbegin = false; + } + else if (firstTouch) + { + action.firstTouch = firstTouch = false; + //firstTouch = false; + if (DateTime.UtcNow <= (action.pastTime + TimeSpan.FromMilliseconds(150)) && !tappedOnce) + { + action.tappedOnce = tappedOnce = true; + //tappedOnce = true; + action.firstTap = DateTime.UtcNow; + action.TimeofEnd = DateTime.UtcNow; + } + } + } + + int type = 0; + string macro = ""; + if (tappedOnce) //single tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[0]; + } + else if (int.TryParse(dets[0], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if ((DateTime.UtcNow - action.TimeofEnd) > TimeSpan.FromMilliseconds(150)) + { + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + tappedOnce = false; + action.tappedOnce = false; + } + //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) + } + else if (firstTouch && (DateTime.UtcNow - action.pastTime) > TimeSpan.FromMilliseconds(500)) //helddown + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[1]; + } + else if (int.TryParse(dets[1], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + firstTouch = false; + action.firstTouch = false; + } + else if (secondtouchbegin) //if double tap + { + if (action.typeID == SpecialAction.ActionTypeId.MultiAction) + { + macro = dets[2]; + } + else if (int.TryParse(dets[2], out type)) + { + switch (type) + { + case 0: macro = "91/71/71/91"; break; + case 1: macro = "91/164/82/82/164/91"; break; + case 2: macro = "91/164/44/44/164/91"; break; + case 3: macro = dets[3] + "/" + dets[3]; break; + case 4: macro = "91/164/71/71/164/91"; break; + } + } + + if (macro != "") + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); + + secondtouchbegin = false; + action.secondtouchbegin = false; + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + } + } + catch { return; } + + if (untriggeraction[device] != null) + { + SpecialAction action = untriggeraction[device]; + int index = untriggerindex[device]; + bool utriggeractivated; + + if (!action.automaticUntrigger) + { + // Untrigger keys defined and auto-untrigger (=unload) profile option is NOT set. Unload a temporary profile only when specified untrigger keys have been triggered. + utriggeractivated = true; + + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, uTrigLen = action.uTrigger.Count; i < uTrigLen; i++) + { + DS4Controls dc = action.uTrigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = false; + break; + } + } + } + else + { + // Untrigger as soon any of the defined regular trigger keys have been released. + utriggeractivated = false; + + for (int i = 0, trigLen = action.trigger.Count; i < trigLen; i++) + { + DS4Controls dc = action.trigger[i]; + if (!getBoolSpecialActionMapping(device, dc, cState, eState, tp, fieldMapping)) + { + utriggeractivated = true; + break; + } + } + } + + if (utriggeractivated && action.typeID == SpecialAction.ActionTypeId.Profile) + { + if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same + action.controls != action.ucontrols) + { + if (useTempProfile[device]) + { + //foreach (DS4Controls dc in action.uTrigger) + for (int i = 0, arlen = action.uTrigger.Count; i < arlen; i++) + { + DS4Controls dc = action.uTrigger[i]; + actionDone[index].dev[device] = true; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + + string profileName = untriggeraction[device].prevProfileName; + string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (device + 1).ToString()).Replace("*Profile name*", (profileName == string.Empty ? ProfilePath[device] : profileName)); + AppLogger.LogToGui(prolog, false); + + untriggeraction[device] = null; + + if (profileName == string.Empty) + LoadProfile(device, false, ctrl); // Previous profile was a regular default profile of a controller + else + LoadTempProfile(device, profileName, true, ctrl); // Previous profile was a temporary profile, so re-load it as a temp profile + } + } + } + else + { + actionDone[index].dev[device] = false; + } + } + } + + private static void ReleaseActionKeys(SpecialAction action, int device) + { + //foreach (DS4Controls dc in action.trigger) + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + DS4ControlSettings dcs = getDS4CSetting(device, dc); + if (dcs.action != null) + { + if (dcs.actionType == DS4ControlSettings.ActionType.Key) + InputMethods.performKeyRelease((ushort)dcs.action); + else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) + { + int[] keys = (int[])dcs.action; + for (int j = 0, keysLen = keys.Length; j < keysLen; j++) + InputMethods.performKeyRelease((ushort)keys[j]); + } + } + } + } + + // Play macro as a background task. Optionally the new macro play waits for completion of a previous macro execution (synchronized macro special action). + // Macro steps are defined either as macrostr string value, macroLst list object or as macroArr integer array. Only one of these should have a valid macro definition when this method is called. + // If the macro definition is a macroStr string value then it will be converted as integer array on the fl. If steps are already defined as list or array of integers then there is no need to do type cast conversion. + private static void PlayMacro(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action = null, ActionState actionDoneState = null) + { + if (action != null && action.synchronized) + { + // Run special action macros in synchronized order (ie. FirstIn-FirstOut). The trigger control name string is the execution queue identifier (ie. each unique trigger combination has an own synchronization queue). + if (!macroTaskQueue[device].TryGetValue(action.controls, out Task prevTask)) + macroTaskQueue[device].Add(action.controls, (Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState))) ); + else + macroTaskQueue[device][action.controls] = prevTask.ContinueWith((x) => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + else + // Run macro as "fire and forget" background task. No need to wait for completion of any of the other macros. + // If the same trigger macro is re-launched while previous macro is still running then the order of parallel macros is not guaranteed. + Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + + // Play through a macro. The macro steps are defined either as string, List or Array object (always only one of those parameters is set to a valid value) + private static void PlayMacroTask(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action, ActionState actionDoneState) + { + if(!String.IsNullOrEmpty(macroStr)) + { + string[] skeys; + + skeys = macroStr.Split('/'); + macroArr = new int[skeys.Length]; + for (int i = 0; i < macroArr.Length; i++) + macroArr[i] = int.Parse(skeys[i]); + } + + // macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18") + if ( (macroLst != null && macroLst.Count >= 4 && ((macroLst[0] == 164 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 164) || (macroLst[0] == 18 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 18))) + || (macroArr != null && macroArr.Length>= 4 && ((macroArr[0] == 164 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 164) || (macroArr[0] == 18 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 18))) + ) + { + int wait; + if(macroLst != null) + wait = macroLst[macroLst.Count - 1]; + else + wait = macroArr[macroArr.Length - 1]; + + if (wait <= 300 || wait > ushort.MaxValue) + wait = 1000; + else + wait -= 300; + + AltTabSwapping(wait, device); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + } + else if(control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + { + int macroCodeValue; + bool[] keydown = new bool[286]; + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + + // Play macro codes and simulate key down/up events (note! The same key may go through several up and down events during the same macro). + // If the return value is TRUE then this method should do a asynchronized delay (the usual Thread.Sleep doesnt work here because it would block the main gamepad reading thread). + if (macroLst != null) + { + for (int i = 0; i < macroLst.Count; i++) + { + macroCodeValue = macroLst[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + else + { + for (int i = 0; i < macroArr.Length; i++) + { + macroCodeValue = macroArr[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + Task.Delay(macroCodeValue - 300).Wait(); + } + } + + // The macro is finished. If any of the keys is still in down state then release a key state (ie. simulate key up event) unless special action specified to keep the last state as it is left in a macro + if (action == null || !action.keepKeyState) + { + for (int i = 0, arlength = keydown.Length; i < arlength; i++) + { + if (keydown[i]) + PlayMacroCodeValue(device, macrocontrol, keyType, i, keydown); + } + } + + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + + // Commented out rumble reset. No need to zero out rumble after a macro because it may conflict with a game generated rumble events (ie. macro would stop a game generated rumble effect). + // If macro generates rumble effects then the macro can stop the rumble as a last step or wait for rumble watchdog timer to do it after few seconds. + //Program.rootHub.DS4Controllers[device].setRumble(0, 0); + + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + Task.Delay(50).Wait(); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + } + + // If a special action type of Macro has "Repeat while held" option and actionDoneState object is defined then reset the action back to "not done" status in order to re-fire it if the trigger key is still held down + if (actionDoneState != null && keyType.HasFlag(DS4KeyType.RepeatMacro)) + actionDoneState.dev[device] = false; + } + + private static bool PlayMacroCodeValue(int device, bool[] macrocontrol, DS4KeyType keyType, int macroCodeValue, bool[] keydown) + { + bool doDelayOnCaller = false; + if (macroCodeValue >= 261 && macroCodeValue <= 285) + { + // Gamepad button up or down macro event. macroCodeValue index value is the button identifier (codeValue-261 = idx in 0..24 range) + if (!keydown[macroCodeValue]) + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = true; + macroCount++; + } + else + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = false; + if (macroCount > 0) macroCount--; + } + } + else if (macroCodeValue < 300) + { + // Keyboard key or mouse button macro event + if (!keydown[macroCodeValue]) + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyPress((ushort)macroCodeValue); + else InputMethods.performKeyPress((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = true; + } + else + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyRelease((ushort)macroCodeValue); + else InputMethods.performKeyRelease((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = false; + } + } + else if (macroCodeValue >= 1000000000) + { + // Lightbar color event + if (macroCodeValue > 1000000000) + { + string lb = macroCodeValue.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + DS4LightBar.forcelight[device] = true; + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); + } + else + { + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + } + } + else if (macroCodeValue >= 1000000) + { + // Rumble event + DS4Device d = Program.rootHub.DS4Controllers[device]; + string r = macroCodeValue.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + d.setRumble(light, heavy); + } + else + { + // Delay specification. Indicate to caller that it should do a delay of macroCodeValue-300 msecs + doDelayOnCaller = true; + } + + return doDelayOnCaller; + } + + private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) + { + if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, List macro, DS4Controls control) + { + if(macro.Count >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, int[] macro, DS4Controls control) + { + if (macro.Length >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void AltTabSwapping(int wait, int device) + { + if (altTabDone) + { + altTabDone = false; + InputMethods.performKeyPress(18); + } + else + { + altTabNow = DateTime.UtcNow; + if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) + { + oldAltTabNow = altTabNow; + InputMethods.performKeyPress(9); + InputMethods.performKeyRelease(9); + } + } + } + + private static void AltTabSwappingRelease() + { + if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + { + altTabDone = true; + InputMethods.performKeyRelease(9); + InputMethods.performKeyRelease(18); + altTabNow = DateTime.UtcNow; + oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + } + } + + private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, bool down) + { + DateTime now = DateTime.UtcNow; + if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + { + oldnow = now; + InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 8.0f * (down ? -1 : 1)), 0); + } + } + + private static double getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, + DS4StateFieldMapping fieldMapping, int mnum, ControlService ctrl) + { + int controlnum = DS4ControltoInt(control); + + int deadzoneL = 0; + int deadzoneR = 0; + if (getLSDeadzone(device) == 0) + deadzoneL = 3; + if (getRSDeadzone(device) == 0) + deadzoneR = 3; + + double value = 0.0; + int speed = ButtonMouseSensitivity[device]; + double root = 1.002; + double divide = 10000d; + //DateTime now = mousenow[mnum]; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + //long timeElapsed = ctrl.DS4Controllers[device].getLastTimeElapsed(); + double timeElapsed = ctrl.DS4Controllers[device].lastTimeElapsedDouble; + //double mouseOffset = 0.025; + double tempMouseOffsetX = 0.0, tempMouseOffsetY = 0.0; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + bool active = fieldMapping.buttons[controlNum]; + value = (active ? Math.Pow(root + speed / divide, 100) - 1 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + if (cState.LX < 128 - deadzoneL) + { + double diff = -(cState.LX - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LX - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LXPos: + { + if (cState.LX > 128 + deadzoneL) + { + double diff = (cState.LX - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetX = cState.LXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.LSAngleRad)) * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LX - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RXNeg: + { + if (cState.RX < 128 - deadzoneR) + { + double diff = -(cState.RX - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + (tempMouseOffsetX * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RX - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RXPos: + { + if (cState.RX > 128 + deadzoneR) + { + double diff = (cState.RX - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetX = cState.RXUnit * MOUSESTICKOFFSET; + //tempMouseOffsetX = MOUSESTICKOFFSET; + //tempMouseOffsetX = Math.Abs(Math.Cos(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetX) * diff + tempMouseOffsetX; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RX - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.LYNeg: + { + if (cState.LY < 128 - deadzoneL) + { + double diff = -(cState.LY - 128 - deadzoneL) / (double)(0 - 128 - deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.LY - 127 - deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.LYPos: + { + if (cState.LY > 128 + deadzoneL) + { + double diff = (cState.LY - 128 + deadzoneL) / (double)(255 - 128 + deadzoneL); + tempMouseOffsetY = cState.LYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.LSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.LY - 127 + deadzoneL) / 2550d * speed; + } + + break; + } + case DS4Controls.RYNeg: + { + if (cState.RY < 128 - deadzoneR) + { + double diff = -(cState.RY - 128 - deadzoneR) / (double)(0 - 128 - deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + (tempMouseOffsetY * -1.0); + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = -(cState.RY - 127 - deadzoneR) / 2550d * speed; + } + + break; + } + case DS4Controls.RYPos: + { + if (cState.RY > 128 + deadzoneR) + { + double diff = (cState.RY - 128 + deadzoneR) / (double)(255 - 128 + deadzoneR); + tempMouseOffsetY = cState.RYUnit * MOUSESTICKOFFSET; + //tempMouseOffsetY = MOUSESTICKOFFSET; + //tempMouseOffsetY = Math.Abs(Math.Sin(cState.RSAngleRad)) * MOUSESTICKOFFSET; + value = ((speed * MOUSESPEEDFACTOR * (timeElapsed * 0.001)) - tempMouseOffsetY) * diff + tempMouseOffsetY; + //value = diff * MOUSESPEEDFACTOR * (timeElapsed * 0.001) * speed; + //value = (cState.RY - 127 + deadzoneR) / 2550d * speed; + } + + break; + } + + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + byte trigger = fieldMapping.triggers[controlNum]; + value = Math.Pow(root + speed / divide, trigger / 2d) - 1; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + //double SXD = getSXDeadzone(device); + //double SZD = getSZDeadzone(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX > 0 ? Math.Pow(root + speed / divide, gyroX) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroX < 0 ? Math.Pow(root + speed / divide, -gyroX) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ > 0 ? Math.Pow(root + speed / divide, gyroZ) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMapping.gryodirs[controlNum]; + value = (byte)(gyroZ < 0 ? Math.Pow(root + speed / divide, -gyroZ) : 0); + break; + } + default: break; + } + } + + if (getMouseAccel(device)) + { + if (value > 0) + { + mcounter = 34; + mouseaccel++; + } + + if (mouseaccel == prevmouseaccel) + { + mcounter--; + } + + if (mcounter <= 0) + { + mouseaccel = 0; + mcounter = 34; + } + + value *= 1 + Math.Min(20000, (mouseaccel)) / 10000d; + prevmouseaccel = mouseaccel; + } + + return value; + } + + private static void calculateFinalMouseMovement(ref double rawMouseX, ref double rawMouseY, + out int mouseX, out int mouseY) + { + if ((rawMouseX > 0.0 && horizontalRemainder > 0.0) || (rawMouseX < 0.0 && horizontalRemainder < 0.0)) + { + rawMouseX += horizontalRemainder; + } + else + { + horizontalRemainder = 0.0; + } + + //double mouseXTemp = rawMouseX - (Math.IEEERemainder(rawMouseX * 1000.0, 1.0) / 1000.0); + double mouseXTemp = rawMouseX - (remainderCutoff(rawMouseX * 1000.0, 1.0) / 1000.0); + //double mouseXTemp = rawMouseX - (rawMouseX * 1000.0 - (1.0 * (int)(rawMouseX * 1000.0 / 1.0))); + mouseX = (int)mouseXTemp; + horizontalRemainder = mouseXTemp - mouseX; + //mouseX = (int)rawMouseX; + //horizontalRemainder = rawMouseX - mouseX; + + if ((rawMouseY > 0.0 && verticalRemainder > 0.0) || (rawMouseY < 0.0 && verticalRemainder < 0.0)) + { + rawMouseY += verticalRemainder; + } + else + { + verticalRemainder = 0.0; + } + + //double mouseYTemp = rawMouseY - (Math.IEEERemainder(rawMouseY * 1000.0, 1.0) / 1000.0); + double mouseYTemp = rawMouseY - (remainderCutoff(rawMouseY * 1000.0, 1.0) / 1000.0); + mouseY = (int)mouseYTemp; + verticalRemainder = mouseYTemp - mouseY; + //mouseY = (int)rawMouseY; + //verticalRemainder = rawMouseY - mouseY; + } + + private static double remainderCutoff(double dividend, double divisor) + { + return dividend - (divisor * (int)(dividend / divisor)); + } + + public static bool compare(byte b1, byte b2) + { + bool result = true; + if (Math.Abs(b1 - b2) > 10) + { + result = false; + } + + return result; + } + + private static byte getByteMapping2(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, + DS4StateFieldMapping fieldMap) + { + byte result = 0; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = (byte)(fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(axisValue - 128.0f >= 0 ? 0 : -(axisValue - 128.0f) * 1.9921875f); break; + default: result = (byte)(axisValue - 128.0f < 0 ? 0 : (axisValue - 128.0f) * 2.0078740157480315f); break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = (byte)(tp != null && fieldMap.buttons[controlNum] ? 255 : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = (byte)(tp != null ? fieldMap.swipedirs[controlNum] : 0); + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = fieldMap.gryodirs[controlNum]; + result = (byte)(sOff == false ? Math.Min(255, -gyroZ * 2) : 0); + break; + } + default: break; + } + } + + return result; + } + + public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + byte result = 0; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = (byte)(cState.Cross ? 255 : 0); break; + case DS4Controls.Square: result = (byte)(cState.Square ? 255 : 0); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? 255 : 0); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = (byte)(cState.L1 ? 255 : 0); break; + case DS4Controls.L2: result = cState.L2; break; + case DS4Controls.L3: result = (byte)(cState.L3 ? 255 : 0); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? 255 : 0); break; + case DS4Controls.R2: result = cState.R2; break; + case DS4Controls.R3: result = (byte)(cState.R3 ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? 255 : 0); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? 255 : 0); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? 255 : 0); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = (byte)(cState.LX - 128.0f >= 0 ? 0 : -(cState.LX - 128.0f) * 1.9921875f); break; + case DS4Controls.LYNeg: result = (byte)(cState.LY - 128.0f >= 0 ? 0 : -(cState.LY - 128.0f) * 1.9921875f); break; + case DS4Controls.RXNeg: result = (byte)(cState.RX - 128.0f >= 0 ? 0 : -(cState.RX - 128.0f) * 1.9921875f); break; + case DS4Controls.RYNeg: result = (byte)(cState.RY - 128.0f >= 0 ? 0 : -(cState.RY - 128.0f) * 1.9921875f); break; + case DS4Controls.LXPos: result = (byte)(cState.LX - 128.0f < 0 ? 0 : (cState.LX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.LYPos: result = (byte)(cState.LY - 128.0f < 0 ? 0 : (cState.LY - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RXPos: result = (byte)(cState.RX - 128.0f < 0 ? 0 : (cState.RX - 128.0f) * 2.0078740157480315f); break; + case DS4Controls.RYPos: result = (byte)(cState.RY - 128.0f < 0 ? 0 : (cState.RY - 128.0f) * 2.0078740157480315f); break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? 255 : 0); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? 255 : 0); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? 255 : 0); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? 255 : 0); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (byte)(tp != null ? tp.swipeUpB : 0); break; + case DS4Controls.SwipeDown: result = (byte)(tp != null ? tp.swipeDownB : 0); break; + case DS4Controls.SwipeLeft: result = (byte)(tp != null ? tp.swipeLeftB : 0); break; + case DS4Controls.SwipeRight: result = (byte)(tp != null ? tp.swipeRightB : 0); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + double sxsens = getSXSens(device); + double szsens = getSZSens(device); + + switch (control) + { + case DS4Controls.GyroXPos: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX > SXD * 10 ? Math.Min(255, sxsens * gyroX * 2) : 0); + break; + } + case DS4Controls.GyroXNeg: + { + int gyroX = -eState.AccelX; + result = (byte)(!sOff && sxsens * gyroX < -SXD * 10 ? Math.Min(255, sxsens * -gyroX * 2) : 0); + break; + } + case DS4Controls.GyroZPos: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ > SZD * 10 ? Math.Min(255, szsens * gyroZ * 2) : 0); + break; + } + case DS4Controls.GyroZNeg: + { + int gyroZ = eState.AccelZ; + result = (byte)(!sOff && szsens * gyroZ < -SZD * 10 ? Math.Min(255, szsens * -gyroZ * 2) : 0); + break; + } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? 255 : 0); break; + case DS4Controls.Options: result = (byte)(cState.Options ? 255 : 0); break; + case DS4Controls.PS: result = (byte)(cState.PS ? 255 : 0); break; + default: break; + } + } + + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) + { + bool result = false; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) + { + switch (control) + { + case DS4Controls.Cross: result = cState.Cross; break; + case DS4Controls.Square: result = cState.Square; break; + case DS4Controls.Triangle: result = cState.Triangle; break; + case DS4Controls.Circle: result = cState.Circle; break; + default: break; + } + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) + { + case DS4Controls.L1: result = cState.L1; break; + case DS4Controls.R1: result = cState.R1; break; + case DS4Controls.L2: result = cState.L2 > 100; break; + case DS4Controls.R2: result = cState.R2 > 100; break; + case DS4Controls.L3: result = cState.L3; break; + case DS4Controls.R3: result = cState.R3; break; + default: break; + } + } + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) + { + switch (control) + { + case DS4Controls.DpadUp: result = cState.DpadUp; break; + case DS4Controls.DpadDown: result = cState.DpadDown; break; + case DS4Controls.DpadLeft: result = cState.DpadLeft; break; + case DS4Controls.DpadRight: result = cState.DpadRight; break; + default: break; + } + } + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) + { + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + case DS4Controls.LXPos: result = cState.LX > 128 + 55; break; + case DS4Controls.LYPos: result = cState.LY > 128 + 55; break; + case DS4Controls.RXPos: result = cState.RX > 128 + 55; break; + case DS4Controls.RYPos: result = cState.RY > 128 + 55; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) + { + case DS4Controls.TouchLeft: result = (tp != null ? tp.leftDown : false); break; + case DS4Controls.TouchRight: result = (tp != null ? tp.rightDown : false); break; + case DS4Controls.TouchMulti: result = (tp != null ? tp.multiDown : false); break; + case DS4Controls.TouchUpper: result = (tp != null ? tp.upperDown : false); break; + default: break; + } + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) + { + case DS4Controls.SwipeUp: result = (tp != null && tp.swipeUp); break; + case DS4Controls.SwipeDown: result = (tp != null && tp.swipeDown); break; + case DS4Controls.SwipeLeft: result = (tp != null && tp.swipeLeft); break; + case DS4Controls.SwipeRight: result = (tp != null && tp.swipeRight); break; + default: break; + } + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + bool sOff = isUsingSAforMouse(device); + + switch (control) + { + case DS4Controls.GyroXPos: result = !sOff ? SXSens[device] * -eState.AccelX > 67 : false; break; + case DS4Controls.GyroXNeg: result = !sOff ? SXSens[device] * -eState.AccelX < -67 : false; break; + case DS4Controls.GyroZPos: result = !sOff ? SZSens[device] * eState.AccelZ > 67 : false; break; + case DS4Controls.GyroZNeg: result = !sOff ? SZSens[device] * eState.AccelZ < -67 : false; break; + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.PS: result = cState.PS; break; + case DS4Controls.Share: result = cState.Share; break; + case DS4Controls.Options: result = cState.Options; break; + default: break; + } + } + + return result; + } + + private static bool getBoolMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolSpecialActionMapping(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: result = cState.LX < 128 - 55; break; + case DS4Controls.LYNeg: result = cState.LY < 128 - 55; break; + case DS4Controls.RXNeg: result = cState.RX < 128 - 55; break; + case DS4Controls.RYNeg: result = cState.RY < 128 - 55; break; + default: result = axisValue > 128 + 55; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 100; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 67; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < -67; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + private static bool getBoolActionMapping2(int device, DS4Controls control, + DS4State cState, DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool analog = false) + { + bool result = false; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + switch (control) + { + case DS4Controls.LXNeg: + { + double angle = cState.LSAngle; + result = cState.LX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.LYNeg: + { + double angle = cState.LSAngle; + result = cState.LY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.RXNeg: + { + double angle = cState.RSAngle; + result = cState.RX < 128 && (angle >= 112.5 && angle <= 247.5); + break; + } + case DS4Controls.RYNeg: + { + double angle = cState.RSAngle; + result = cState.RY < 128 && (angle >= 22.5 && angle <= 157.5); + break; + } + case DS4Controls.LXPos: + { + double angle = cState.LSAngle; + result = cState.LX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.LYPos: + { + double angle = cState.LSAngle; + result = cState.LY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + case DS4Controls.RXPos: + { + double angle = cState.RSAngle; + result = cState.RX > 128 && (angle <= 67.5 || angle >= 292.5); + break; + } + case DS4Controls.RYPos: + { + double angle = cState.RSAngle; + result = cState.RY > 128 && (angle >= 202.5 && angle <= 337.5); + break; + } + default: break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + result = fieldMap.triggers[controlNum] > 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + result = fieldMap.swipedirbools[controlNum]; + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); + bool safeTest = false; + + switch (control) + { + case DS4Controls.GyroXPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroXNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + case DS4Controls.GyroZPos: safeTest = fieldMap.gryodirs[controlNum] > 0; break; + case DS4Controls.GyroZNeg: safeTest = fieldMap.gryodirs[controlNum] < 0; break; + default: break; + } + + result = sOff == false ? safeTest : false; + } + + return result; + } + + public static bool getBoolButtonMapping(bool stateButton) + { + return stateButton; + } + + public static bool getBoolAxisDirMapping(byte stateAxis, bool positive) + { + return positive ? stateAxis > 128 + 55 : stateAxis < 128 - 55; + } + + public static bool getBoolTriggerMapping(byte stateAxis) + { + return stateAxis > 100; + } + + public static bool getBoolTouchMapping(bool touchButton) + { + return touchButton; + } + + private static byte getXYAxisMapping2(int device, DS4Controls control, DS4State cState, + DS4StateExposed eState, Mouse tp, DS4StateFieldMapping fieldMap, bool alt = false) + { + const byte falseVal = 128; + byte result = 0; + byte trueVal = 0; + + if (alt) + trueVal = 255; + + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + + if (controlType == DS4StateFieldMapping.ControlType.Button) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) + { + byte axisValue = fieldMap.axisdirs[controlNum]; + + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.LYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RXNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + case DS4Controls.RYNeg: if (!alt) result = axisValue < falseVal ? axisValue : falseVal; else result = axisValue < falseVal ? (byte)(255 - axisValue) : falseVal; break; + default: if (!alt) result = axisValue > falseVal ? (byte)(255 - axisValue) : falseVal; else result = axisValue > falseVal ? axisValue : falseVal; break; + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + if (alt) + { + result = (byte)(128.0f + fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + else + { + result = (byte)(128.0f - fieldMap.triggers[controlNum] / 2.0078740157480315f); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + result = fieldMap.buttons[controlNum] ? trueVal : falseVal; + } + else if (controlType == DS4StateFieldMapping.ControlType.SwipeDir) + { + if (alt) + { + result = (byte)(tp != null ? 128.0f + fieldMap.swipedirs[controlNum] / 2f : 0); } - } - - if (macroControl[00]) MappedState.Cross = true; - if (macroControl[01]) MappedState.Circle = true; - if (macroControl[02]) MappedState.Square = true; - if (macroControl[03]) MappedState.Triangle = true; - if (macroControl[04]) MappedState.Options = true; - if (macroControl[05]) MappedState.Share = true; - if (macroControl[06]) MappedState.DpadUp = true; - if (macroControl[07]) MappedState.DpadDown = true; - if (macroControl[08]) MappedState.DpadLeft = true; - if (macroControl[09]) MappedState.DpadRight = true; - if (macroControl[10]) MappedState.PS = true; - if (macroControl[11]) MappedState.L1 = true; - if (macroControl[12]) MappedState.R1 = true; - if (macroControl[13]) MappedState.L2 = 255; - if (macroControl[14]) MappedState.R2 = 255; - if (macroControl[15]) MappedState.L3 = true; - if (macroControl[16]) MappedState.R3 = true; - if (macroControl[17]) MappedState.LX = 255; - if (macroControl[18]) MappedState.LX = 0; - if (macroControl[19]) MappedState.LY = 255; - if (macroControl[20]) MappedState.LY = 0; - if (macroControl[21]) MappedState.RX = 255; - if (macroControl[22]) MappedState.RX = 0; - if (macroControl[23]) MappedState.RY = 255; - if (macroControl[24]) MappedState.RY = 0; - foreach (DS4Controls dc in Cross) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Cross = true; - foreach (DS4Controls dc in Circle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Circle = true; - foreach (DS4Controls dc in Square) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Square = true; - foreach (DS4Controls dc in Triangle) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Triangle = true; - foreach (DS4Controls dc in L1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L1 = true; - foreach (DS4Controls dc in L2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.L2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in L3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.L3 = true; - foreach (DS4Controls dc in R1) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R1 = true; - foreach (DS4Controls dc in R2) - if (getByteMapping(device, dc, cState, eState, tp) > 5) - MappedState.R2 = getByteMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in R3) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.R3 = true; - foreach (DS4Controls dc in DpadUp) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadUp = true; - foreach (DS4Controls dc in DpadRight) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadRight = true; - foreach (DS4Controls dc in DpadLeft) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadLeft = true; - foreach (DS4Controls dc in DpadDown) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.DpadDown = true; - foreach (DS4Controls dc in Options) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Options = true; - foreach (DS4Controls dc in Share) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.Share = true; - foreach (DS4Controls dc in PS) - if (getBoolMapping(device, dc, cState, eState, tp)) - MappedState.PS = true; - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXNeg.ToString()), device, cState, eState, tp), DS4Controls.LXNeg)) - LXN.Add(DS4Controls.LXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LXPos.ToString()), device, cState, eState, tp), DS4Controls.LXPos)) - LXP.Add(DS4Controls.LXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYNeg.ToString()), device, cState, eState, tp), DS4Controls.LYNeg)) - LYN.Add(DS4Controls.LYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.LYPos.ToString()), device, cState, eState, tp), DS4Controls.LYPos)) - LYP.Add(DS4Controls.LYPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXNeg.ToString()), device, cState, eState, tp), DS4Controls.RXNeg)) - RXN.Add(DS4Controls.RXNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RXPos.ToString()), device, cState, eState, tp), DS4Controls.RXPos)) - RXP.Add(DS4Controls.RXPos); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYNeg.ToString()), device, cState, eState, tp), DS4Controls.RYNeg)) - RYN.Add(DS4Controls.RYNeg); - - if (IfAxisIsNotModified(device, ShiftTrigger(GetDS4STrigger(device, DS4Controls.RYPos.ToString()), device, cState, eState, tp), DS4Controls.RYPos)) - RYP.Add(DS4Controls.RYPos); - - if (Math.Abs(MappedState.LX - 127) < 10) - if (LXN.Count > 0 || LXP.Count > 0) - { - foreach (DS4Controls dc in LXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp, true); - foreach (DS4Controls dc in LXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LX = getXYAxisMapping(device, dc, cState, eState, tp); - } - //else - //MappedState.LX = cState.LX; - if (Math.Abs(MappedState.LY - 127) < 10) - if (LYN.Count > 0 || LYP.Count > 0) - { - foreach (DS4Controls dc in LYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in LYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.LY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - //MappedState.LY = cState.LY; - if (Math.Abs(MappedState.RX - 127) < 10) - if (RXN.Count > 0 || RXP.Count > 0) - { - foreach (DS4Controls dc in RXN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RXP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RX = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - //else - // MappedState.RX = cState.RX; - if (Math.Abs(MappedState.RY - 127) < 10) - if (RYN.Count > 0 || RYP.Count > 0) - { - foreach (DS4Controls dc in RYN) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp); - foreach (DS4Controls dc in RYP) - if (Math.Abs(127 - getXYAxisMapping(device, dc, cState, eState, tp, true)) > 5) - MappedState.RY = getXYAxisMapping(device, dc, cState, eState, tp, true); - } - // else - // MappedState.RY = cState.RY; - InputMethods.MoveCursorBy(MouseDeltaX, MouseDeltaY); - } + else + { + result = (byte)(tp != null ? 128.0f - fieldMap.swipedirs[controlNum] / 2f : 0); + } + } + else if (controlType == DS4StateFieldMapping.ControlType.GyroDir) + { + bool sOff = isUsingSAforMouse(device); - private static bool IfAxisIsNotModified(int device, bool shift, DS4Controls dc) - { - return shift ? false : GetDS4Action(device, dc.ToString(), false) == null; - } - public static async void MapCustomAction(int device, DS4State cState, DS4State MappedState, DS4StateExposed eState, Mouse tp, ControlService ctrl) - { - try { - foreach (string actionname in ProfileActions[device]) + switch (control) { - //DS4KeyType keyType = getShiftCustomKeyType(device, customKey.Key); - SpecialAction action = GetAction(actionname); - int index = GetActionIndexOf(actionname); - if (actionDone.Count < index + 1) - actionDone.Add(new ActionState()); - else if (actionDone.Count > GetActions().Count()) - actionDone.RemoveAt(actionDone.Count - 1); - double time; - //If a key or button is assigned to the trigger, a key special action is used like - //a quick tap to use and hold to use the regular custom button/key - bool triggerToBeTapped = action.type == "Key" && action.trigger.Count == 1 && - GetDS4Action(device, action.trigger[0].ToString(), false) == null; - if (!(action.name == "null" || index < 0)) + case DS4Controls.GyroXPos: { - bool triggeractivated = true; - if (action.delayTime > 0) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - time = action.delayTime; - nowAction[device] = DateTime.UtcNow; - if (nowAction[device] >= oldnowAction[device] + TimeSpan.FromSeconds(time)) - triggeractivated = true; - } - else if (nowAction[device] < DateTime.UtcNow - TimeSpan.FromMilliseconds(100)) - oldnowAction[device] = DateTime.UtcNow; - } - else if (triggerToBeTapped && oldnowKeyAct[device] == DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - if (subtriggeractivated) - { - oldnowKeyAct[device] = DateTime.UtcNow; - } - } - else if (triggerToBeTapped && oldnowKeyAct[device] != DateTime.MinValue) - { - triggeractivated = false; - bool subtriggeractivated = true; - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - subtriggeractivated = false; - break; - } - } - DateTime now = DateTime.UtcNow; - if (!subtriggeractivated && now <= oldnowKeyAct[device] + TimeSpan.FromMilliseconds(250)) - { - await Task.Delay(3); //if the button is assigned to the same key use a delay so the key down is the last action, not key up - triggeractivated = true; - oldnowKeyAct[device] = DateTime.MinValue; - } - else if (!subtriggeractivated) - oldnowKeyAct[device] = DateTime.MinValue; - } - else - foreach (DS4Controls dc in action.trigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - triggeractivated = false; - break; - } - } - - bool utriggeractivated = true; - if (action.type == "Key" && action.uTrigger.Count > 0) - { - foreach (DS4Controls dc in action.uTrigger) - { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } - } - if (action.pressRelease) utriggeractivated = !utriggeractivated; - } - - if (triggeractivated && action.type == "Program") - { - if (!actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - if (!string.IsNullOrEmpty(action.extra)) - Process.Start(action.details, action.extra); - else - Process.Start(action.details); - } - } - else if (triggeractivated && action.type == "Profile") - { - if (!actionDone[index].dev[device] && string.IsNullOrEmpty(tempprofilename[device])) - { - actionDone[index].dev[device] = true; - untriggeraction[device] = action; - untriggerindex[device] = index; - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease(ushort.Parse(action.ToString())); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - LoadTempProfile(device, action.details, true, ctrl); - return; - } - } - else if (triggeractivated && action.type == "Macro") - { - if (!actionDone[index].dev[device]) - { - DS4KeyType keyType = action.keyType; - actionDone[index].dev[device] = true; - foreach (DS4Controls dc in action.trigger) - resetToDefaultValue(dc, MappedState); - PlayMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None, keyType); - } - else - EndMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None); - } - else if (triggeractivated && action.type == "Key") + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (action.uTrigger.Count == 0 || (action.uTrigger.Count > 0 && untriggerindex[device] == -1 && !actionDone[index].dev[device])) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = index; - ushort key; - ushort.TryParse(action.details, out key); - if (action.uTrigger.Count == 0) - { - SyntheticState.KeyPresses kp; - if (!deviceState[device].keyPresses.TryGetValue(key, out kp)) - deviceState[device].keyPresses[key] = kp = new SyntheticState.KeyPresses(); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - kp.current.scanCodeCount++; - else - kp.current.vkCount++; - kp.current.repeatCount++; - } - else if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress(key); - else - InputMethods.performKeyPress(key); - } - } - else if (action.uTrigger.Count > 0 && utriggeractivated && action.type == "Key") - { - if (untriggerindex[device] > -1 && !actionDone[index].dev[device]) - { - actionDone[index].dev[device] = true; - untriggerindex[device] = -1; - ushort key; - ushort.TryParse(action.details, out key); - if (action.keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(key); - else - InputMethods.performKeyRelease(key); - } - } - else if (triggeractivated && action.type == "DisconnectBT") - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!d.Charging) - { - d.DisconnectBT(); - foreach (DS4Controls dc in action.trigger) - { - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - return; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - string[] dets = action.details.Split('|'); - if (dets.Length == 1) - dets = action.details.Split(','); - if (bool.Parse(dets[1]) && !actionDone[index].dev[device]) - { - Log.LogToTray("Controller " + (device + 1) + ": " + - ctrl.getDS4Battery(device), true); - } - if (bool.Parse(dets[2])) - { - DS4Device d = ctrl.DS4Controllers[device]; - if (!actionDone[index].dev[device]) - { - lastColor[device] = d.LightBarColor; - DS4LightBar.forcelight[device] = true; - } - DS4Color empty = new DS4Color(byte.Parse(dets[3]), byte.Parse(dets[4]), byte.Parse(dets[5])); - DS4Color full = new DS4Color(byte.Parse(dets[6]), byte.Parse(dets[7]), byte.Parse(dets[8])); - DS4Color trans = getTransitionedColor(empty, full, d.Battery); - if (fadetimer[device] < 100) - DS4LightBar.forcedColor[device] = getTransitionedColor(lastColor[device], trans, fadetimer[device] += 2); - } - actionDone[index].dev[device] = true; + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else if (!triggeractivated && action.type == "BatteryCheck") + else result = falseVal; + break; + } + case DS4Controls.GyroZPos: + { + if (sOff == false && fieldMap.gryodirs[controlNum] > 0) { - if (actionDone[index].dev[device]) - { - fadetimer[device] = 0; - /*if (prevFadetimer[device] == fadetimer[device]) - { - prevFadetimer[device] = 0; - fadetimer[device] = 0; - } - else - prevFadetimer[device] = fadetimer[device];*/ - DS4LightBar.forcelight[device] = false; - actionDone[index].dev[device] = false; - } + if (alt) result = (byte)Math.Min(255, 128 + fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - fieldMap.gryodirs[controlNum]); } - else if (action.type == "XboxGameDVR" || action.type == "MultiAction") + else return falseVal; + break; + } + case DS4Controls.GyroZNeg: + { + if (sOff == false && fieldMap.gryodirs[controlNum] < 0) { - /*if (getCustomButton(device, action.trigger[0]) != X360Controls.Unbound) - getCustomButtons(device)[action.trigger[0]] = X360Controls.Unbound; - if (getCustomMacro(device, action.trigger[0]) != "0") - getCustomMacros(device).Remove(action.trigger[0]); - if (getCustomKey(device, action.trigger[0]) != 0) - getCustomMacros(device).Remove(action.trigger[0]);*/ - string[] dets = action.details.Split(','); - DS4Device d = ctrl.DS4Controllers[device]; - //cus - if (getBoolMapping(device, action.trigger[0], cState, eState, tp) && !getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//pressed down - pastTime = DateTime.UtcNow; - if (DateTime.UtcNow <= (firstTap + TimeSpan.FromMilliseconds(150))) - { - tappedOnce = false; - secondtouchbegin = true; - } - else - firstTouch = true; - } - else if (!getBoolMapping(device, action.trigger[0], cState, eState, tp) && getBoolMapping(device, action.trigger[0], d.getPreviousState(), eState, tp)) - {//released - if (secondtouchbegin) - { - firstTouch = false; - secondtouchbegin = false; - } - else if (firstTouch) - { - firstTouch = false; - if (DateTime.UtcNow <= (pastTime + TimeSpan.FromMilliseconds(200)) && !tappedOnce) - { - tappedOnce = true; - firstTap = DateTime.UtcNow; - TimeofEnd = DateTime.UtcNow; - } - } - } - - int type = 0; - string macro = ""; - if (tappedOnce) //single tap - { - if (action.type == "MultiAction") - { - macro = dets[0]; - } - else if (int.TryParse(dets[0], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if ((DateTime.UtcNow - TimeofEnd) > TimeSpan.FromMilliseconds(150)) - { - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - tappedOnce = false; - } - //if it fails the method resets, and tries again with a new tester value (gives tap a delay so tap and hold can work) - } - else if (firstTouch && (DateTime.UtcNow - pastTime) > TimeSpan.FromMilliseconds(1000)) //helddown - { - if (action.type == "MultiAction") - { - macro = dets[1]; - } - else if (int.TryParse(dets[1], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - firstTouch = false; - } - else if (secondtouchbegin) //if double tap - { - if (action.type == "MultiAction") - { - macro = dets[2]; - } - else if (int.TryParse(dets[2], out type)) - { - switch (type) - { - case 0: macro = "91/71/71/91"; break; - case 1: macro = "91/164/82/82/164/91"; break; - case 2: macro = "91/164/44/44/164/91"; break; - case 3: macro = dets[3] + "/" + dets[3]; break; - case 4: macro = "91/164/71/71/164/91"; break; - } - } - if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); - secondtouchbegin = false; - } + if (alt) result = (byte)Math.Min(255, 128 + -fieldMap.gryodirs[controlNum]); else result = (byte)Math.Max(0, 128 - -fieldMap.gryodirs[controlNum]); } - else - actionDone[index].dev[device] = false; + else result = falseVal; + break; } + default: break; } } - catch { return; } - if (untriggeraction[device] != null) + return result; + } + + /* TODO: Possibly remove usage of this version of the method */ + public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) + { + byte result = 0; + byte trueVal = 0; + byte falseVal = 127; + + if (alt) + trueVal = 255; + + if (control >= DS4Controls.Square && control <= DS4Controls.Cross) { - SpecialAction action = untriggeraction[device]; - int index = untriggerindex[device]; - bool utriggeractivated = true; - foreach (DS4Controls dc in action.uTrigger) + switch (control) { - if (!getBoolMapping(device, dc, cState, eState, tp)) - { - utriggeractivated = false; - break; - } + case DS4Controls.Cross: result = (byte)(cState.Cross ? trueVal : falseVal); break; + case DS4Controls.Square: result = (byte)(cState.Square ? trueVal : falseVal); break; + case DS4Controls.Triangle: result = (byte)(cState.Triangle ? trueVal : falseVal); break; + case DS4Controls.Circle: result = (byte)(cState.Circle ? trueVal : falseVal); break; + default: break; } - - if (utriggeractivated && action.type == "Profile") + } + else if (control >= DS4Controls.L1 && control <= DS4Controls.R3) + { + switch (control) { - if ((action.controls == action.ucontrols && !actionDone[index].dev[device]) || //if trigger and end trigger are the same - action.controls != action.ucontrols) - if (!string.IsNullOrEmpty(tempprofilename[device])) - { - foreach (DS4Controls dc in action.uTrigger) - { - actionDone[index].dev[device] = true; - DS4ControlSettings dcs = getDS4CSetting(device, dc.ToString()); - if (dcs.action != null) - { - if (dcs.actionType == DS4ControlSettings.ActionType.Key) - InputMethods.performKeyRelease((ushort)dcs.action); - else if (dcs.actionType == DS4ControlSettings.ActionType.Macro) - { - int[] keys = (int[])dcs.action; - for (int i = 0; i < keys.Length; i++) - InputMethods.performKeyRelease((ushort)keys[i]); - } - } - } - untriggeraction[device] = null; - LoadProfile(device, false, ctrl); - } + case DS4Controls.L1: result = (byte)(cState.L1 ? trueVal : falseVal); break; + case DS4Controls.L2: if (alt) result = (byte)(128.0f + cState.L2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.L2 / 2.0078740157480315f); break; + case DS4Controls.L3: result = (byte)(cState.L3 ? trueVal : falseVal); break; + case DS4Controls.R1: result = (byte)(cState.R1 ? trueVal : falseVal); break; + case DS4Controls.R2: if (alt) result = (byte)(128.0f + cState.R2 / 2.0078740157480315f); else result = (byte)(128.0f - cState.R2 / 2.0078740157480315f); break; + case DS4Controls.R3: result = (byte)(cState.R3 ? trueVal : falseVal); break; + default: break; } - else - actionDone[index].dev[device] = false; } - } - - private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) - { - if (macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) + else if (control >= DS4Controls.DpadUp && control <= DS4Controls.DpadLeft) { - string[] skeys; - int wait = 1000; - if (!string.IsNullOrEmpty(macro)) + switch (control) { - skeys = macro.Split('/'); - ushort delay; - if (ushort.TryParse(skeys[skeys.Length - 1], out delay) && delay > 300) - wait = delay - 300; + case DS4Controls.DpadUp: result = (byte)(cState.DpadUp ? trueVal : falseVal); break; + case DS4Controls.DpadDown: result = (byte)(cState.DpadDown ? trueVal : falseVal); break; + case DS4Controls.DpadLeft: result = (byte)(cState.DpadLeft ? trueVal : falseVal); break; + case DS4Controls.DpadRight: result = (byte)(cState.DpadRight ? trueVal : falseVal); break; + default: break; } - AltTabSwapping(wait, device); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; } - else + else if (control >= DS4Controls.LXNeg && control <= DS4Controls.RYPos) { - string[] skeys; - int[] keys; - if (!string.IsNullOrEmpty(macro)) + switch (control) + { + case DS4Controls.LXNeg: if (!alt) result = cState.LX; else result = (byte)(255 - cState.LX); break; + case DS4Controls.LYNeg: if (!alt) result = cState.LY; else result = (byte)(255 - cState.LY); break; + case DS4Controls.RXNeg: if (!alt) result = cState.RX; else result = (byte)(255 - cState.RX); break; + case DS4Controls.RYNeg: if (!alt) result = cState.RY; else result = (byte)(255 - cState.RY); break; + case DS4Controls.LXPos: if (!alt) result = (byte)(255 - cState.LX); else result = cState.LX; break; + case DS4Controls.LYPos: if (!alt) result = (byte)(255 - cState.LY); else result = cState.LY; break; + case DS4Controls.RXPos: if (!alt) result = (byte)(255 - cState.RX); else result = cState.RX; break; + case DS4Controls.RYPos: if (!alt) result = (byte)(255 - cState.RY); else result = cState.RY; break; + default: break; + } + } + else if (control >= DS4Controls.TouchLeft && control <= DS4Controls.TouchRight) + { + switch (control) { - skeys = macro.Split('/'); - keys = new int[skeys.Length]; + case DS4Controls.TouchLeft: result = (byte)(tp != null && tp.leftDown ? trueVal : falseVal); break; + case DS4Controls.TouchRight: result = (byte)(tp != null && tp.rightDown ? trueVal : falseVal); break; + case DS4Controls.TouchMulti: result = (byte)(tp != null && tp.multiDown ? trueVal : falseVal); break; + case DS4Controls.TouchUpper: result = (byte)(tp != null && tp.upperDown ? trueVal : falseVal); break; + default: break; } - else + } + else if (control >= DS4Controls.SwipeLeft && control <= DS4Controls.SwipeDown) + { + switch (control) { - skeys = new string[0]; - keys = new int[0]; + case DS4Controls.SwipeUp: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeUpB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeUpB / 2f : 0); break; + case DS4Controls.SwipeDown: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeDownB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeDownB / 2f : 0); break; + case DS4Controls.SwipeLeft: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeLeftB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeLeftB / 2f : 0); break; + case DS4Controls.SwipeRight: if (alt) result = (byte)(tp != null ? 128.0f + tp.swipeRightB / 2f : 0); else result = (byte)(tp != null ? 128.0f - tp.swipeRightB / 2f : 0); break; + default: break; } - for (int i = 0; i < keys.Length; i++) - keys[i] = int.Parse(skeys[i]); - bool[] keydown = new bool[286]; - if (control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + } + else if (control >= DS4Controls.GyroXPos && control <= DS4Controls.GyroZNeg) + { + double SXD = getSXDeadzone(device); + double SZD = getSZDeadzone(device); + bool sOff = isUsingSAforMouse(device); + + switch (control) { - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; - foreach (int i in keys) + case DS4Controls.GyroXPos: { - if (i >= 1000000000) - { - string lb = i.ToString().Substring(1); - if (i > 1000000000) - { - byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); - byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); - byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); - DS4LightBar.forcelight[device] = true; - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); - } - else - { - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - } - } - else if (i >= 1000000) - { - DS4Device d = Program.rootHub.DS4Controllers[device]; - string r = i.ToString().Substring(1); - byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); - byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); - d.setRumble(light, heavy); - } - else if (i >= 300) //ints over 300 used to delay - await Task.Delay(i - 300); - else if (!keydown[i]) + if (!sOff && -eState.AccelX > SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (i == 261) macroControl[0] = true; - else if (i == 262) macroControl[1] = true; - else if (i == 263) macroControl[2] = true; - else if (i == 264) macroControl[3] = true; - else if (i == 265) macroControl[4] = true; - else if (i == 266) macroControl[5] = true; - else if (i == 267) macroControl[6] = true; - else if (i == 268) macroControl[7] = true; - else if (i == 269) macroControl[8] = true; - else if (i == 270) macroControl[9] = true; - else if (i == 271) macroControl[10] = true; - else if (i == 272) macroControl[11] = true; - else if (i == 273) macroControl[12] = true; - else if (i == 274) macroControl[13] = true; - else if (i == 275) macroControl[14] = true; - else if (i == 276) macroControl[15] = true; - else if (i == 277) macroControl[16] = true; - else if (i == 278) macroControl[17] = true; - else if (i == 279) macroControl[18] = true; - else if (i == 280) macroControl[19] = true; - else if (i == 281) macroControl[20] = true; - else if (i == 282) macroControl[21] = true; - else if (i == 283) macroControl[22] = true; - else if (i == 284) macroControl[23] = true; - else if (i == 285) macroControl[24] = true; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress((ushort)i); - else - InputMethods.performKeyPress((ushort)i); - keydown[i] = true; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * -eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * -eState.AccelX); } - else + else result = falseVal; + break; + } + case DS4Controls.GyroXNeg: + { + if (!sOff && -eState.AccelX < -SXD * 10) { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease((ushort)i); - else - InputMethods.performKeyRelease((ushort)i); - keydown[i] = false; + if (alt) result = (byte)Math.Min(255, 127 + SXSens[device] * eState.AccelX); else result = (byte)Math.Max(0, 127 - SXSens[device] * eState.AccelX); } + else result = falseVal; + break; } - for (ushort i = 0; i < keydown.Length; i++) + case DS4Controls.GyroZPos: { - if (keydown[i]) - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) macroControl[0] = false; - else if (i == 262) macroControl[1] = false; - else if (i == 263) macroControl[2] = false; - else if (i == 264) macroControl[3] = false; - else if (i == 265) macroControl[4] = false; - else if (i == 266) macroControl[5] = false; - else if (i == 267) macroControl[6] = false; - else if (i == 268) macroControl[7] = false; - else if (i == 269) macroControl[8] = false; - else if (i == 270) macroControl[9] = false; - else if (i == 271) macroControl[10] = false; - else if (i == 272) macroControl[11] = false; - else if (i == 273) macroControl[12] = false; - else if (i == 274) macroControl[13] = false; - else if (i == 275) macroControl[14] = false; - else if (i == 276) macroControl[15] = false; - else if (i == 277) macroControl[16] = false; - else if (i == 278) macroControl[17] = false; - else if (i == 279) macroControl[18] = false; - else if (i == 280) macroControl[19] = false; - else if (i == 281) macroControl[20] = false; - else if (i == 282) macroControl[21] = false; - else if (i == 283) macroControl[22] = false; - else if (i == 284) macroControl[23] = false; - else if (i == 285) macroControl[24] = false; - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease(i); - else - InputMethods.performKeyRelease(i); + if (!sOff && eState.AccelZ > SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * eState.AccelZ); + } + else return falseVal; + break; } - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - Program.rootHub.DS4Controllers[device].setRumble(0, 0); - if (keyType.HasFlag(DS4KeyType.HoldMacro)) + case DS4Controls.GyroZNeg: { - await Task.Delay(50); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + if (!sOff && eState.AccelZ < -SZD * 10) + { + if (alt) result = (byte)Math.Min(255, 127 + SZSens[device] * -eState.AccelZ); else result = (byte)Math.Max(0, 127 - SZSens[device] * -eState.AccelZ); + } + else result = falseVal; + break; } + default: break; + } + } + else + { + switch (control) + { + case DS4Controls.Share: result = (byte)(cState.Share ? trueVal : falseVal); break; + case DS4Controls.Options: result = (byte)(cState.Options ? trueVal : falseVal); break; + case DS4Controls.PS: result = (byte)(cState.PS ? trueVal : falseVal); break; + default: break; } } - } - private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) - { - if ((macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) && !altTabDone) - AltTabSwappingRelease(); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + return result; } - private static void AltTabSwapping(int wait, int device) + + private static void resetToDefaultValue2(DS4Controls control, DS4State cState, + DS4StateFieldMapping fieldMap) { - if (altTabDone) + int controlNum = (int)control; + DS4StateFieldMapping.ControlType controlType = DS4StateFieldMapping.mappedType[controlNum]; + if (controlType == DS4StateFieldMapping.ControlType.Button) { - altTabDone = false; - InputMethods.performKeyPress(18); + fieldMap.buttons[controlNum] = false; } - else + else if (controlType == DS4StateFieldMapping.ControlType.AxisDir) { - altTabNow = DateTime.UtcNow; - if (altTabNow >= oldAltTabNow + TimeSpan.FromMilliseconds(wait)) - { - oldAltTabNow = altTabNow; - InputMethods.performKeyPress(9); - InputMethods.performKeyRelease(9); - } + fieldMap.axisdirs[controlNum] = 128; + int controlRelation = (controlNum % 2 == 0 ? controlNum - 1 : controlNum + 1); + fieldMap.axisdirs[controlRelation] = 128; + } + else if (controlType == DS4StateFieldMapping.ControlType.Trigger) + { + fieldMap.triggers[controlNum] = 0; + } + else if (controlType == DS4StateFieldMapping.ControlType.Touch) + { + fieldMap.buttons[controlNum] = false; } } - private static void AltTabSwappingRelease() + + // SA steering wheel emulation mapping + + private const int C_WHEEL_ANGLE_PRECISION = 10; // Precision of SA angle in 1/10 of degrees + + private static readonly DS4Color calibrationColor_0 = new DS4Color { red = 0xA0, green = 0x00, blue = 0x00 }; + private static readonly DS4Color calibrationColor_1 = new DS4Color { red = 0xFF, green = 0xFF, blue = 0x00 }; + private static readonly DS4Color calibrationColor_2 = new DS4Color { red = 0x00, green = 0x50, blue = 0x50 }; + private static readonly DS4Color calibrationColor_3 = new DS4Color { red = 0x00, green = 0xC0, blue = 0x00 }; + + private static DateTime latestDebugMsgTime; + private static string latestDebugData; + private static void LogToGuiSACalibrationDebugMsg(string data, bool forceOutput = false) { - if (altTabNow < DateTime.UtcNow - TimeSpan.FromMilliseconds(10)) //in case multiple controls are mapped to alt+tab + // Print debug calibration log messages only once per 2 secs to avoid flooding the log receiver + DateTime curTime = DateTime.Now; + if (forceOutput || ((TimeSpan)(curTime - latestDebugMsgTime)).TotalSeconds > 2) { - altTabDone = true; - InputMethods.performKeyRelease(9); - InputMethods.performKeyRelease(18); - altTabNow = DateTime.UtcNow; - oldAltTabNow = DateTime.UtcNow - TimeSpan.FromDays(1); + latestDebugMsgTime = curTime; + if (data != latestDebugData) + { + AppLogger.LogToGui(data, false); + latestDebugData = data; + } } } - private static void getMouseWheelMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool down) + // Return number of bits set in a value + protected static int CountNumOfSetBits(int bitValue) { - DateTime now = DateTime.UtcNow; - if (now >= oldnow + TimeSpan.FromMilliseconds(10) && !pressagain) + int count = 0; + while (bitValue != 0) { - oldnow = now; - InputMethods.MouseWheel((int)(getByteMapping(device, control, cState, eState, tp) / 51f * (down ? -1 : 1)), 0); + count++; + bitValue &= (bitValue - 1); } + return count; } - private static int getMouseMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, int mnum) + // Calculate and return the angle of the controller as -180...0...+180 value. + private static Int32 CalculateControllerAngle(int gyroAccelX, int gyroAccelZ, DS4Device controller) { - int controlnum = DS4ControltoInt(control); - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - int deadzoneL = 3; - int deadzoneR = 3; - if (LSDeadzone[device] >= 3) - deadzoneL = 0; - if (RSDeadzone[device] >= 3) - deadzoneR = 0; - double value = 0; - int speed = ButtonMouseSensitivity[device] + 15; - double root = 1.002; - double divide = 10000d; - //DateTime now = mousenow[mnum]; - switch (control) + Int32 result; + + if (gyroAccelX == controller.wheelCenterPoint.X && Math.Abs(gyroAccelZ - controller.wheelCenterPoint.Y) <= 1) { - case DS4Controls.LXNeg: - if (cState.LX - 127.5f < -deadzoneL) - value = -(cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LXPos: - if (cState.LX - 127.5f > deadzoneL) - value = (cState.LX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXNeg: - if (cState.RX - 127.5f < -deadzoneR) - value = -(cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.RXPos: - if (cState.RX - 127.5f > deadzoneR) - value = (cState.RX - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYNeg: - if (cState.LY - 127.5f < -deadzoneL) - value = -(cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.LYPos: - if (cState.LY - 127.5f > deadzoneL) - value = (cState.LY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYNeg: - if (cState.RY - 127.5f < -deadzoneR) - value = -(cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.RYPos: - if (cState.RY - 127.5f > deadzoneR) - value = (cState.RY - 127.5f) / 2550d * speed; - break; - case DS4Controls.Share: value = (cState.Share ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Options: value = (cState.Options ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L1: value = (cState.L1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R1: value = (cState.R1 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L3: value = (cState.L3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.R3: value = (cState.R3 ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadUp: value = (cState.DpadUp ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadDown: value = (cState.DpadDown ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadLeft: value = (cState.DpadLeft ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.DpadRight: value = (cState.DpadRight ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.PS: value = (cState.PS ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Cross: value = (cState.Cross ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Square: value = (cState.Square ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Triangle: value = (cState.Triangle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.Circle: value = (cState.Circle ? Math.Pow(root + speed / divide, 100) - 1 : 0); break; - case DS4Controls.L2: value = Math.Pow(root + speed / divide, cState.L2 / 2d) - 1; break; - case DS4Controls.R2: value = Math.Pow(root + speed / divide, cState.R2 / 2d) - 1; break; - case DS4Controls.GyroXPos: return (byte)(eState.GyroX > SXD * 10 ? - Math.Pow(root + speed / divide, eState.GyroX) : 0); - case DS4Controls.GyroXNeg: return (byte)(eState.GyroX < -SXD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroX) : 0); - case DS4Controls.GyroZPos: return (byte)(eState.GyroZ > SZD * 10 ? - Math.Pow(root + speed / divide, eState.GyroZ) : 0); - case DS4Controls.GyroZNeg: return (byte)(eState.GyroZ < -SZD * 10 ? - Math.Pow(root + speed / divide, -eState.GyroZ) : 0); - } - bool LXChanged = (Math.Abs(127 - cState.LX) < deadzoneL); - bool LYChanged = (Math.Abs(127 - cState.LY) < deadzoneL); - bool RXChanged = (Math.Abs(127 - cState.RX) < deadzoneR); - bool RYChanged = (Math.Abs(127 - cState.RY) < deadzoneR); - bool contains = (control.ToString().Contains("LX") || - control.ToString().Contains("LY") || - control.ToString().Contains("RX") || - control.ToString().Contains("RY")); - if (MouseAccel[device]) + // When the current gyro position is "close enough" the wheel center point then no need to go through the hassle of calculating an angle + result = 0; + } + else { - if (value > 0) + // Calculate two vectors based on "circle center" (ie. circle represents the 360 degree wheel turn and wheelCenterPoint and currentPosition vectors both start from circle center). + // To improve accuracy both left and right turns use a decicated calibration "circle" because DS4 gyro and DoItYourselfWheelRig may return slightly different SA sensor values depending on the tilt direction (well, only one or two degree difference so nothing major). + Point vectorAB; + Point vectorCD; + + if (gyroAccelX >= controller.wheelCenterPoint.X) { - mcounter = 34; - mouseaccel++; + // "DS4 gyro wheel" tilted to right + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointRight.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointRight.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointRight.X, gyroAccelZ - controller.wheelCircleCenterPointRight.Y); } - if (mouseaccel == prevmouseaccel) + else { - mcounter--; + // "DS4 gyro wheel" tilted to left + vectorAB = new Point(controller.wheelCenterPoint.X - controller.wheelCircleCenterPointLeft.X, controller.wheelCenterPoint.Y - controller.wheelCircleCenterPointLeft.Y); + vectorCD = new Point(gyroAccelX - controller.wheelCircleCenterPointLeft.X, gyroAccelZ - controller.wheelCircleCenterPointLeft.Y); } - if (mcounter <= 0) + + // Calculate dot product and magnitude of vectors (center vector and the current tilt vector) + double dotProduct = vectorAB.X * vectorCD.X + vectorAB.Y * vectorCD.Y; + double magAB = Math.Sqrt(vectorAB.X * vectorAB.X + vectorAB.Y * vectorAB.Y); + double magCD = Math.Sqrt(vectorCD.X * vectorCD.X + vectorCD.Y * vectorCD.Y); + + // Calculate angle between vectors and convert radian to degrees + if (magAB == 0 || magCD == 0) { - mouseaccel = 0; - mcounter = 34; + result = 0; } - value *= 1 + (double)Math.Min(20000, (mouseaccel)) / 10000d; - prevmouseaccel = mouseaccel; - } - int intValue; - if (mnum > 1) - { - if ((value > 0.0 && horizontalRemainder > 0.0) || (value < 0.0 && horizontalRemainder < 0.0)) - value += horizontalRemainder; - intValue = (int)value; - horizontalRemainder = value - intValue; - } - else - { - if ((value > 0.0 && verticalRemainder > 0.0) || (value < 0.0 && verticalRemainder < 0.0)) - value += verticalRemainder; - intValue = (int)value; - verticalRemainder = value - intValue; + else + { + double angle = Math.Acos(dotProduct / (magAB * magCD)); + result = Convert.ToInt32(Global.Clamp( + -180.0 * C_WHEEL_ANGLE_PRECISION, + Math.Round((angle * (180.0 / Math.PI)), 1) * C_WHEEL_ANGLE_PRECISION, + 180.0 * C_WHEEL_ANGLE_PRECISION) + ); + } + + // Left turn is -180..0 and right turn 0..180 degrees + if (gyroAccelX < controller.wheelCenterPoint.X) result = -result; } - return intValue; + + return result; } - public static bool compare(byte b1, byte b2) + // Calibrate sixaxis steering wheel emulation. Use DS4Windows configuration screen to start a calibration or press a special action key (if defined) + private static void SAWheelEmulationCalibration(int device, DS4StateExposed exposedState, ControlService ctrl, DS4State currentDeviceState, DS4Device controller) { - if (Math.Abs(b1 - b2) > 10) + int gyroAccelX, gyroAccelZ; + int result; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // State 0=Normal mode (ie. calibration process is not running), 1=Activating calibration, 2=Calibration process running, 3=Completing calibration, 4=Cancelling calibration + if (controller.WheelRecalibrateActiveState == 1) { - return false; + AppLogger.LogToGui($"Controller {1 + device} activated re-calibration of SA steering wheel emulation", false); + + controller.WheelRecalibrateActiveState = 2; + + controller.wheelPrevPhysicalAngle = 0; + controller.wheelPrevFullAngle = 0; + controller.wheelFullTurnCount = 0; + + // Clear existing calibration value and use current position as "center" point. + // This initial center value may be off-center because of shaking the controller while button was pressed. The value will be overriden with correct value once controller is stabilized and hold still few secs at the center point + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + controller.wheel90DegPointRight.X = gyroAccelX + 20; + controller.wheel90DegPointLeft.X = gyroAccelX - 20; + + // Clear bitmask for calibration points. All three calibration points need to be set before re-calibration process is valid + controller.wheelCalibratedAxisBitmask = DS4Device.WheelCalibrationPoint.None; + + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); } - return true; - } + else if (controller.WheelRecalibrateActiveState == 3) + { + AppLogger.LogToGui($"Controller {1 + device} completed the calibration of SA steering wheel emulation. center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y})", false); - public static byte getByteMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? 255 : 0); - case DS4Controls.Options: return (byte)(cState.Options ? 255 : 0); - case DS4Controls.L1: return (byte)(cState.L1 ? 255 : 0); - case DS4Controls.R1: return (byte)(cState.R1 ? 255 : 0); - case DS4Controls.L3: return (byte)(cState.L3 ? 255 : 0); - case DS4Controls.R3: return (byte)(cState.R3 ? 255 : 0); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? 255 : 0); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? 255 : 0); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? 255 : 0); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? 255 : 0); - case DS4Controls.PS: return (byte)(cState.PS ? 255 : 0); - case DS4Controls.Cross: return (byte)(cState.Cross ? 255 : 0); - case DS4Controls.Square: return (byte)(cState.Square ? 255 : 0); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? 255 : 0); - case DS4Controls.Circle: return (byte)(cState.Circle ? 255 : 0); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? 255 : 0); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? 255 : 0); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? 255 : 0); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? 255 : 0); - case DS4Controls.LXNeg: return (byte)(cState.LX - 127.5f > 0 ? 0 : -(cState.LX - 127.5f) * 2); - case DS4Controls.LYNeg: return (byte)(cState.LY - 127.5f > 0 ? 0 : -(cState.LY - 127.5f) * 2); - case DS4Controls.RXNeg: return (byte)(cState.RX - 127.5f > 0 ? 0 : -(cState.RX - 127.5f) * 2); - case DS4Controls.RYNeg: return (byte)(cState.RY - 127.5f > 0 ? 0 : -(cState.RY - 127.5f) * 2); - case DS4Controls.LXPos: return (byte)(cState.LX - 127.5f < 0 ? 0 : (cState.LX - 127.5f) * 2); - case DS4Controls.LYPos: return (byte)(cState.LY - 127.5f < 0 ? 0 : (cState.LY - 127.5f) * 2); - case DS4Controls.RXPos: return (byte)(cState.RX - 127.5f < 0 ? 0 : (cState.RX - 127.5f) * 2); - case DS4Controls.RYPos: return (byte)(cState.RY - 127.5f < 0 ? 0 : (cState.RY - 127.5f) * 2); - case DS4Controls.L2: return cState.L2; - case DS4Controls.R2: return cState.R2; - case DS4Controls.GyroXPos: return (byte)(!sOff && SXSens[device] * eState.GyroX > SXD * 10 ? Math.Min(255, SXSens[device] * eState.GyroX * 2) : 0); - case DS4Controls.GyroXNeg: return (byte)(!sOff && SXSens[device] * eState.GyroX < -SXD * 10 ? Math.Min(255, SXSens[device] * -eState.GyroX * 2) : 0); - case DS4Controls.GyroZPos: return (byte)(!sOff && SZSens[device] * eState.GyroZ > SZD * 10 ? Math.Min(255, SZSens[device] * eState.GyroZ * 2) : 0); - case DS4Controls.GyroZNeg: return (byte)(!sOff && SZSens[device] * eState.GyroZ < -SZD * 10 ? Math.Min(255, SZSens[device] * -eState.GyroZ * 2) : 0); - case DS4Controls.SwipeUp: return (byte)(tp != null ? tp.swipeUpB : 0); - case DS4Controls.SwipeDown: return (byte)(tp != null ? tp.swipeDownB: 0); - case DS4Controls.SwipeLeft: return (byte)(tp != null ? tp.swipeLeftB: 0); - case DS4Controls.SwipeRight: return (byte)(tp != null ? tp.swipeRightB : 0); - } - return 0; - } + // If any of the calibration points (center, left 90deg, right 90deg) are missing then reset back to default calibration values + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.All) == DS4Device.WheelCalibrationPoint.All)) + Global.SaveControllerConfigs(controller); + else + controller.wheelCenterPoint.X = controller.wheelCenterPoint.Y = 0; - public static bool getBoolMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp) - { - bool sOff = UseSAforMouse[device]; - switch (control) - { - case DS4Controls.Share: return cState.Share; - case DS4Controls.Options: return cState.Options; - case DS4Controls.L1: return cState.L1; - case DS4Controls.R1: return cState.R1; - case DS4Controls.L3: return cState.L3; - case DS4Controls.R3: return cState.R3; - case DS4Controls.DpadUp: return cState.DpadUp; - case DS4Controls.DpadDown: return cState.DpadDown; - case DS4Controls.DpadLeft: return cState.DpadLeft; - case DS4Controls.DpadRight: return cState.DpadRight; - case DS4Controls.PS: return cState.PS; - case DS4Controls.Cross: return cState.Cross; - case DS4Controls.Square: return cState.Square; - case DS4Controls.Triangle: return cState.Triangle; - case DS4Controls.Circle: return cState.Circle; - case DS4Controls.TouchLeft: return (tp != null ? tp.leftDown : false); - case DS4Controls.TouchRight: return (tp != null ? tp.rightDown : false); - case DS4Controls.TouchMulti: return (tp != null ? tp.multiDown : false); - case DS4Controls.TouchUpper: return (tp != null ? tp.upperDown : false); - case DS4Controls.LXNeg: return cState.LX < 127 - 55; - case DS4Controls.LYNeg: return cState.LY < 127 - 55; - case DS4Controls.RXNeg: return cState.RX < 127 - 55; - case DS4Controls.RYNeg: return cState.RY < 127 - 55; - case DS4Controls.LXPos: return cState.LX > 127 + 55; - case DS4Controls.LYPos: return cState.LY > 127 + 55; - case DS4Controls.RXPos: return cState.RX > 127 + 55; - case DS4Controls.RYPos: return cState.RY > 127 + 55; - case DS4Controls.L2: return cState.L2 > 100; - case DS4Controls.R2: return cState.R2 > 100; - case DS4Controls.GyroXPos: return !sOff ? SXSens[device] * eState.GyroX > 67 : false; - case DS4Controls.GyroXNeg: return !sOff ? SXSens[device] * eState.GyroX < -67 : false; - case DS4Controls.GyroZPos: return !sOff ? SZSens[device] * eState.GyroZ > 67 : false; - case DS4Controls.GyroZNeg: return !sOff ? SZSens[device] * eState.GyroZ < -67 : false; - case DS4Controls.SwipeUp: return (tp != null && tp.swipeUp); - case DS4Controls.SwipeDown: return (tp != null && tp.swipeDown); - case DS4Controls.SwipeLeft: return (tp != null && tp.swipeLeft); - case DS4Controls.SwipeRight: return (tp != null && tp.swipeRight); - } - return false; - } + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + else if (controller.WheelRecalibrateActiveState == 4) + { + AppLogger.LogToGui($"Controller {1 + device} cancelled the calibration of SA steering wheel emulation.", false); - public static byte getXYAxisMapping(int device, DS4Controls control, DS4State cState, DS4StateExposed eState, Mouse tp, bool alt = false) - { - byte trueVal = 0; - byte falseVal = 127; - double SXD = SXDeadzone[device]; - double SZD = SZDeadzone[device]; - bool sOff = UseSAforMouse[device]; - if (alt) - trueVal = 255; - switch (control) - { - case DS4Controls.Share: return (byte)(cState.Share ? trueVal : falseVal); - case DS4Controls.Options: return (byte)(cState.Options ? trueVal : falseVal); - case DS4Controls.L1: return (byte)(cState.L1 ? trueVal : falseVal); - case DS4Controls.R1: return (byte)(cState.R1 ? trueVal : falseVal); - case DS4Controls.L3: return (byte)(cState.L3 ? trueVal : falseVal); - case DS4Controls.R3: return (byte)(cState.R3 ? trueVal : falseVal); - case DS4Controls.DpadUp: return (byte)(cState.DpadUp ? trueVal : falseVal); - case DS4Controls.DpadDown: return (byte)(cState.DpadDown ? trueVal : falseVal); - case DS4Controls.DpadLeft: return (byte)(cState.DpadLeft ? trueVal : falseVal); - case DS4Controls.DpadRight: return (byte)(cState.DpadRight ? trueVal : falseVal); - case DS4Controls.PS: return (byte)(cState.PS ? trueVal : falseVal); - case DS4Controls.Cross: return (byte)(cState.Cross ? trueVal : falseVal); - case DS4Controls.Square: return (byte)(cState.Square ? trueVal : falseVal); - case DS4Controls.Triangle: return (byte)(cState.Triangle ? trueVal : falseVal); - case DS4Controls.Circle: return (byte)(cState.Circle ? trueVal : falseVal); - case DS4Controls.TouchLeft: return (byte)(tp != null && tp.leftDown ? trueVal : falseVal); - case DS4Controls.TouchRight: return (byte)(tp != null && tp.rightDown ? trueVal : falseVal); - case DS4Controls.TouchMulti: return (byte)(tp != null && tp.multiDown ? trueVal : falseVal); - case DS4Controls.TouchUpper: return (byte)(tp != null && tp.upperDown ? trueVal : falseVal); - case DS4Controls.L2: if (alt) return (byte)(127.5f + cState.L2 / 2f); else return (byte)(127.5f - cState.L2 / 2f); - case DS4Controls.R2: if (alt) return (byte)(127.5f + cState.R2 / 2f); else return (byte)(127.5f - cState.R2 / 2f); - case DS4Controls.SwipeUp: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeUpB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeUpB / 2f : 0); - case DS4Controls.SwipeDown: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeDownB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeDownB / 2f : 0); - case DS4Controls.SwipeLeft: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeLeftB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeLeftB / 2f : 0); - case DS4Controls.SwipeRight: if (alt) return (byte)(tp != null ? 127.5f + tp.swipeRightB / 2f : 0); else return (byte)(tp != null ? 127.5f - tp.swipeRightB / 2f : 0); - case DS4Controls.GyroXPos: if (!sOff && eState.GyroX > SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * eState.GyroX); - else return falseVal; - case DS4Controls.GyroXNeg: if (!sOff && eState.GyroX < -SXD * 10) - if (alt) return (byte)Math.Min(255, 127 + SXSens[device] * -eState.GyroX); else return (byte)Math.Max(0, 127 - SXSens[device] * -eState.GyroX); - else return falseVal; - case DS4Controls.GyroZPos: if (!sOff && eState.GyroZ > SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * eState.GyroZ); - else return falseVal; - case DS4Controls.GyroZNeg: if (!sOff && eState.GyroZ < -SZD * 10) - if (alt) return (byte)Math.Min(255, 127 + SZSens[device] * -eState.GyroZ); else return (byte)Math.Max(0, 127 - SZSens[device] * -eState.GyroZ); - else return falseVal; - } - if (!alt) + controller.WheelRecalibrateActiveState = 0; + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + if (controller.WheelRecalibrateActiveState > 0) { - switch (control) + // Cross "X" key pressed. Set calibration point when the key is released and controller hold steady for a few seconds + if (currentDeviceState.Cross == true) controller.wheelPrevRecalibrateTime = DateTime.Now; + + // Make sure controller is hold steady (velocity of gyro axis) to avoid misaligments and set calibration few secs after the "X" key was released + if (Math.Abs(currentDeviceState.Motion.angVelPitch) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelYaw) < 0.5 && Math.Abs(currentDeviceState.Motion.angVelRoll) < 0.5 + && ((TimeSpan)(DateTime.Now - controller.wheelPrevRecalibrateTime)).TotalSeconds > 1) { - case DS4Controls.LXNeg: return cState.LX; - case DS4Controls.LYNeg: return cState.LY; - case DS4Controls.RXNeg: return cState.RX; - case DS4Controls.RYNeg: return cState.RY; - case DS4Controls.LXPos: return (byte)(255 - cState.LX); - case DS4Controls.LYPos: return (byte)(255 - cState.LY); - case DS4Controls.RXPos: return (byte)(255 - cState.RX); - case DS4Controls.RYPos: return (byte)(255 - cState.RY); + controller.wheelPrevRecalibrateTime = new DateTime(2500, 1, 1); + + if (controller.wheelCalibratedAxisBitmask == DS4Device.WheelCalibrationPoint.None) + { + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Center; + } + else if (controller.wheel90DegPointRight.X < gyroAccelX) + { + controller.wheel90DegPointRight.X = gyroAccelX; + controller.wheel90DegPointRight.Y = gyroAccelZ; + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Right90; + } + else if (controller.wheel90DegPointLeft.X > gyroAccelX) + { + controller.wheel90DegPointLeft.X = gyroAccelX; + controller.wheel90DegPointLeft.Y = gyroAccelZ; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + controller.wheelCalibratedAxisBitmask |= DS4Device.WheelCalibrationPoint.Left90; + } } + + // Show lightbar color feedback how the calibration process is proceeding. + // red / yellow / blue / green = No calibration anchors/one anchor/two anchors/all three anchors calibrated when color turns to green (center, 90DegLeft, 90DegRight). + int bitsSet = CountNumOfSetBits((int)controller.wheelCalibratedAxisBitmask); + if (bitsSet >= 3) DS4LightBar.forcedColor[device] = calibrationColor_3; + else if (bitsSet == 2) DS4LightBar.forcedColor[device] = calibrationColor_2; + else if (bitsSet == 1) DS4LightBar.forcedColor[device] = calibrationColor_1; + else DS4LightBar.forcedColor[device] = calibrationColor_0; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Force lightbar flashing when controller is currently at calibration point (user can verify the calibration before accepting it by looking at flashing lightbar) + if (((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Center) != 0 && Math.Abs(result) <= 1 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && result <= -89 * C_WHEEL_ANGLE_PRECISION && result >= -91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Right90) != 0 && result >= 89 * C_WHEEL_ANGLE_PRECISION && result <= 91 * C_WHEEL_ANGLE_PRECISION) + || ((controller.wheelCalibratedAxisBitmask & DS4Device.WheelCalibrationPoint.Left90) != 0 && Math.Abs(result) >= 179 * C_WHEEL_ANGLE_PRECISION)) + DS4LightBar.forcedFlash[device] = 2; + else + DS4LightBar.forcedFlash[device] = 0; + + DS4LightBar.forcelight[device] = true; + + LogToGuiSACalibrationDebugMsg($"Calibration values ({gyroAccelX}, {gyroAccelZ}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)}\n"); } else { - switch (control) - { - case DS4Controls.LXNeg: return (byte)(255 - cState.LX); - case DS4Controls.LYNeg: return (byte)(255 - cState.LY); - case DS4Controls.RXNeg: return (byte)(255 - cState.RX); - case DS4Controls.RYNeg: return (byte)(255 - cState.RY); - case DS4Controls.LXPos: return cState.LX; - case DS4Controls.LYPos: return cState.LY; - case DS4Controls.RXPos: return cState.RX; - case DS4Controls.RYPos: return cState.RY; - } + // Re-calibration completed or cancelled. Set lightbar color back to normal color + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = Global.getMainColor(device); + DS4LightBar.forcelight[device] = false; + DS4LightBar.updateLightBar(controller, device); } - return 0; } - //Returns false for any bool, - //if control is one of the xy axis returns 127 - //if its a trigger returns 0 - public static void resetToDefaultValue(DS4Controls control, DS4State cState) + protected static Int32 Scale360degreeGyroAxis(int device, DS4StateExposed exposedState, ControlService ctrl) { - switch (control) - { - case DS4Controls.Share: cState.Share = false; break; - case DS4Controls.Options: cState.Options = false; break; - case DS4Controls.L1: cState.L1 = false; break; - case DS4Controls.R1: cState.R1 = false; break; - case DS4Controls.L3: cState.L3 = false; break; - case DS4Controls.R3: cState.R3 = false; break; - case DS4Controls.DpadUp: cState.DpadUp = false; break; - case DS4Controls.DpadDown: cState.DpadDown = false; break; - case DS4Controls.DpadLeft: cState.DpadLeft = false; break; - case DS4Controls.DpadRight: cState.DpadRight = false; break; - case DS4Controls.PS: cState.PS = false; break; - case DS4Controls.Cross: cState.Cross = false; break; - case DS4Controls.Square: cState.Square = false; break; - case DS4Controls.Triangle: cState.Triangle = false; break; - case DS4Controls.Circle: cState.Circle = false; break; - case DS4Controls.LXNeg: cState.LX = 127; break; - case DS4Controls.LYNeg: cState.LY = 127; break; - case DS4Controls.RXNeg: cState.RX = 127; break; - case DS4Controls.RYNeg: cState.RY = 127; break; - case DS4Controls.LXPos: cState.LX = 127; break; - case DS4Controls.LYPos: cState.LY = 127; break; - case DS4Controls.RXPos: cState.RX = 127; break; - case DS4Controls.RYPos: cState.RY = 127; break; - case DS4Controls.L2: cState.L2 = 0; break; - case DS4Controls.R2: cState.R2 = 0; break; + unchecked + { + DS4Device controller; + DS4State currentDeviceState; + + int gyroAccelX, gyroAccelZ; + int result; + + controller = ctrl.DS4Controllers[device]; + if (controller == null) return 0; + + currentDeviceState = controller.getCurrentStateRef(); + + // If calibration is active then do the calibration process instead of the normal "angle calculation" + if (controller.WheelRecalibrateActiveState > 0) + { + SAWheelEmulationCalibration(device, exposedState, ctrl, currentDeviceState, controller); + + // Return center wheel position while SA wheel emuation is being calibrated + return 0; + } + + // Do nothing if connection is active but the actual DS4 controller is still missing or not yet synchronized + if (!controller.Synced) + return 0; + + gyroAccelX = exposedState.getAccelX(); + gyroAccelZ = exposedState.getAccelZ(); + + // If calibration values are missing then use "educated guesses" about good starting values + if (controller.wheelCenterPoint.IsEmpty) + { + if (!Global.LoadControllerConfigs(controller)) + { + AppLogger.LogToGui($"Controller {1 + device} sixaxis steering wheel calibration data missing. It is recommended to run steering wheel calibration process by pressing SASteeringWheelEmulationCalibration special action key. Using estimated values until the controller is calibrated at least once.", false); + + // Use current controller position as "center point". Assume DS4Windows was started while controller was hold in center position (yes, dangerous assumption but can't do much until controller is calibrated) + controller.wheelCenterPoint.X = gyroAccelX; + controller.wheelCenterPoint.Y = gyroAccelZ; + + controller.wheel90DegPointRight.X = controller.wheelCenterPoint.X + 113; + controller.wheel90DegPointRight.Y = controller.wheelCenterPoint.Y + 110; + + controller.wheel90DegPointLeft.X = controller.wheelCenterPoint.X - 127; + controller.wheel90DegPointLeft.Y = controller.wheel90DegPointRight.Y; + } + + controller.wheelCircleCenterPointRight.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointRight.Y = controller.wheel90DegPointRight.Y; + controller.wheelCircleCenterPointLeft.X = controller.wheelCenterPoint.X; + controller.wheelCircleCenterPointLeft.Y = controller.wheel90DegPointLeft.Y; + + AppLogger.LogToGui($"Controller {1 + device} steering wheel emulation calibration values. Center=({controller.wheelCenterPoint.X}, {controller.wheelCenterPoint.Y}) 90L=({controller.wheel90DegPointLeft.X}, {controller.wheel90DegPointLeft.Y}) 90R=({controller.wheel90DegPointRight.X}, {controller.wheel90DegPointRight.Y}) Range={Global.GetSASteeringWheelEmulationRange(device)}", false); + controller.wheelPrevRecalibrateTime = DateTime.Now; + } + + + int maxRangeRight = Global.GetSASteeringWheelEmulationRange(device) / 2 * C_WHEEL_ANGLE_PRECISION; + int maxRangeLeft = -maxRangeRight; + + result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); + + // Apply deadzone (SA X-deadzone value). This code assumes that 20deg is the max deadzone anyone ever might wanna use (in practice effective deadzone + // is probably just few degrees by using SXDeadZone values 0.01...0.05) + double sxDead = getSXDeadzone(device); + if (sxDead > 0) + { + int sxDeadInt = Convert.ToInt32(20.0 * C_WHEEL_ANGLE_PRECISION * sxDead); + if (Math.Abs(result) <= sxDeadInt) + { + result = 0; + } + else + { + // Smooth steering angle based on deadzone range instead of just clipping the deadzone gap + result -= (result < 0 ? -sxDeadInt : sxDeadInt); + } + } + + // If wrapped around from +180 to -180 side (or vice versa) then SA steering wheel keeps on turning beyond 360 degrees (if range is >360). + // Keep track of how many times the steering wheel has been turned beyond the full 360 circle and clip the result to max range. + int wheelFullTurnCount = controller.wheelFullTurnCount; + if (controller.wheelPrevPhysicalAngle < 0 && result > 0) + { + if ((result - controller.wheelPrevPhysicalAngle) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount--; + else + result = maxRangeLeft; + } + } + else if (controller.wheelPrevPhysicalAngle > 0 && result < 0) + { + if ((controller.wheelPrevPhysicalAngle - result) > 180 * C_WHEEL_ANGLE_PRECISION) + { + if (maxRangeRight > 360/2 * C_WHEEL_ANGLE_PRECISION) + wheelFullTurnCount++; + else + result = maxRangeRight; + } + } + controller.wheelPrevPhysicalAngle = result; + + if (wheelFullTurnCount != 0) + { + // Adjust value of result (steering wheel angle) based on num of full 360 turn counts + result += (wheelFullTurnCount * 180 * C_WHEEL_ANGLE_PRECISION * 2); + } + + // If the new angle is more than 180 degrees further away then this is probably bogus value (controller shaking too much and gyro and velocity sensors went crazy). + // Accept the new angle only when the new angle is within a "stability threshold", otherwise use the previous full angle value and wait for controller to be stabilized. + if (Math.Abs(result - controller.wheelPrevFullAngle) <= 180 * C_WHEEL_ANGLE_PRECISION) + { + controller.wheelPrevFullAngle = result; + controller.wheelFullTurnCount = wheelFullTurnCount; + } + else + { + result = controller.wheelPrevFullAngle; + } + + result = Mapping.ClampInt(maxRangeLeft, result, maxRangeRight); + + // Debug log output of SA sensor values + //LogToGuiSACalibrationDebugMsg($"DBG gyro=({gyroAccelX}, {gyroAccelZ}) output=({exposedState.OutputAccelX}, {exposedState.OutputAccelZ}) PitRolYaw=({currentDeviceState.Motion.gyroPitch}, {currentDeviceState.Motion.gyroRoll}, {currentDeviceState.Motion.gyroYaw}) VelPitRolYaw=({currentDeviceState.Motion.angVelPitch}, {currentDeviceState.Motion.angVelRoll}, {currentDeviceState.Motion.angVelYaw}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)} fullTurns={controller.wheelFullTurnCount}", false); + + // Apply anti-deadzone (SA X-antideadzone value) + double sxAntiDead = getSXAntiDeadzone(device); + + int outputAxisMax, outputAxisMin, outputAxisZero; + if ( Global.OutContType[device] == OutContType.DS4 ) + { + // DS4 analog stick axis supports only 0...255 output value range (not the best one for steering wheel usage) + outputAxisMax = 255; + outputAxisMin = 0; + outputAxisZero = 128; + } + else + { + // x360 (xinput) analog stick axis supports -32768...32767 output value range (more than enough for steering wheel usage) + outputAxisMax = 32767; + outputAxisMin = -32768; + outputAxisZero = 0; + } + + switch (Global.GetSASteeringWheelEmulationAxis(device)) + { + case SASteeringWheelEmulationAxisType.LX: + case SASteeringWheelEmulationAxisType.LY: + case SASteeringWheelEmulationAxisType.RX: + case SASteeringWheelEmulationAxisType.RY: + // DS4 thumbstick axis output (-32768..32767 raw value range) + //return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768); + if (result == 0) return outputAxisZero; + + if (sxAntiDead > 0) + { + sxAntiDead *= (outputAxisMax - outputAxisZero); + if (result < 0) return (((result - maxRangeLeft) * (outputAxisZero - Convert.ToInt32(sxAntiDead) - (outputAxisMin))) / (0 - maxRangeLeft)) + (outputAxisMin); + else return (((result - 0) * (outputAxisMax - (outputAxisZero + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (outputAxisZero + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (outputAxisMax - (outputAxisMin))) / (maxRangeRight - maxRangeLeft)) + (outputAxisMin); + } + + case SASteeringWheelEmulationAxisType.L2R2: + // DS4 Trigger axis output. L2+R2 triggers share the same axis in x360 xInput/DInput controller, + // so L2+R2 steering output supports only 360 turn range (-255..255 raw value range in the shared trigger axis) + if (result == 0) return 0; + + result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION))); + if (result < 0) result = -181 - result; + + if (sxAntiDead > 0) + { + sxAntiDead *= 255; + if (result < 0) return (((result - (-180)) * (-Convert.ToInt32(sxAntiDead) - (-255))) / (0 - (-180))) + (-255); + else return (((result - (0)) * (255 - (Convert.ToInt32(sxAntiDead)))) / (180 - (0))) + (Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255); + } + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2X: + case SASteeringWheelEmulationAxisType.VJoy2Y: + case SASteeringWheelEmulationAxisType.VJoy2Z: + // SASteeringWheelEmulationAxisType.VJoy1X/VJoy1Y/VJoy1Z/VJoy2X/VJoy2Y/VJoy2Z VJoy axis output (0..32767 raw value range by default) + if (result == 0) return 16384; + + if (sxAntiDead > 0) + { + sxAntiDead *= 16384; + if (result < 0) return (((result - maxRangeLeft) * (16384 - Convert.ToInt32(sxAntiDead) - (-0))) / (0 - maxRangeLeft)) + (-0); + else return (((result - 0) * (32767 - (16384 + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (16384 + Convert.ToInt32(sxAntiDead)); + } + else + { + return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0); + } + + default: + // Should never come here, but C# case statement syntax requires DEFAULT handler + return 0; + } } } + } } diff --git a/DS4Windows/DS4Control/Mouse.cs b/DS4Windows/DS4Control/Mouse.cs index bd90856253..f939d11587 100644 --- a/DS4Windows/DS4Control/Mouse.cs +++ b/DS4Windows/DS4Control/Mouse.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Runtime.InteropServices; +using System.Drawing; namespace DS4Windows { @@ -17,12 +14,35 @@ public class Mouse : ITouchpadBehaviour private readonly MouseWheel wheel; private bool tappedOnce = false, secondtouchbegin = false; public bool swipeLeft, swipeRight, swipeUp, swipeDown; + public bool priorSwipeLeft, priorSwipeRight, priorSwipeUp, priorSwipeDown; public byte swipeLeftB, swipeRightB, swipeUpB, swipeDownB, swipedB; + public byte priorSwipeLeftB, priorSwipeRightB, priorSwipeUpB, priorSwipeDownB, priorSwipedB; public bool slideleft, slideright; + public bool priorSlideLeft, priorSlideright; // touch area stuff public bool leftDown, rightDown, upperDown, multiDown; + public bool priorLeftDown, priorRightDown, priorUpperDown, priorMultiDown; protected DS4Controls pushed = DS4Controls.None; protected Mapping.Click clicked = Mapping.Click.None; + public int CursorGyroDead { get => cursor.GyroCursorDeadZone; set => cursor.GyroCursorDeadZone = value; } + + internal const int TRACKBALL_INIT_FICTION = 10; + internal const int TRACKBALL_MASS = 45; + internal const double TRACKBALL_RADIUS = 0.0245; + + private double TRACKBALL_INERTIA = 2.0 * (TRACKBALL_MASS * TRACKBALL_RADIUS * TRACKBALL_RADIUS) / 5.0; + private double TRACKBALL_SCALE = 0.004; + private const int TRACKBALL_BUFFER_LEN = 8; + private double[] trackballXBuffer = new double[TRACKBALL_BUFFER_LEN]; + private double[] trackballYBuffer = new double[TRACKBALL_BUFFER_LEN]; + private int trackballBufferTail = 0; + private int trackballBufferHead = 0; + private double trackballAccel = 0.0; + private double trackballXVel = 0.0; + private double trackballYVel = 0.0; + private bool trackballActive = false; + private double trackballDXRemain = 0.0; + private double trackballDYRemain = 0.0; public Mouse(int deviceID, DS4Device d) { @@ -30,25 +50,279 @@ public Mouse(int deviceID, DS4Device d) dev = d; cursor = new MouseCursor(deviceNum); wheel = new MouseWheel(deviceNum); + trackballAccel = TRACKBALL_RADIUS * TRACKBALL_INIT_FICTION / TRACKBALL_INERTIA; + firstTouch = new Touch(0, 0, 0, null); + } + + public void ResetTrackAccel(double friction) + { + trackballAccel = TRACKBALL_RADIUS * friction / TRACKBALL_INERTIA; + } + + public Point GetLastMouseMovementXY() { + return cursor.GetLastMouseMovementXY(); } - public virtual void sixaxisMoved(object sender, SixAxisEventArgs arg) + public void ResetToggleGyroM() { - if (Global.UseSAforMouse[deviceNum] && Global.GyroSensitivity[deviceNum] > 0) + currentToggleGyroM = false; + previousTriggerActivated = false; + triggeractivated = false; + } + + bool triggeractivated = false; + bool previousTriggerActivated = false; + bool useReverseRatchet = false; + bool toggleGyroMouse = true; + public bool ToggleGyroMouse { get => toggleGyroMouse; + set { toggleGyroMouse = value; ResetToggleGyroM(); } } + bool currentToggleGyroM = false; + + public virtual void sixaxisMoved(DS4SixAxis sender, SixAxisEventArgs arg) + { + GyroOutMode outMode = Global.GetGyroOutMode(deviceNum); + if (outMode == GyroOutMode.Mouse && Global.getGyroSensitivity(deviceNum) > 0) { - bool triggeractivated = true; + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.getGyroTriggerTurns(deviceNum); int i = 0; - string[] ss = Global.SATriggers[deviceNum].Split(','); + string[] ss = Global.getSATriggers(deviceNum).Split(','); + bool andCond = Global.getSATriggerCond(deviceNum); + triggeractivated = andCond ? true : false; if (!string.IsNullOrEmpty(ss[0])) - foreach (string s in ss) - if (!(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { triggeractivated = false; - if (triggeractivated) + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) cursor.sixaxisMoved(arg); - dev.getCurrentState(s); + else if (!useReverseRatchet && !triggeractivated) + cursor.sixaxisMoved(arg); + else + cursor.mouseRemainderReset(); + + } + else if (outMode == GyroOutMode.MouseJoystick) + { + s = dev.getCurrentStateRef(); + + useReverseRatchet = Global.GetGyroMouseStickTriggerTurns(deviceNum); + int i = 0; + string[] ss = Global.GetSAMouseStickTriggers(deviceNum).Split(','); + bool andCond = Global.GetSAMouseStickTriggerCond(deviceNum); + triggeractivated = andCond ? true : false; + if (!string.IsNullOrEmpty(ss[0])) + { + string s = string.Empty; + for (int index = 0, arlen = ss.Length; index < arlen; index++) + { + s = ss[index]; + if (andCond && !(int.TryParse(s, out i) && getDS4ControlsByName(i))) + { + triggeractivated = false; + break; + } + else if (!andCond && int.TryParse(s, out i) && getDS4ControlsByName(i)) + { + triggeractivated = true; + break; + } + } + } + + if (toggleGyroMouse) + { + if (triggeractivated && triggeractivated != previousTriggerActivated) + { + currentToggleGyroM = !currentToggleGyroM; + } + + previousTriggerActivated = triggeractivated; + triggeractivated = currentToggleGyroM; + } + else + { + previousTriggerActivated = triggeractivated; + } + + if (useReverseRatchet && triggeractivated) + SixMouseStick(arg); + else if (!useReverseRatchet && !triggeractivated) + SixMouseStick(arg); + else + SixMouseReset(arg); } } + private const int SMOOTH_BUFFER_LEN = 3; + private int[] xSmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int[] ySmoothBuffer = new int[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + + private void SixMouseReset(SixAxisEventArgs args) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0; + ySmoothBuffer[iIndex] = 0; + smoothBufferTail = iIndex + 1; + } + + private void SixMouseStick(SixAxisEventArgs arg) + { + int deltaX = 0, deltaY = 0; + deltaX = Global.getGyroMouseStickHorizontalAxis(0) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //int inputX = deltaX, inputY = deltaY; + int maxDirX = deltaX >= 0 ? 127 : -128; + int maxDirY = deltaY >= 0 ? 127 : -128; + + GyroMouseStickInfo msinfo = Global.GetGyroMouseStickInfo(deviceNum); + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + int deadzoneX = (int)Math.Abs(normX * msinfo.deadZone); + int deadzoneY = (int)Math.Abs(normY * msinfo.deadZone); + + int maxValX = signX * msinfo.maxZone; + int maxValY = signY * msinfo.maxZone; + + double xratio = 0.0, yratio = 0.0; + double antiX = msinfo.antiDeadX * normX; + double antiY = msinfo.antiDeadY * normY; + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + deltaX = (deltaX < 0 && deltaX < maxValX) ? maxValX : + (deltaX > 0 && deltaX > maxValX) ? maxValX : deltaX; + //if (deltaX != maxValX) deltaX -= deltaX % (signX * GyroMouseFuzz); + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + //if (deltaY != maxValY) deltaY -= deltaY % (signY * GyroMouseFuzz); + } + else + { + deltaY = 0; + } + + if (msinfo.useSmoothing) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = deltaX; + ySmoothBuffer[iIndex] = deltaY; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= msinfo.smoothWeight; + } + + x_out /= finalWeight; + deltaX = (int)x_out; + y_out /= finalWeight; + deltaY = (int)y_out; + + maxValX = deltaX < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxValY = deltaY < 0 ? -msinfo.maxZone : msinfo.maxZone; + maxDirX = deltaX >= 0 ? 127 : -128; + maxDirY = deltaY >= 0 ? 127 : -128; + } + + if (msinfo.vertScale != 100) + { + double verticalScale = msinfo.vertScale * 0.01; + deltaY = (int)(deltaY * verticalScale); + deltaY = (deltaY < 0 && deltaY < maxValY) ? maxValY : + (deltaY > 0 && deltaY > maxValY) ? maxValY : deltaY; + } + + if (deltaX != 0) xratio = deltaX / (double)maxValX; + if (deltaY != 0) yratio = deltaY / (double)maxValY; + + double xNorm = 0.0, yNorm = 0.0; + if (xratio != 0.0) + { + xNorm = (1.0 - antiX) * xratio + antiX; + } + + if (yratio != 0.0) + { + yNorm = (1.0 - antiY) * yratio + antiY; + } + + if (msinfo.inverted != 0) + { + if ((msinfo.inverted & 1) == 1) + { + // Invert max dir value + maxDirX = deltaX >= 0 ? -128 : 127; + } + + if ((msinfo.inverted & 2) == 2) + { + // Invert max dir value + maxDirY = deltaY >= 0 ? -128 : 127; + } + } + + byte axisXOut = (byte)(xNorm * maxDirX + 128.0); + byte axisYOut = (byte)(yNorm * maxDirY + 128.0); + Mapping.gyroStickX[deviceNum] = axisXOut; + Mapping.gyroStickY[deviceNum] = axisYOut; + } + private bool getDS4ControlsByName(int key) { switch (key) @@ -59,30 +333,68 @@ private bool getDS4ControlsByName(int key) case 2: return s.Square; case 3: return s.Triangle; case 4: return s.L1; - case 5: return s.L2 > 127; + case 5: return s.L2 > 128; case 6: return s.R1; - case 7: return s.R2 > 127; + case 7: return s.R2 > 128; case 8: return s.DpadUp; case 9: return s.DpadDown; case 10: return s.DpadLeft; case 11: return s.DpadRight; case 12: return s.L3; case 13: return s.R3; - case 14: return s.Touch1; - case 15: return s.Touch2; + case 14: return s.Touch1Finger; + case 15: return s.Touch2Fingers; case 16: return s.Options; case 17: return s.Share; case 18: return s.PS; + default: break; } + return false; } - public virtual void touchesMoved(object sender, TouchpadEventArgs arg) + private bool tempBool = false; + public virtual void touchesMoved(DS4Touchpad sender, TouchpadEventArgs arg) { - if (!Global.UseTPforControls[deviceNum]) + s = dev.getCurrentStateRef(); + + if (Global.getUseTPforControls(deviceNum) == false) { - cursor.touchesMoved(arg, dragging || dragging2); - wheel.touchesMoved(arg, dragging || dragging2); + if (Global.GetTouchActive(deviceNum)) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = (arg.touches[0].deltaX * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballYBuffer[iIndex] = (arg.touches[0].deltaY * TRACKBALL_SCALE) / dev.getCurrentStateRef().elapsedTime; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + + cursor.touchesMoved(arg, dragging || dragging2, tempBool); + wheel.touchesMoved(arg, dragging || dragging2); + } + else + { + if (Global.getTrackballMode(deviceNum)) + { + int iIndex = trackballBufferTail; + trackballXBuffer[iIndex] = 0; + trackballYBuffer[iIndex] = 0; + trackballBufferTail = (iIndex + 1) % TRACKBALL_BUFFER_LEN; + if (trackballBufferHead == trackballBufferTail) + trackballBufferHead = (trackballBufferHead + 1) % TRACKBALL_BUFFER_LEN; + } + } } else { @@ -93,54 +405,78 @@ public virtual void touchesMoved(object sender, TouchpadEventArgs arg) if (arg.touches[0].hwY - firstTouch.hwY > 300) swipeDown = true; if (arg.touches[0].hwY - firstTouch.hwY < -300) swipeUp = true; } + swipeUpB = (byte)Math.Min(255, Math.Max(0, (firstTouch.hwY - arg.touches[0].hwY) * 1.5f)); swipeDownB = (byte)Math.Min(255, Math.Max(0, (arg.touches[0].hwY - firstTouch.hwY) * 1.5f)); swipeLeftB = (byte)Math.Min(255, Math.Max(0, firstTouch.hwX - arg.touches[0].hwX)); swipeRightB = (byte)Math.Min(255, Math.Max(0, arg.touches[0].hwX - firstTouch.hwX)); } + if (Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 50 && arg.touches.Length == 2) + { if (arg.touches[0].hwX - firstTouch.hwX > 200 && !slideleft) slideright = true; else if (firstTouch.hwX - arg.touches[0].hwX > 200 && !slideright) slideleft = true; - dev.getCurrentState(s); + } + synthesizeMouseButtons(); } - public virtual void touchesBegan(object sender, TouchpadEventArgs arg) + + public virtual void touchesBegan(DS4Touchpad sender, TouchpadEventArgs arg) { if (!Global.UseTPforControls[deviceNum]) { + Array.Clear(trackballXBuffer, 0, TRACKBALL_BUFFER_LEN); + Array.Clear(trackballYBuffer, 0, TRACKBALL_BUFFER_LEN); + trackballXVel = 0.0; + trackballYVel = 0.0; + trackballActive = false; + trackballBufferTail = 0; + trackballBufferHead = 0; + trackballDXRemain = 0.0; + trackballDYRemain = 0.0; + cursor.touchesBegan(arg); wheel.touchesBegan(arg); } + pastTime = arg.timeStamp; - firstTouch = arg.touches[0]; - if (Global.DoubleTap[deviceNum]) + firstTouch.populate(arg.touches[0].hwX, arg.touches[0].hwY, arg.touches[0].touchID, + arg.touches[0].previousTouch); + + if (Global.getDoubleTap(deviceNum)) { DateTime test = arg.timeStamp; if (test <= (firstTap + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 1.5)) && !arg.touchButtonPressed) secondtouchbegin = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } - public virtual void touchesEnded(object sender, TouchpadEventArgs arg) + + public virtual void touchesEnded(DS4Touchpad sender, TouchpadEventArgs arg) { + s = dev.getCurrentStateRef(); slideright = slideleft = false; swipeUp = swipeDown = swipeLeft = swipeRight = false; swipeUpB = swipeDownB = swipeLeftB = swipeRightB = 0; - if (Global.TapSensitivity[deviceNum] != 0 && !Global.UseTPforControls[deviceNum]) + byte tapSensitivity = Global.getTapSensitivity(deviceNum); + if (tapSensitivity != 0 && !Global.getUseTPforControls(deviceNum)) { - if (secondtouchbegin) { tappedOnce = false; secondtouchbegin = false; } + DateTime test = arg.timeStamp; - if (test <= (pastTime + TimeSpan.FromMilliseconds((double)Global.TapSensitivity[deviceNum] * 2)) && !arg.touchButtonPressed && !tappedOnce) + if (test <= (pastTime + TimeSpan.FromMilliseconds((double)tapSensitivity * 2)) && !arg.touchButtonPressed && !tappedOnce) + { if (Math.Abs(firstTouch.hwX - arg.touches[0].hwX) < 10 && Math.Abs(firstTouch.hwY - arg.touches[0].hwY) < 10) - if (Global.DoubleTap[deviceNum]) + { + if (Global.getDoubleTap(deviceNum)) { tappedOnce = true; firstTap = arg.timeStamp; @@ -148,8 +484,97 @@ public virtual void touchesEnded(object sender, TouchpadEventArgs arg) } else Mapping.MapClick(deviceNum, Mapping.Click.Left); //this way no delay if disabled + } + } + } + else + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + if (Global.getTrackballMode(deviceNum)) + { + if (!trackballActive) + { + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = -1; + for (int i = 0; i < TRACKBALL_BUFFER_LEN && idx != trackballBufferHead; i++) + { + idx = (trackballBufferTail - i - 1 + TRACKBALL_BUFFER_LEN) % TRACKBALL_BUFFER_LEN; + x_out += trackballXBuffer[idx] * currentWeight; + y_out += trackballYBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= 1.0; + } + + x_out /= finalWeight; + trackballXVel = x_out; + y_out /= finalWeight; + trackballYVel = y_out; + + trackballActive = true; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } } - dev.getCurrentState(s); + synthesizeMouseButtons(); } @@ -163,30 +588,98 @@ private bool isRight(Touch t) return t.hwX >= 1920 * 2 / 5; } - public virtual void touchUnchanged(object sender, EventArgs unused) + public virtual void touchUnchanged(DS4Touchpad sender, EventArgs unused) { - dev.getCurrentState(s); - //if (s.Touch1 || s.Touch2 || s.TouchButton) - synthesizeMouseButtons(); + s = dev.getCurrentStateRef(); + + if (trackballActive) + { + if (Global.getUseTPforControls(deviceNum) == false) + { + int[] disArray = Global.getTouchDisInvertTriggers(deviceNum); + tempBool = true; + for (int i = 0, arlen = disArray.Length; tempBool && i < arlen; i++) + { + if (getDS4ControlsByName(disArray[i]) == false) + tempBool = false; + } + + double tempAngle = Math.Atan2(-trackballYVel, trackballXVel); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(trackballXVel); + int signY = Math.Sign(trackballYVel); + double trackXvDecay = Math.Min(Math.Abs(trackballXVel), trackballAccel * s.elapsedTime * normX); + double trackYvDecay = Math.Min(Math.Abs(trackballYVel), trackballAccel * s.elapsedTime * normY); + double xVNew = trackballXVel - (trackXvDecay * signX); + double yVNew = trackballYVel - (trackYvDecay * signY); + double xMotion = (xVNew * s.elapsedTime) / TRACKBALL_SCALE; + double yMotion = (yVNew * s.elapsedTime) / TRACKBALL_SCALE; + if (xMotion != 0.0) + { + xMotion += trackballDXRemain; + } + else + { + trackballDXRemain = 0.0; + } + + int dx = (int)xMotion; + trackballDXRemain = xMotion - dx; + + if (yMotion != 0.0) + { + yMotion += trackballDYRemain; + } + else + { + trackballDYRemain = 0.0; + } + + int dy = (int)yMotion; + trackballDYRemain = yMotion - dy; + + trackballXVel = xVNew; + trackballYVel = yVNew; + + if (dx == 0 && dy == 0) + { + trackballActive = false; + } + else + { + cursor.TouchMoveCursor(dx, dy, tempBool); + } + } + } + + if (s.Touch1Finger || s.TouchButton) + synthesizeMouseButtons(); } - private DS4State remapped = new DS4State(); public bool dragging, dragging2; + private void synthesizeMouseButtons() { - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft.ToString(), false) == null && leftDown) + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchLeft, false) == null && leftDown) { Mapping.MapClick(deviceNum, Mapping.Click.Left); dragging2 = true; } else + { dragging2 = false; - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper.ToString(), false) == null && upperDown) + } + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchUpper, false) == null && upperDown) Mapping.MapClick(deviceNum, Mapping.Click.Middle); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight.ToString(), false) == null && rightDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchRight, false) == null && rightDown) Mapping.MapClick(deviceNum, Mapping.Click.Left); - if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti.ToString(), false) == null && multiDown) + + if (Global.GetDS4Action(deviceNum, DS4Controls.TouchMulti, false) == null && multiDown) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (!Global.UseTPforControls[deviceNum]) { if (tappedOnce) @@ -205,23 +698,22 @@ private void synthesizeMouseButtons() dragging = true; } else + { dragging = false; + } } - s = remapped; - //remapped.CopyTo(s); } - public virtual void touchButtonUp(object sender, TouchpadEventArgs arg) + public virtual void touchButtonUp(DS4Touchpad sender, TouchpadEventArgs arg) { pushed = DS4Controls.None; upperDown = leftDown = rightDown = multiDown = false; - dev.setRumble(0, 0); - dev.getCurrentState(s); + s = dev.getCurrentStateRef(); if (s.Touch1 || s.Touch2) synthesizeMouseButtons(); } - public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) + public virtual void touchButtonDown(DS4Touchpad sender, TouchpadEventArgs arg) { if (arg.touches == null) upperDown = true; @@ -231,15 +723,30 @@ public virtual void touchButtonDown(object sender, TouchpadEventArgs arg) { if ((Global.LowerRCOn[deviceNum] && arg.touches[0].hwX > (1920 * 3) / 4 && arg.touches[0].hwY > (960 * 3) / 4)) Mapping.MapClick(deviceNum, Mapping.Click.Right); + if (isLeft(arg.touches[0])) leftDown = true; else if (isRight(arg.touches[0])) rightDown = true; } - dev.getCurrentState(s); + + s = dev.getCurrentStateRef(); synthesizeMouseButtons(); } + public void populatePriorButtonStates() + { + priorUpperDown = upperDown; + priorLeftDown = leftDown; + priorRightDown = rightDown; + priorMultiDown = multiDown; + + priorSwipeLeft = swipeLeft; priorSwipeRight = swipeRight; + priorSwipeUp = swipeUp; priorSwipeDown = swipeDown; + priorSwipeLeftB = swipeLeftB; priorSwipeRightB = swipeRightB; priorSwipeUpB = swipeUpB; + priorSwipeDownB = swipeDownB; priorSwipedB = swipedB; + } + public DS4State getDS4State() { return s; diff --git a/DS4Windows/DS4Control/MouseCursor.cs b/DS4Windows/DS4Control/MouseCursor.cs index 0a5870d3b7..d11ea56865 100644 --- a/DS4Windows/DS4Control/MouseCursor.cs +++ b/DS4Windows/DS4Control/MouseCursor.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - +using System.Drawing; namespace DS4Windows { @@ -20,37 +17,187 @@ public MouseCursor(int deviceNum) /** Indicate x/y direction for doing jitter compensation, etc. */ public enum Direction { Negative, Neutral, Positive } // Track direction vector separately and very trivially for now. - private Direction horizontalDirection = Direction.Neutral, verticalDirection = Direction.Neutral; + private Direction horizontalDirection = Direction.Neutral, + verticalDirection = Direction.Neutral; private Direction hDirection = Direction.Neutral, vDirection = Direction.Neutral; + private const double GYRO_MOUSE_COEFFICIENT = 0.0095; + public const int GYRO_MOUSE_DEADZONE = 10; + private const double GYRO_MOUSE_OFFSET = 0.1463; + private const double GYRO_SMOOTH_MOUSE_OFFSET = 0.14698; + private const double TOUCHPAD_MOUSE_OFFSET = 0.015; + + private const int SMOOTH_BUFFER_LEN = 3; + private double[] xSmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private double[] ySmoothBuffer = new double[SMOOTH_BUFFER_LEN]; + private int smoothBufferTail = 0; + private int gyroCursorDeadZone = GYRO_MOUSE_DEADZONE; + public int GyroCursorDeadZone { get => gyroCursorDeadZone; set => gyroCursorDeadZone = value; } + + Point lastMouseMovementXY = new Point(0, 0); + + double coefficient = 0.0; + double verticalScale = 0.0; + bool gyroSmooth = false; + + int tempInt = 0; + double tempDouble = 0.0; + bool tempBool = false; + public virtual void sixaxisMoved(SixAxisEventArgs arg) { int deltaX = 0, deltaY = 0; - deltaX = -arg.sixAxis.accelX; - deltaY = -arg.sixAxis.accelY; - //Console.WriteLine(arg.sixAxis.deltaX); - - double coefficient = Global.GyroSensitivity[deviceNumber] / 100f; - //Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - xMotion += hRemainder; - int xAction = (int)xMotion; - hRemainder += xMotion - xAction; - hRemainder -= (int)hRemainder; - double yMotion = coefficient * deltaY; - yMotion += vRemainder; - int yAction = (int)yMotion; - vRemainder += yMotion - yAction; - vRemainder -= (int)vRemainder; - if (Global.GyroInvert[deviceNumber] == 2 || Global.GyroInvert[deviceNumber] == 3) + deltaX = Global.getGyroMouseHorizontalAxis(deviceNumber) == 0 ? arg.sixAxis.gyroYawFull : + arg.sixAxis.gyroRollFull; + deltaY = -arg.sixAxis.gyroPitchFull; + //tempDouble = arg.sixAxis.elapsed * 0.001 * 200.0; // Base default speed on 5 ms + tempDouble = arg.sixAxis.elapsed * 200.0; // Base default speed on 5 ms + + gyroSmooth = Global.getGyroSmoothing(deviceNumber); + double gyroSmoothWeight = 0.0; + + coefficient = (Global.getGyroSensitivity(deviceNumber) * 0.01) * GYRO_MOUSE_COEFFICIENT; + double offset = GYRO_MOUSE_OFFSET; + if (gyroSmooth) + { + gyroSmoothWeight = Global.getGyroSmoothingWeight(deviceNumber); + if (gyroSmoothWeight > 0.0) + { + offset = GYRO_SMOOTH_MOUSE_OFFSET; + } + } + + double tempAngle = Math.Atan2(-deltaY, deltaX); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(deltaX); + int signY = Math.Sign(deltaY); + + if (deltaX == 0 || (hRemainder > 0 != deltaX > 0)) + { + hRemainder = 0.0; + } + + if (deltaY == 0 || (vRemainder > 0 != deltaY > 0)) + { + vRemainder = 0.0; + } + + int deadzoneX = (int)Math.Abs(normX * gyroCursorDeadZone); + int deadzoneY = (int)Math.Abs(normY * gyroCursorDeadZone); + + if (Math.Abs(deltaX) > deadzoneX) + { + deltaX -= signX * deadzoneX; + } + else + { + deltaX = 0; + } + + if (Math.Abs(deltaY) > deadzoneY) + { + deltaY -= signY * deadzoneY; + } + else + { + deltaY = 0; + } + + double xMotion = deltaX != 0 ? coefficient * (deltaX * tempDouble) + + (normX * (offset * signX)) : 0; + + int xAction = 0; + if (xMotion != 0.0) + { + xMotion += hRemainder; + } + else + { + hRemainder = 0.0; + } + + verticalScale = Global.getGyroSensVerticalScale(deviceNumber) * 0.01; + double yMotion = deltaY != 0 ? (coefficient * verticalScale) * (deltaY * tempDouble) + + (normY * (offset * signY)) : 0; + + int yAction = 0; + if (yMotion != 0.0) + { + yMotion += vRemainder; + } + else + { + vRemainder = 0.0; + } + + if (gyroSmooth) + { + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = xMotion; + ySmoothBuffer[iIndex] = yMotion; + smoothBufferTail = iIndex + 1; + + double currentWeight = 1.0; + double finalWeight = 0.0; + double x_out = 0.0, y_out = 0.0; + int idx = 0; + for (int i = 0; i < SMOOTH_BUFFER_LEN; i++) + { + idx = (smoothBufferTail - i - 1 + SMOOTH_BUFFER_LEN) % SMOOTH_BUFFER_LEN; + x_out += xSmoothBuffer[idx] * currentWeight; + y_out += ySmoothBuffer[idx] * currentWeight; + finalWeight += currentWeight; + currentWeight *= gyroSmoothWeight; + } + + x_out /= finalWeight; + xMotion = x_out; + y_out /= finalWeight; + yMotion = y_out; + } + + hRemainder = vRemainder = 0.0; + if (xMotion != 0.0) + { + xAction = (int)xMotion; + hRemainder = xMotion - xAction; + } + + if (yMotion != 0.0) + { + yAction = (int)yMotion; + vRemainder = yMotion - yAction; + } + + int gyroInvert = Global.getGyroInvert(deviceNumber); + if ((gyroInvert & 0x02) == 2) xAction *= -1; - if (Global.GyroInvert[deviceNumber] == 1 || Global.GyroInvert[deviceNumber] == 3) + + if ((gyroInvert & 0x01) == 1) yAction *= -1; - if (yAction != 0 || xAction != 0) - InputMethods.MoveCursorBy(xAction, yAction); + if (!DS4Lightgun.SET_R2_AS_LIGHTGUN) { + if (yAction != 0 || xAction != 0) + InputMethods.MoveCursorBy(xAction, yAction); + } hDirection = xMotion > 0.0 ? Direction.Positive : xMotion < 0.0 ? Direction.Negative : Direction.Neutral; vDirection = yMotion > 0.0 ? Direction.Positive : yMotion < 0.0 ? Direction.Negative : Direction.Neutral; + + lastMouseMovementXY = new Point(xAction, yAction); + } + + public Point GetLastMouseMovementXY() { + return lastMouseMovementXY; + } + + public void mouseRemainderReset() + { + hRemainder = vRemainder = 0.0; + int iIndex = smoothBufferTail % SMOOTH_BUFFER_LEN; + xSmoothBuffer[iIndex] = 0.0; + ySmoothBuffer[iIndex] = 0.0; + smoothBufferTail = iIndex + 1; } public void touchesBegan(TouchpadEventArgs arg) @@ -59,15 +206,18 @@ public void touchesBegan(TouchpadEventArgs arg) { horizontalRemainder = verticalRemainder = 0.0; horizontalDirection = verticalDirection = Direction.Neutral; + } } private byte lastTouchID; - public void touchesMoved(TouchpadEventArgs arg, bool dragging) + public void touchesMoved(TouchpadEventArgs arg, bool dragging, bool disableInvert = false) { - if ((!dragging && arg.touches.Length != 1) || (dragging && arg.touches.Length < 1)) + int touchesLen = arg.touches.Length; + if ((!dragging && touchesLen != 1) || (dragging && touchesLen < 1)) return; - int deltaX, deltaY; + + int deltaX = 0, deltaY = 0; if (arg.touches[0].touchID != lastTouchID) { deltaX = deltaY = 0; @@ -75,11 +225,9 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) horizontalDirection = verticalDirection = Direction.Neutral; lastTouchID = arg.touches[0].touchID; } - else if (Global.TouchpadJitterCompensation[deviceNumber]) + else { - // Often the DS4's internal jitter compensation kicks in and starts hiding changes, ironically creating jitter... - - if (dragging && arg.touches.Length > 1) + if (dragging && touchesLen > 1) { deltaX = arg.touches[1].deltaX; deltaY = arg.touches[1].deltaY; @@ -89,86 +237,75 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) deltaX = arg.touches[0].deltaX; deltaY = arg.touches[0].deltaY; } - // allow only very fine, slow motions, when changing direction, even from neutral - // TODO maybe just consume it completely? - if (deltaX <= -1) - { - if (horizontalDirection != Direction.Negative) - { - deltaX = -1; - horizontalRemainder = 0.0; - } - } - else if (deltaX >= 1) - { - if (horizontalDirection != Direction.Positive) - { - deltaX = 1; - horizontalRemainder = 0.0; - } - } - - if (deltaY <= -1) - { - if (verticalDirection != Direction.Negative) - { - deltaY = -1; - verticalRemainder = 0.0; - } - } - else if (deltaY >= 1) - { - if (verticalDirection != Direction.Positive) - { - deltaY = 1; - verticalRemainder = 0.0; - } - } } - else + + TouchMoveCursor(deltaX, deltaY, disableInvert); + } + + public void TouchMoveCursor(int dx, int dy, bool disableInvert = false) + { + double tempAngle = Math.Atan2(-dy, dx); + double normX = Math.Abs(Math.Cos(tempAngle)); + double normY = Math.Abs(Math.Sin(tempAngle)); + int signX = Math.Sign(dx); + int signY = Math.Sign(dy); + double coefficient = Global.getTouchSensitivity(deviceNumber) * 0.01; + bool jitterCompenstation = Global.getTouchpadJitterCompensation(deviceNumber); + + double xMotion = dx != 0 ? + coefficient * dx + (normX * (TOUCHPAD_MOUSE_OFFSET * signX)) : 0.0; + + double yMotion = dy != 0 ? + coefficient * dy + (normY * (TOUCHPAD_MOUSE_OFFSET * signY)) : 0.0; + + if (jitterCompenstation) { - if (dragging && arg.touches.Length > 1) + double absX = Math.Abs(xMotion); + if (absX <= normX * 0.15) { - deltaX = arg.touches[1].deltaX; - deltaY = arg.touches[1].deltaY; + xMotion = signX * Math.Pow(absX / 0.15f, 1.408) * 0.15; } - else + + double absY = Math.Abs(yMotion); + if (absY <= normY * 0.15) { - deltaX = arg.touches[0].deltaX; - deltaY = arg.touches[0].deltaY; + yMotion = signY * Math.Pow(absY / 0.15f, 1.408) * 0.15; } } - double coefficient = Global.TouchSensitivity[deviceNumber] / 100.0; // Collect rounding errors instead of losing motion. - double xMotion = coefficient * deltaX; - if (xMotion > 0.0) + if (xMotion > 0.0 && horizontalRemainder > 0.0) { - if (horizontalRemainder > 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } - else if (xMotion < 0.0) + else if (xMotion < 0.0 && horizontalRemainder < 0.0) { - if (horizontalRemainder < 0.0) - xMotion += horizontalRemainder; + xMotion += horizontalRemainder; } int xAction = (int)xMotion; horizontalRemainder = xMotion - xAction; - double yMotion = coefficient * deltaY; - if (yMotion > 0.0) + if (yMotion > 0.0 && verticalRemainder > 0.0) { - if (verticalRemainder > 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } - else if (yMotion < 0.0) + else if (yMotion < 0.0 && verticalRemainder < 0.0) { - if (verticalRemainder < 0.0) - yMotion += verticalRemainder; + yMotion += verticalRemainder; } int yAction = (int)yMotion; verticalRemainder = yMotion - yAction; + if (disableInvert == false) + { + int touchpadInvert = tempInt = Global.getTouchpadInvert(deviceNumber); + if ((touchpadInvert & 0x02) == 2) + xAction *= -1; + + if ((touchpadInvert & 0x01) == 1) + yAction *= -1; + } + if (yAction != 0 || xAction != 0) InputMethods.MoveCursorBy(xAction, yAction); diff --git a/DS4Windows/DS4Control/MouseWheel.cs b/DS4Windows/DS4Control/MouseWheel.cs index d237731bca..e17ba13aef 100644 --- a/DS4Windows/DS4Control/MouseWheel.cs +++ b/DS4Windows/DS4Control/MouseWheel.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace DS4Windows { @@ -27,6 +23,7 @@ public void touchesMoved(TouchpadEventArgs arg, bool dragging) { if (arg.touches.Length != 2 || dragging) return; + Touch lastT0 = arg.touches[0].previousTouch; Touch lastT1 = arg.touches[1].previousTouch; Touch T0 = arg.touches[0]; diff --git a/DS4Windows/DS4Control/OutputDevice.cs b/DS4Windows/DS4Control/OutputDevice.cs new file mode 100644 index 0000000000..3e46f526f2 --- /dev/null +++ b/DS4Windows/DS4Control/OutputDevice.cs @@ -0,0 +1,12 @@ +using System; + +namespace DS4Windows +{ + public abstract class OutputDevice + { + public abstract void ConvertandSendReport(DS4State state, int device); + public abstract void Connect(); + public abstract void Disconnect(); + public abstract string GetDeviceType(); + } +} diff --git a/DS4Windows/DS4Control/OutputSlotManager.cs b/DS4Windows/DS4Control/OutputSlotManager.cs new file mode 100644 index 0000000000..429a143e88 --- /dev/null +++ b/DS4Windows/DS4Control/OutputSlotManager.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Nefarius.ViGEm.Client; + +namespace DS4Windows +{ + public class OutputSlotManager + { + public const int DELAY_TIME = 500; // measured in ms + + private Dictionary deviceDict = new Dictionary(); + private Dictionary revDeviceDict = new Dictionary(); + private OutputDevice[] outputDevices = new OutputDevice[4]; + private Queue actions = new Queue(); + private object actionLock = new object(); + private bool runningQueue; + + public bool RunningQueue { get => runningQueue; } + + public OutputDevice AllocateController(OutContType contType, ViGEmClient client) + { + OutputDevice outputDevice = null; + switch(contType) + { + case OutContType.X360: + outputDevice = new Xbox360OutDevice(client); + break; + case OutContType.DS4: + outputDevice = new DS4OutDevice(client); + break; + case OutContType.None: + default: + break; + } + + return outputDevice; + } + + private int FindSlot() + { + int result = -1; + for (int i = 0; i < outputDevices.Length && result == -1; i++) + { + OutputDevice tempdev = outputDevices[i]; + if (tempdev == null) + { + result = i; + } + } + + return result; + } + + private void LaunchEvents() + { + bool hasItems = false; + Action act = null; + lock (actionLock) + { + hasItems = actions.Count > 0; + } + + while (hasItems) + { + lock (actionLock) + { + act = actions.Dequeue(); + } + + act.Invoke(); + + lock (actionLock) + { + hasItems = actions.Count > 0; + } + } + } + + private void PrepareEventTask() + { + if (!runningQueue) + { + runningQueue = true; + Task.Run(() => + { + LaunchEvents(); + runningQueue = false; + }); + } + } + + public void DeferredPlugin(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs) + { + Action tempAction = new Action(() => + { + int slot = FindSlot(); + if (slot != -1) + { + outputDevice.Connect(); + outputDevices[slot] = outputDevice; + deviceDict.Add(slot, outputDevice); + revDeviceDict.Add(outputDevice, slot); + Task.Delay(DELAY_TIME).Wait(); + outdevs[inIdx] = outputDevice; + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + + public void DeferredRemoval(OutputDevice outputDevice, int inIdx, OutputDevice[] outdevs, bool immediate = false) + { + Action tempAction = new Action(() => + { + if (revDeviceDict.ContainsKey(outputDevice)) + { + int slot = revDeviceDict[outputDevice]; + outputDevices[slot] = null; + deviceDict.Remove(slot); + revDeviceDict.Remove(outputDevice); + outputDevice.Disconnect(); + outdevs[inIdx] = null; + if (!immediate) + { + Task.Delay(DELAY_TIME).Wait(); + } + } + }); + + lock (actionLock) + { + actions.Enqueue(tempAction); + } + + PrepareEventTask(); + } + } +} diff --git a/DS4Windows/DS4Control/ProfilePropGroups.cs b/DS4Windows/DS4Control/ProfilePropGroups.cs new file mode 100644 index 0000000000..91eee96b09 --- /dev/null +++ b/DS4Windows/DS4Control/ProfilePropGroups.cs @@ -0,0 +1,98 @@ +using System; + +namespace DS4Windows +{ + public class SquareStickInfo + { + public bool lsMode; + public bool rsMode; + public double lsRoundness = 5.0; + public double rsRoundness = 5.0; + } + + public class StickDeadZoneInfo + { + public int deadZone; + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class TriggerDeadZoneZInfo + { + public byte deadZone; // Trigger deadzone is expressed in axis units + public int antiDeadZone; + public int maxZone = 100; + public double maxOutput = 100.0; + } + + public class GyroMouseInfo + { + + } + + public class GyroMouseStickInfo + { + public int deadZone; + public int maxZone; + public double antiDeadX; + public double antiDeadY; + public int vertScale; + // Flags representing invert axis choices + public uint inverted; + public bool useSmoothing; + public double smoothWeight; + } + + public enum LightbarMode : uint + { + None, + DS4Win, + Passthru, + } + + public class LightbarDS4WinInfo + { + public bool useCustomLed; + public bool ledAsBattery; + public DS4Color m_CustomLed = new DS4Color(0, 0, 255); + public DS4Color m_Led; + public DS4Color m_LowLed; + public DS4Color m_ChargingLed; + public DS4Color m_FlashLed; + public double rainbow; + public double maxRainbowSat = 1.0; + public int flashAt; // Battery % when flashing occurs. <0 means disabled + public byte flashType; + public int chargingType; + } + + public class LightbarSettingInfo + { + public LightbarMode mode = LightbarMode.DS4Win; + public LightbarDS4WinInfo ds4winSettings = new LightbarDS4WinInfo(); + public LightbarMode Mode + { + get => mode; + set + { + if (mode == value) return; + mode = value; + ChangedMode?.Invoke(this, EventArgs.Empty); + } + } + public event EventHandler ChangedMode; + + public LightbarSettingInfo() + { + /*ChangedMode += (sender, e) => + { + if (mode != LightbarMode.DS4Win) + { + ds4winSettings = null; + } + }; + */ + } + } +} \ No newline at end of file diff --git a/DS4Windows/DS4Control/Program.cs b/DS4Windows/DS4Control/Program.cs new file mode 100644 index 0000000000..c78e7615a8 --- /dev/null +++ b/DS4Windows/DS4Control/Program.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DS4Windows +{ + public class Program + { + public static ControlService rootHub; + } +} diff --git a/DS4Windows/DS4Control/ScpDevice.Designer.cs b/DS4Windows/DS4Control/ScpDevice.Designer.cs deleted file mode 100644 index f1f8f1ed01..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpDevice - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpDevice.cs b/DS4Windows/DS4Control/ScpDevice.cs deleted file mode 100644 index ae276170c7..0000000000 --- a/DS4Windows/DS4Control/ScpDevice.cs +++ /dev/null @@ -1,707 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Windows.Forms; - -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -namespace DS4Windows -{ - public partial class ScpDevice : Component - { - public virtual Boolean IsActive - { - get { return m_IsActive; } - } - - public virtual String Path - { - get { return m_Path; } - } - - - public ScpDevice() - { - InitializeComponent(); - } - - public ScpDevice(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - public ScpDevice(String Class) - { - InitializeComponent(); - - this.m_Class = new Guid(Class); - } - - - public virtual Boolean Open(Int32 Instance = 0) - { - String DevicePath = String.Empty; - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (Find(m_Class, ref DevicePath, Instance)) - { - Open(DevicePath); - } - - return m_IsActive; - } - - public virtual Boolean Open(String DevicePath) - { - m_Path = DevicePath.ToUpper(); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - - if (GetDeviceHandle(m_Path)) - { - if (WinUsb_Initialize(m_FileHandle, ref m_WinUsbHandle)) - { - if (InitializeDevice()) - { - m_IsActive = true; - } - else - { - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - } - else - { - m_FileHandle.Close(); - } - } - - return m_IsActive; - } - - public virtual Boolean Start() - { - return m_IsActive; - } - - public virtual Boolean Stop() - { - m_IsActive = false; - - if (!(m_WinUsbHandle == (IntPtr) INVALID_HANDLE_VALUE)) - { - WinUsb_AbortPipe(m_WinUsbHandle, m_IntIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkIn); - WinUsb_AbortPipe(m_WinUsbHandle, m_BulkOut); - - WinUsb_Free(m_WinUsbHandle); - m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; - } - - if (m_FileHandle != null && !m_FileHandle.IsInvalid && !m_FileHandle.IsClosed) - { - m_FileHandle.Close(); - m_FileHandle = null; - } - - return true; - } - - public virtual Boolean Close() - { - return Stop(); - } - - - public virtual Boolean ReadIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_IntIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean ReadBulkPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_ReadPipe(m_WinUsbHandle, m_BulkIn, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_IntOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - public virtual Boolean WriteBulkPipe(Byte[] Buffer, Int32 Length, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - return WinUsb_WritePipe(m_WinUsbHandle, m_BulkOut, Buffer, Length, ref Transfered, IntPtr.Zero); - } - - - public virtual Boolean SendTransfer(Byte RequestType, Byte Request, UInt16 Value, Byte[] Buffer, ref Int32 Transfered) - { - if (!m_IsActive) return false; - - WINUSB_SETUP_PACKET Setup = new WINUSB_SETUP_PACKET(); - - Setup.RequestType = RequestType; - Setup.Request = Request; - Setup.Value = Value; - Setup.Index = 0; - Setup.Length = (UInt16) Buffer.Length; - - return WinUsb_ControlTransfer(m_WinUsbHandle, Setup, Buffer, Buffer.Length, ref Transfered, IntPtr.Zero); - } - - - #region Constant and Structure Definitions - public const Int32 SERVICE_CONTROL_STOP = 0x00000001; - public const Int32 SERVICE_CONTROL_SHUTDOWN = 0x00000005; - public const Int32 SERVICE_CONTROL_DEVICEEVENT = 0x0000000B; - public const Int32 SERVICE_CONTROL_POWEREVENT = 0x0000000D; - - public const Int32 DBT_DEVICEARRIVAL = 0x8000; - public const Int32 DBT_DEVICEQUERYREMOVE = 0x8001; - public const Int32 DBT_DEVICEREMOVECOMPLETE = 0x8004; - public const Int32 DBT_DEVTYP_DEVICEINTERFACE = 0x0005; - public const Int32 DBT_DEVTYP_HANDLE = 0x0006; - - public const Int32 PBT_APMRESUMEAUTOMATIC = 0x0012; - public const Int32 PBT_APMSUSPEND = 0x0004; - - public const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0x0000; - public const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 0x0001; - public const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x0004; - - public const Int32 WM_DEVICECHANGE = 0x0219; - - public const Int32 DIGCF_PRESENT = 0x0002; - public const Int32 DIGCF_DEVICEINTERFACE = 0x0010; - - public delegate Int32 ServiceControlHandlerEx(Int32 Control, Int32 Type, IntPtr Data, IntPtr Context); - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_DEVICEINTERFACE - { - internal Int32 dbcc_size; - internal Int32 dbcc_devicetype; - internal Int32 dbcc_reserved; - internal Guid dbcc_classguid; - internal Int16 dbcc_name; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public class DEV_BROADCAST_DEVICEINTERFACE_M - { - public Int32 dbcc_size; - public Int32 dbcc_devicetype; - public Int32 dbcc_reserved; - - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] - public Byte[] dbcc_classguid; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] - public Char[] dbcc_name; - } - - [StructLayout(LayoutKind.Sequential)] - public class DEV_BROADCAST_HDR - { - public Int32 dbch_size; - public Int32 dbch_devicetype; - public Int32 dbch_reserved; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_DEVICE_INTERFACE_DATA - { - internal Int32 cbSize; - internal Guid InterfaceClassGuid; - internal Int32 Flags; - internal IntPtr Reserved; - } - - protected const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80; - protected const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; - protected const UInt32 FILE_SHARE_READ = 1; - protected const UInt32 FILE_SHARE_WRITE = 2; - protected const UInt32 GENERIC_READ = 0x80000000; - protected const UInt32 GENERIC_WRITE = 0x40000000; - protected const Int32 INVALID_HANDLE_VALUE = -1; - protected const UInt32 OPEN_EXISTING = 3; - protected const UInt32 DEVICE_SPEED = 1; - protected const Byte USB_ENDPOINT_DIRECTION_MASK = 0x80; - - protected enum POLICY_TYPE - { - SHORT_PACKET_TERMINATE = 1, - AUTO_CLEAR_STALL = 2, - PIPE_TRANSFER_TIMEOUT = 3, - IGNORE_SHORT_PACKETS = 4, - ALLOW_PARTIAL_READS = 5, - AUTO_FLUSH = 6, - RAW_IO = 7, - } - - protected enum USBD_PIPE_TYPE - { - UsbdPipeTypeControl = 0, - UsbdPipeTypeIsochronous = 1, - UsbdPipeTypeBulk = 2, - UsbdPipeTypeInterrupt = 3, - } - - protected enum USB_DEVICE_SPEED - { - UsbLowSpeed = 1, - UsbFullSpeed = 2, - UsbHighSpeed = 3, - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_CONFIGURATION_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal UInt16 wTotalLength; - internal Byte bNumInterfaces; - internal Byte bConfigurationValue; - internal Byte iConfiguration; - internal Byte bmAttributes; - internal Byte MaxPower; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct USB_INTERFACE_DESCRIPTOR - { - internal Byte bLength; - internal Byte bDescriptorType; - internal Byte bInterfaceNumber; - internal Byte bAlternateSetting; - internal Byte bNumEndpoints; - internal Byte bInterfaceClass; - internal Byte bInterfaceSubClass; - internal Byte bInterfaceProtocol; - internal Byte iInterface; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct WINUSB_PIPE_INFORMATION - { - internal USBD_PIPE_TYPE PipeType; - internal Byte PipeId; - internal UInt16 MaximumPacketSize; - internal Byte Interval; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - protected struct WINUSB_SETUP_PACKET - { - internal Byte RequestType; - internal Byte Request; - internal UInt16 Value; - internal UInt16 Index; - internal UInt16 Length; - } - - protected const Int32 DIF_PROPERTYCHANGE = 0x12; - protected const Int32 DICS_ENABLE = 1; - protected const Int32 DICS_DISABLE = 2; - protected const Int32 DICS_PROPCHANGE = 3; - protected const Int32 DICS_FLAG_GLOBAL = 1; - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_CLASSINSTALL_HEADER - { - internal Int32 cbSize; - internal Int32 InstallFunction; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_PROPCHANGE_PARAMS - { - internal SP_CLASSINSTALL_HEADER ClassInstallHeader; - internal Int32 StateChange; - internal Int32 Scope; - internal Int32 HwProfile; - } - #endregion - - #region Protected Data Members - protected Guid m_Class = Guid.Empty; - protected String m_Path = String.Empty; - - protected SafeFileHandle m_FileHandle = null; - protected IntPtr m_WinUsbHandle = IntPtr.Zero; - - protected Byte m_IntIn = 0xFF; - protected Byte m_IntOut = 0xFF; - protected Byte m_BulkIn = 0xFF; - protected Byte m_BulkOut = 0xFF; - - protected Boolean m_IsActive = false; - #endregion - - #region Static Helper Methods - public enum Notified { Ignore = 0x0000, Arrival = 0x8000, QueryRemove = 0x8001, Removal = 0x8004 }; - - public static Boolean RegisterNotify(IntPtr Form, Guid Class, ref IntPtr Handle, Boolean Window = true) - { - IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; - - try - { - DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); - Int32 Size = Marshal.SizeOf(devBroadcastDeviceInterface); - - devBroadcastDeviceInterface.dbcc_size = Size; - devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - devBroadcastDeviceInterface.dbcc_reserved = 0; - devBroadcastDeviceInterface.dbcc_classguid = Class; - - devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(Size); - Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); - - Handle = RegisterDeviceNotification(Form, devBroadcastDeviceInterfaceBuffer, Window ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE); - - Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); - - return Handle != IntPtr.Zero; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) - { - Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); - } - } - } - - public static Boolean UnregisterNotify(IntPtr Handle) - { - try - { - return UnregisterDeviceNotification(Handle); - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - #endregion - - #region Protected Methods - protected virtual Boolean Find(Guid Target, ref String Path, Int32 Instance = 0) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref Target, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref Target, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4: detailDataBuffer.ToInt64() + 4); - - Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (memberIndex == Instance) return true; - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceInstance(ref String Instance) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); - Int32 bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref m_Class, memberIndex, ref DeviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - { - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4 : detailDataBuffer.ToInt64() + 4); - - String Current = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - Marshal.FreeHGlobal(detailDataBuffer); - - if (Current == Path) - { - Int32 nBytes = 256; - IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); - - CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0); - Instance = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper(); - - Marshal.FreeHGlobal(ptrInstanceBuf); - return true; - } - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - protected virtual Boolean GetDeviceHandle(String Path) - { - m_FileHandle = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); - - return !m_FileHandle.IsInvalid; - } - - protected virtual Boolean UsbEndpointDirectionIn(Int32 addr) - { - return (addr & 0x80) == 0x80; - } - - protected virtual Boolean UsbEndpointDirectionOut(Int32 addr) - { - return (addr & 0x80) == 0x00; - } - - protected virtual Boolean InitializeDevice() - { - try - { - USB_INTERFACE_DESCRIPTOR ifaceDescriptor = new USB_INTERFACE_DESCRIPTOR(); - WINUSB_PIPE_INFORMATION pipeInfo = new WINUSB_PIPE_INFORMATION(); - - if (WinUsb_QueryInterfaceSettings(m_WinUsbHandle, 0, ref ifaceDescriptor)) - { - for (Int32 i = 0; i < ifaceDescriptor.bNumEndpoints; i++) - { - WinUsb_QueryPipe(m_WinUsbHandle, 0, System.Convert.ToByte(i), ref pipeInfo); - - if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionIn(pipeInfo.PipeId))) - { - m_BulkIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkIn); - } - else if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionOut(pipeInfo.PipeId))) - { - m_BulkOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_BulkOut); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionIn(pipeInfo.PipeId)) - { - m_IntIn = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntIn); - } - else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionOut(pipeInfo.PipeId)) - { - m_IntOut = pipeInfo.PipeId; - WinUsb_FlushPipe(m_WinUsbHandle, m_IntOut); - } - } - - return true; - } - - return false; - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - } - - protected virtual Boolean RestartDevice(String InstanceId) - { - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); - - deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); - deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if (SetupDiOpenDeviceInfo(deviceInfoSet, InstanceId, IntPtr.Zero, 0, ref deviceInterfaceData)) - { - SP_PROPCHANGE_PARAMS props = new SP_PROPCHANGE_PARAMS(); - - props.ClassInstallHeader = new SP_CLASSINSTALL_HEADER(); - props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader); - props.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; - - props.Scope = DICS_FLAG_GLOBAL; - props.StateChange = DICS_PROPCHANGE; - props.HwProfile = 0x00; - - if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, Marshal.SizeOf(props))) - { - return SetupDiChangeState(deviceInfoSet, ref deviceInterfaceData); - } - } - } - catch (Exception ex) - { - Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); - throw; - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - #endregion - - #region Interop Definitions - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true)] - protected static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - protected static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); - - [DllImport("user32.dll", SetLastError = true)] - protected static extern Boolean UnregisterDeviceNotification(IntPtr Handle); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UInt32 hTemplateFile); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, ref USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, ref WINUSB_PIPE_INFORMATION PipeInformation); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_AbortPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_FlushPipe(IntPtr InterfaceHandle, Byte PipeID); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_ReadPipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_WritePipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); - - [DllImport("winusb.dll", SetLastError = true)] - protected static extern Boolean WinUsb_Free(IntPtr InterfaceHandle); - - [DllImport("advapi32.dll", SetLastError = true)] - public static extern IntPtr RegisterServiceCtrlHandlerEx(String ServiceName, ServiceControlHandlerEx Callback, IntPtr Context); - - [DllImport("kernel32.dll", SetLastError = true)] - protected static extern Boolean DeviceIoControl(SafeFileHandle DeviceHandle, Int32 IoControlCode, Byte[] InBuffer, Int32 InBufferSize, Byte[] OutBuffer, Int32 OutBufferSize, ref Int32 BytesReturned, IntPtr Overlapped); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Int32 CM_Get_Device_ID(Int32 dnDevInst, IntPtr Buffer, Int32 BufferLen, Int32 ulFlags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, String DeviceInstanceId, IntPtr hwndParent, Int32 Flags, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiChangeState(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_PROPCHANGE_PARAMS ClassInstallParams, Int32 ClassInstallParamsSize); - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.Designer.cs b/DS4Windows/DS4Control/ScpHub.Designer.cs deleted file mode 100644 index ed08d0c1c0..0000000000 --- a/DS4Windows/DS4Control/ScpHub.Designer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace DS4Windows -{ - partial class ScpHub - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - } - - #endregion - } -} diff --git a/DS4Windows/DS4Control/ScpHub.cs b/DS4Windows/DS4Control/ScpHub.cs deleted file mode 100644 index fd7031a495..0000000000 --- a/DS4Windows/DS4Control/ScpHub.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.ComponentModel; - -namespace DS4Windows -{ - public partial class ScpHub : Component - { - protected IntPtr m_Reference = IntPtr.Zero; - protected volatile Boolean m_Started = false; - - public event EventHandler Debug = null; - - public event EventHandler Report = null; - - protected virtual Boolean LogDebug(String Data, bool warning) - { - DebugEventArgs args = new DebugEventArgs(Data, warning); - - On_Debug(this, args); - - return true; - } - - public Boolean Active - { - get { return m_Started; } - } - - - public ScpHub() - { - InitializeComponent(); - } - - public ScpHub(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } - - - public virtual Boolean Open() - { - return true; - } - - public virtual Boolean Start() - { - return m_Started; - } - - public virtual Boolean Stop() - { - return !m_Started; - } - - public virtual Boolean Close() - { - if (m_Reference != IntPtr.Zero) ScpDevice.UnregisterNotify(m_Reference); - - return !m_Started; - } - - - public virtual Boolean Suspend() - { - return true; - } - - public virtual Boolean Resume() - { - return true; - } - - protected virtual void On_Debug(object sender, DebugEventArgs e) - { - if (Debug != null) Debug(sender, e); - } - - - protected virtual void On_Report(object sender, ReportEventArgs e) - { - if (Report != null) Report(sender, e); - } - } -} diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs index 2b23fcb603..f755a0e422 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.IO; using System.Reflection; @@ -8,18 +8,34 @@ using System.Drawing; using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; +using System.Diagnostics; + namespace DS4Windows { [Flags] - public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; //Increment by exponents of 2*, starting at 2^0 + public enum DS4KeyType : byte { None = 0, ScanCode = 1, Toggle = 2, Unbound = 4, Macro = 8, HoldMacro = 16, RepeatMacro = 32 }; // Increment by exponents of 2*, starting at 2^0 public enum Ds3PadId : byte { None = 0xFF, One = 0x00, Two = 0x01, Three = 0x02, Four = 0x03, All = 0x04 }; public enum DS4Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, L1, L2, L3, R1, R2, R3, Square, Triangle, Circle, Cross, DpadUp, DpadRight, DpadDown, DpadLeft, PS, TouchLeft, TouchUpper, TouchMulti, TouchRight, Share, Options, GyroXPos, GyroXNeg, GyroZPos, GyroZNeg, SwipeLeft, SwipeRight, SwipeUp, SwipeDown }; public enum X360Controls : byte { None, LXNeg, LXPos, LYNeg, LYPos, RXNeg, RXPos, RYNeg, RYPos, LB, LT, LS, RB, RT, RS, X, Y, B, A, DpadUp, DpadRight, DpadDown, DpadLeft, Guide, Back, Start, LeftMouse, RightMouse, MiddleMouse, FourthMouse, FifthMouse, WUP, WDOWN, MouseUp, MouseDown, MouseLeft, MouseRight, Unbound }; + public enum SASteeringWheelEmulationAxisType: byte { None = 0, LX, LY, RX, RY, L2R2, VJoy1X, VJoy1Y, VJoy1Z, VJoy2X, VJoy2Y, VJoy2Z }; + public enum OutContType : uint { None = 0, X360, DS4 } + + public enum GyroOutMode : uint + { + None, + Controls, + Mouse, + MouseJoystick, + } + public class DS4ControlSettings { public DS4Controls control; - public string extras = "0,0,0,0,0,0,0,0"; + public string extras = null; public DS4KeyType keyType = DS4KeyType.None; public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType actionType = ActionType.Default; @@ -27,7 +43,7 @@ public enum ActionType : byte { Default, Key, Button, Macro }; public ActionType shiftActionType = ActionType.Default; public object shiftAction = null; public int shiftTrigger = 0; - public string shiftExtras = "0,0,0,0,0,0,0,0"; + public string shiftExtras = null; public DS4KeyType shiftKeyType = DS4KeyType.None; public DS4ControlSettings(DS4Controls ctrl) @@ -37,17 +53,17 @@ public DS4ControlSettings(DS4Controls ctrl) public void Reset() { - extras = "0,0,0,0,0,0,0,0"; + extras = null; keyType = DS4KeyType.None; actionType = ActionType.Default; action = null; shiftActionType = ActionType.Default; shiftAction = null; shiftTrigger = 0; - shiftExtras = "0,0,0,0,0,0,0,0"; + shiftExtras = null; shiftKeyType = DS4KeyType.None; } - + internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, int trigger = 0) { if (!shift) @@ -60,6 +76,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, actionType = ActionType.Macro; else actionType = ActionType.Default; + action = act; extras = exts; keyType = kt; @@ -74,6 +91,7 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, shiftActionType = ActionType.Macro; else shiftActionType = ActionType.Default; + shiftAction = act; shiftExtras = exts; shiftKeyType = kt; @@ -85,16 +103,16 @@ internal void UpdateSettings(bool shift, object act, string exts, DS4KeyType kt, public class DebugEventArgs : EventArgs { protected DateTime m_Time = DateTime.Now; - protected String m_Data = String.Empty; + protected string m_Data = string.Empty; protected bool warning = false; - public DebugEventArgs(String Data, bool warn) + public DebugEventArgs(string Data, bool warn) { m_Data = Data; warning = warn; } public DateTime Time => m_Time; - public String Data => m_Data; + public string Data => m_Data; public bool Warning => warning; } @@ -113,7 +131,7 @@ public MappingDoneEventArgs(int DeviceID) public class ReportEventArgs : EventArgs { protected Ds3PadId m_Pad = Ds3PadId.None; - protected Byte[] m_Report = new Byte[64]; + protected byte[] m_Report = new byte[64]; public ReportEventArgs() { @@ -136,20 +154,337 @@ public Byte[] Report } } + public class BatteryReportArgs : EventArgs + { + private int index; + private int level; + private bool charging; + + public BatteryReportArgs(int index, int level, bool charging) + { + this.index = index; + this.level = level; + this.charging = charging; + } + + public int getIndex() + { + return index; + } + + public int getLevel() + { + return level; + } + + public bool isCharging() + { + return charging; + } + } + + public class ControllerRemovedArgs : EventArgs + { + private int index; + + public ControllerRemovedArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return this.index; + } + } + + public class DeviceStatusChangeEventArgs : EventArgs + { + private int index; + + public DeviceStatusChangeEventArgs(int index) + { + this.index = index; + } + + public int getIndex() + { + return index; + } + } + + public class SerialChangeArgs : EventArgs + { + private int index; + private string serial; + + public SerialChangeArgs(int index, string serial) + { + this.index = index; + this.serial = serial; + } + + public int getIndex() + { + return index; + } + + public string getSerial() + { + return serial; + } + } + public class Global { protected static BackingStore m_Config = new BackingStore(); protected static Int32 m_IdleTimeout = 600000; - static string exepath = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName; + public static string exelocation = Assembly.GetExecutingAssembly().Location; + public static string exedirpath = Directory.GetParent(exelocation).FullName; + public static FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(exelocation); + public static string exeversion = fileVersion.ProductVersion; public static string appdatapath; + public static bool firstRun = false; + public static bool multisavespots = false; + public static string appDataPpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Windows"; + public static bool runHotPlug = false; public static string[] tempprofilename = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public static bool[] useTempProfile = new bool[5] { false, false, false, false, false }; + public static bool[] tempprofileDistance = new bool[5] { false, false, false, false, false }; + public static bool[] useDInputOnly = new bool[5] { true, true, true, true, true }; + public static bool[] linkedProfileCheck = new bool[4] { false, false, false, false }; + public static bool[] touchpadActive = new bool[5] { true, true, true, true, true }; + // Used to hold device type desired from Profile Editor + public static OutContType[] outDevTypeTemp = new OutContType[5] { DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360, DS4Windows.OutContType.X360, + DS4Windows.OutContType.X360 }; + // Used to hold the currently active controller output type in use for a slot + public static OutContType[] activeOutDevType = new OutContType[5] { DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None, DS4Windows.OutContType.None, + DS4Windows.OutContType.None }; + public static bool vigemInstalled = IsViGEmBusInstalled(); + public static bool hidguardInstalled = IsHidGuardianInstalled(); + public static string vigembusVersion = ViGEmBusVersion(); + + public static X360Controls[] defaultButtonMapping = { X360Controls.None, X360Controls.LXNeg, X360Controls.LXPos, + X360Controls.LYNeg, X360Controls.LYPos, X360Controls.RXNeg, X360Controls.RXPos, X360Controls.RYNeg, X360Controls.RYPos, + X360Controls.LB, X360Controls.LT, X360Controls.LS, X360Controls.RB, X360Controls.RT, X360Controls.RS, X360Controls.X, + X360Controls.Y, X360Controls.B, X360Controls.A, X360Controls.DpadUp, X360Controls.DpadRight, X360Controls.DpadDown, + X360Controls.DpadLeft, X360Controls.Guide, X360Controls.LeftMouse, X360Controls.MiddleMouse, X360Controls.RightMouse, X360Controls.LeftMouse, + X360Controls.Back, X360Controls.Start, X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None, + X360Controls.None, X360Controls.None, X360Controls.None, X360Controls.None + }; + + // Create mapping array at runtime + public static DS4Controls[] reverseX360ButtonMapping = new Func(() => + { + DS4Controls[] temp = new DS4Controls[defaultButtonMapping.Length]; + for (int i = 0, arlen = defaultButtonMapping.Length; i < arlen; i++) + { + X360Controls mapping = defaultButtonMapping[i]; + if (mapping != X360Controls.None) + { + temp[(int)mapping] = (DS4Controls)i; + } + } + + return temp; + })(); + + public static Dictionary xboxDefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "Left Bumper", + [X360Controls.LT] = "Left Trigger", + [X360Controls.LS] = "Left Stick", + [X360Controls.RB] = "Right Bumper", + [X360Controls.RT] = "Right Trigger", + [X360Controls.RS] = "Right Stick", + [X360Controls.X] = "X Button", + [X360Controls.Y] = "Y Button", + [X360Controls.B] = "B Button", + [X360Controls.A] = "A Button", + [X360Controls.DpadUp] = "Up Button", + [X360Controls.DpadRight] = "Right Button", + [X360Controls.DpadDown] = "Down Button", + [X360Controls.DpadLeft] = "Left Button", + [X360Controls.Guide] = "Guide", + [X360Controls.Back] = "Back", + [X360Controls.Start] = "Start", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static Dictionary ds4DefaultNames = new Dictionary() + { + [X360Controls.LXNeg] = "Left X-Axis-", + [X360Controls.LXPos] = "Left X-Axis+", + [X360Controls.LYNeg] = "Left Y-Axis-", + [X360Controls.LYPos] = "Left Y-Axis+", + [X360Controls.RXNeg] = "Right X-Axis-", + [X360Controls.RXPos] = "Right X-Axis+", + [X360Controls.RYNeg] = "Right Y-Axis-", + [X360Controls.RYPos] = "Right Y-Axis+", + [X360Controls.LB] = "L1", + [X360Controls.LT] = "L2", + [X360Controls.LS] = "L3", + [X360Controls.RB] = "R1", + [X360Controls.RT] = "R2", + [X360Controls.RS] = "R3", + [X360Controls.X] = "Square", + [X360Controls.Y] = "Triangle", + [X360Controls.B] = "Circle", + [X360Controls.A] = "Cross", + [X360Controls.DpadUp] = "Dpad Up", + [X360Controls.DpadRight] = "Dpad Right", + [X360Controls.DpadDown] = "Dpad Down", + [X360Controls.DpadLeft] = "Dpad Left", + [X360Controls.Guide] = "PS", + [X360Controls.Back] = "Share", + [X360Controls.Start] = "Options", + [X360Controls.LeftMouse] = "Left Mouse Button", + [X360Controls.RightMouse] = "Right Mouse Button", + [X360Controls.MiddleMouse] = "Middle Mouse Button", + [X360Controls.FourthMouse] = "4th Mouse Button", + [X360Controls.FifthMouse] = "5th Mouse Button", + [X360Controls.WUP] = "Mouse Wheel Up", + [X360Controls.WDOWN] = "Mouse Wheel Down", + [X360Controls.MouseUp] = "Mouse Up", + [X360Controls.MouseDown] = "Mouse Down", + [X360Controls.MouseLeft] = "Mouse Left", + [X360Controls.MouseRight] = "Mouse Right", + [X360Controls.Unbound] = "Unbound", + }; + + public static string getX360ControlString(X360Controls key, OutContType conType) + { + string result = string.Empty; + if (conType == DS4Windows.OutContType.X360) + { + xboxDefaultNames.TryGetValue(key, out result); + } + else if (conType == DS4Windows.OutContType.DS4) + { + ds4DefaultNames.TryGetValue(key, out result); + } + + return result; + } + + public static Dictionary ds4inputNames = new Dictionary() + { + [DS4Controls.LXNeg] = "Left X-Axis-", + [DS4Controls.LXPos] = "Left X-Axis+", + [DS4Controls.LYNeg] = "Left Y-Axis-", + [DS4Controls.LYPos] = "Left Y-Axis+", + [DS4Controls.RXNeg] = "Right X-Axis-", + [DS4Controls.RXPos] = "Right X-Axis+", + [DS4Controls.RYNeg] = "Right Y-Axis-", + [DS4Controls.RYPos] = "Right Y-Axis+", + [DS4Controls.L1] = "L1", + [DS4Controls.L2] = "L2", + [DS4Controls.L3] = "L3", + [DS4Controls.R1] = "R1", + [DS4Controls.R2] = "R2", + [DS4Controls.R3] = "R3", + [DS4Controls.Square] = "Square", + [DS4Controls.Triangle] = "Triangle", + [DS4Controls.Circle] = "Circle", + [DS4Controls.Cross] = "Cross", + [DS4Controls.DpadUp] = "Dpad Up", + [DS4Controls.DpadRight] = "Dpad Right", + [DS4Controls.DpadDown] = "Dpad Down", + [DS4Controls.DpadLeft] = "Dpad Left", + [DS4Controls.PS] = "PS", + [DS4Controls.Share] = "Share", + [DS4Controls.Options] = "Options", + [DS4Controls.TouchLeft] = "Left Touch", + [DS4Controls.TouchUpper] = "Upper Touch", + [DS4Controls.TouchMulti] = "Multitouch", + [DS4Controls.TouchRight] = "Right Touch", + [DS4Controls.GyroXPos] = "Gyro X+", + [DS4Controls.GyroXNeg] = "Gyro X-", + [DS4Controls.GyroZPos] = "Gyro Z+", + [DS4Controls.GyroZNeg] = "Gyro Z-", + [DS4Controls.SwipeLeft] = "Swipe Left", + [DS4Controls.SwipeRight] = "Swipe Right", + [DS4Controls.SwipeUp] = "Swipe Up", + [DS4Controls.SwipeDown] = "Swipe Down", + }; + + public static Dictionary macroDS4Values = new Dictionary() + { + [DS4Controls.Cross] = 261, [DS4Controls.Circle] = 262, + [DS4Controls.Square] = 263, [DS4Controls.Triangle] = 264, + [DS4Controls.Options] = 265, [DS4Controls.Share] = 266, + [DS4Controls.DpadUp] = 267, [DS4Controls.DpadDown] = 268, + [DS4Controls.DpadLeft] = 269, [DS4Controls.DpadRight] = 270, + [DS4Controls.PS] = 271, [DS4Controls.L1] = 272, + [DS4Controls.R1] = 273, [DS4Controls.L2] = 274, + [DS4Controls.R2] = 275, [DS4Controls.L3] = 276, + [DS4Controls.R3] = 277, [DS4Controls.LXPos] = 278, + [DS4Controls.LXNeg] = 279, [DS4Controls.LYPos] = 280, + [DS4Controls.LYNeg] = 281, [DS4Controls.RXPos] = 282, + [DS4Controls.RXNeg] = 283, [DS4Controls.RYPos] = 284, + [DS4Controls.RYNeg] = 285, + }; public static void SaveWhere(string path) { appdatapath = path; m_Config.m_Profile = appdatapath + "\\Profiles.xml"; m_Config.m_Actions = appdatapath + "\\Actions.xml"; + m_Config.m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + m_Config.m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; + } + + public static bool SaveDefault(string path) + { + Boolean Saved = true; + XmlDocument m_Xdoc = new XmlDocument(); + try + { + XmlNode Node; + + m_Xdoc.RemoveAll(); + + Node = m_Xdoc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateComment(string.Format(" Profile Configuration Data. {0} ", DateTime.Now)); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateWhitespace("\r\n"); + m_Xdoc.AppendChild(Node); + + Node = m_Xdoc.CreateNode(XmlNodeType.Element, "Profile", null); + + m_Xdoc.AppendChild(Node); + + m_Xdoc.Save(path); + } + catch { Saved = false; } + + return Saved; } + /// /// Check if Admin Rights are needed to write in Appliplation Directory /// @@ -158,8 +493,8 @@ public static bool AdminNeeded() { try { - File.WriteAllText(exepath + "\\test.txt", "test"); - File.Delete(exepath + "\\test.txt"); + File.WriteAllText(exedirpath + "\\test.txt", "test"); + File.Delete(exedirpath + "\\test.txt"); return false; } catch (UnauthorizedAccessException) @@ -175,6 +510,230 @@ public static bool IsAdministrator() return principal.IsInRole(WindowsBuiltInRole.Administrator); } + public static bool CheckForDevice(string guid) + { + bool result = false; + Guid deviceGuid = Guid.Parse(guid); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + result = NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static bool CheckForSysDevice(string searchHardwareId) + { + bool result = false; + Guid sysGuid = Guid.Parse("{4d36e97d-e325-11ce-bfc1-08002be10318}"); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref sysGuid, null, 0, 0); + for (int i = 0; !result && NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++) + { + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, + ref NativeMethods.DEVPKEY_Device_HardwareIds, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + string hardwareId = dataBuffer.ToUTF16String(); + //if (hardwareIds.Contains("Virtual Gamepad Emulation Bus")) + // result = true; + if (hardwareId.Equals(searchHardwareId)) + result = true; + } + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + internal static string GetDeviceProperty(string deviceInstanceId, + NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + NativeMethods.SP_DEVINFO_DATA deviceInfoData = new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + Guid hidGuid = new Guid(); + NativeMethods.HidD_GetHidGuid(ref hidGuid); + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + private static string GetViGEmDriverProperty(NativeMethods.DEVPROPKEY prop) + { + string result = string.Empty; + Guid deviceGuid = Guid.Parse(VIGEMBUS_GUID); + NativeMethods.SP_DEVINFO_DATA deviceInfoData = + new NativeMethods.SP_DEVINFO_DATA(); + deviceInfoData.cbSize = + System.Runtime.InteropServices.Marshal.SizeOf(deviceInfoData); + + var dataBuffer = new byte[4096]; + ulong propertyType = 0; + var requiredSize = 0; + + IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref deviceGuid, null, 0, + NativeMethods.DIGCF_DEVICEINTERFACE); + NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); + if (NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref deviceInfoData, ref prop, ref propertyType, + dataBuffer, dataBuffer.Length, ref requiredSize, 0)) + { + result = dataBuffer.ToUTF16String(); + } + + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) + { + NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return result; + } + + public static bool IsHidGuardianInstalled() + { + return CheckForSysDevice(@"Root\HidGuardian"); + } + + const string VIGEMBUS_GUID = "{96E42B22-F5E9-42F8-B043-ED0F932F014F}"; + public static bool IsViGEmBusInstalled() + { + return CheckForDevice(VIGEMBUS_GUID); + } + + public static string ViGEmBusVersion() + { + return GetViGEmDriverProperty(NativeMethods.DEVPKEY_Device_DriverVersion); + } + + public static void FindConfigLocation() + { + if (File.Exists(exedirpath + "\\Auto Profiles.xml") + && File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = true; + } + else if (File.Exists(exedirpath + "\\Auto Profiles.xml")) + SaveWhere(exedirpath); + else if (File.Exists(appDataPpath + "\\Auto Profiles.xml")) + SaveWhere(appDataPpath); + else if (!File.Exists(exedirpath + "\\Auto Profiles.xml") + && !File.Exists(appDataPpath + "\\Auto Profiles.xml")) + { + Global.firstRun = true; + Global.multisavespots = false; + } + } + + public static void SetCulture(string culture) + { + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo(culture); + } + catch { /* Skip setting culture that we cannot set */ } + } + + public static void CreateStdActions() + { + XmlDocument xDoc = new XmlDocument(); + try + { + string[] profiles = Directory.GetFiles(appdatapath + @"\Profiles\"); + string s = string.Empty; + //foreach (string s in profiles) + for (int i = 0, proflen = profiles.Length; i < proflen; i++) + { + s = profiles[i]; + if (Path.GetExtension(s) == ".xml") + { + xDoc.Load(s); + XmlNode el = xDoc.SelectSingleNode("DS4Windows/ProfileActions"); + if (el != null) + { + if (string.IsNullOrEmpty(el.InnerText)) + el.InnerText = "Disconnect Controller"; + else + el.InnerText += "/Disconnect Controller"; + } + else + { + XmlNode Node = xDoc.SelectSingleNode("DS4Windows"); + el = xDoc.CreateElement("ProfileActions"); + el.InnerText = "Disconnect Controller"; + Node.AppendChild(el); + } + + xDoc.Save(s); + LoadActions(); + } + } + } + catch { } + } + + public static bool CreateAutoProfiles(string m_Profile) + { + bool Saved = true; + + try + { + XmlNode Node; + XmlDocument doc = new XmlDocument(); + + Node = doc.CreateXmlDeclaration("1.0", "utf-8", String.Empty); + doc.AppendChild(Node); + + Node = doc.CreateComment(string.Format(" Auto-Profile Configuration Data. {0} ", DateTime.Now)); + doc.AppendChild(Node); + + Node = doc.CreateWhitespace("\r\n"); + doc.AppendChild(Node); + + Node = doc.CreateNode(XmlNodeType.Element, "Programs", ""); + doc.AppendChild(Node); + doc.Save(m_Profile); + } + catch { Saved = false; } + + return Saved; + } + public static event EventHandler ControllerStatusChange; // called when a controller is added/removed/battery or touchpad mode changes/etc. public static void ControllerStatusChanged(object sender) { @@ -182,13 +741,58 @@ public static void ControllerStatusChanged(object sender) ControllerStatusChange(sender, EventArgs.Empty); } - //general values + public static event EventHandler BatteryStatusChange; + public static void OnBatteryStatusChange(object sender, int index, int level, bool charging) + { + if (BatteryStatusChange != null) + { + BatteryReportArgs args = new BatteryReportArgs(index, level, charging); + BatteryStatusChange(sender, args); + } + } + + public static event EventHandler ControllerRemoved; + public static void OnControllerRemoved(object sender, int index) + { + if (ControllerRemoved != null) + { + ControllerRemovedArgs args = new ControllerRemovedArgs(index); + ControllerRemoved(sender, args); + } + } + + public static event EventHandler DeviceStatusChange; + public static void OnDeviceStatusChanged(object sender, int index) + { + if (DeviceStatusChange != null) + { + DeviceStatusChangeEventArgs args = new DeviceStatusChangeEventArgs(index); + DeviceStatusChange(sender, args); + } + } + + public static event EventHandler DeviceSerialChange; + public static void OnDeviceSerialChange(object sender, int index, string serial) + { + if (DeviceSerialChange != null) + { + SerialChangeArgs args = new SerialChangeArgs(index, serial); + DeviceSerialChange(sender, args); + } + } + + // general values public static bool UseExclusiveMode { set { m_Config.useExclusiveMode = value; } get { return m_Config.useExclusiveMode; } } + public static bool getUseExclusiveMode() + { + return m_Config.useExclusiveMode; + } + public static DateTime LastChecked { set { m_Config.lastChecked = value; } @@ -200,146 +804,826 @@ public static int CheckWhen set { m_Config.CheckWhen = value; } get { return m_Config.CheckWhen; } } + public static int Notifications { set { m_Config.notifications = value; } get { return m_Config.notifications; } } + public static bool DCBTatStop { set { m_Config.disconnectBTAtStop = value; } get { return m_Config.disconnectBTAtStop; } } + public static bool SwipeProfiles { set { m_Config.swipeProfiles = value; } get { return m_Config.swipeProfiles; } } + public static bool DS4Mapping { set { m_Config.ds4Mapping = value; } get { return m_Config.ds4Mapping; } } + public static bool QuickCharge { set { m_Config.quickCharge = value; } get { return m_Config.quickCharge; } } - public static int FirstXinputPort + + public static bool getQuickCharge() { - set { m_Config.firstXinputPort = value; } - get { return m_Config.firstXinputPort; } + return m_Config.quickCharge; } + public static bool CloseMini { set { m_Config.closeMini = value; } get { return m_Config.closeMini; } } + public static bool StartMinimized { set { m_Config.startMinimized = value; } get { return m_Config.startMinimized; } } + + public static bool MinToTaskbar + { + set { m_Config.minToTaskbar = value; } + get { return m_Config.minToTaskbar; } + } + + public static bool GetMinToTaskbar() + { + return m_Config.minToTaskbar; + } + public static int FormWidth { set { m_Config.formWidth = value; } - get { return m_Config.formWidth;} + get { return m_Config.formWidth; } } + public static int FormHeight { set { m_Config.formHeight = value; } get { return m_Config.formHeight; } } + + public static int FormLocationX + { + set { m_Config.formLocationX = value; } + get { return m_Config.formLocationX; } + } + + public static int FormLocationY + { + set { m_Config.formLocationY = value; } + get { return m_Config.formLocationY; } + } + + public static string UseLang + { + set { m_Config.useLang = value; } + get { return m_Config.useLang; } + } + public static bool DownloadLang { set { m_Config.downloadLang = value; } get { return m_Config.downloadLang; } } + public static bool FlashWhenLate { set { m_Config.flashWhenLate = value; } get { return m_Config.flashWhenLate; } } + + public static bool getFlashWhenLate() + { + return m_Config.flashWhenLate; + } + public static int FlashWhenLateAt { set { m_Config.flashWhenLateAt = value; } get { return m_Config.flashWhenLateAt; } } - public static bool UseWhiteIcon + + public static int getFlashWhenLateAt() + { + return m_Config.flashWhenLateAt; + } + + public static bool isUsingUDPServer() + { + return m_Config.useUDPServ; + } + public static void setUsingUDPServer(bool state) + { + m_Config.useUDPServ = state; + } + + public static int getUDPServerPortNum() + { + return m_Config.udpServPort; + } + public static void setUDPServerPort(int value) + { + m_Config.udpServPort = value; + } + + public static string getUDPServerListenAddress() + { + return m_Config.udpServListenAddress; + } + public static void setUDPServerListenAddress(string value) + { + m_Config.udpServListenAddress = value.Trim(); + } + + public static bool UseWhiteIcon { set { m_Config.useWhiteIcon = value; } get { return m_Config.useWhiteIcon; } } - //controller/profile specfic values - public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; - public static byte[] RumbleBoost => m_Config.rumble; - public static double[] Rainbow => m_Config.rainbow; - public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; - public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; - public static byte[] TouchSensitivity => m_Config.touchSensitivity; - public static byte[] FlashType => m_Config.flashType; - public static int[] FlashAt => m_Config.flashAt; - public static bool[] LedAsBatteryIndicator => m_Config.ledAsBattery; - public static int[] ChargingType => m_Config.chargingType; - public static bool[] DinputOnly => m_Config.dinputOnly; - public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; - public static bool[] UseTPforControls => m_Config.useTPforControls; - public static bool[] UseSAforMouse => m_Config.useSAforMouse; - public static string[] SATriggers => m_Config.sATriggers; - public static int[] GyroSensitivity => m_Config.gyroSensitivity; - public static int[] GyroInvert => m_Config.gyroInvert; - public static DS4Color[] MainColor => m_Config.m_Leds; - public static DS4Color[] LowColor => m_Config.m_LowLeds; - public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; - public static DS4Color[] CustomColor => m_Config.m_CustomLeds; - public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool UseCustomSteamFolder + { + set { m_Config.useCustomSteamFolder = value; } + get { return m_Config.useCustomSteamFolder; } + } + + public static string CustomSteamFolder + { + set { m_Config.customSteamFolder = value; } + get { return m_Config.customSteamFolder; } + } + + public static bool AutoProfileRevertDefaultProfile + { + set { m_Config.autoProfileRevertDefaultProfile = value; } + get { return m_Config.autoProfileRevertDefaultProfile; } + } + + // controller/profile specfic values + public static int[] ButtonMouseSensitivity => m_Config.buttonMouseSensitivity; + + public static byte[] RumbleBoost => m_Config.rumble; + public static byte getRumbleBoost(int index) + { + return m_Config.rumble[index]; + } + + public static void setRumbleAutostopTime(int index, int value) + { + m_Config.rumbleAutostopTime[index] = value; + + DS4Device tempDev = Program.rootHub.DS4Controllers[index]; + if (tempDev != null && tempDev.isSynced()) + tempDev.RumbleAutostopTime = value; + } + + public static int getRumbleAutostopTime(int index) + { + return m_Config.rumbleAutostopTime[index]; + } + + public static bool[] FlushHIDQueue => m_Config.flushHIDQueue; + public static bool getFlushHIDQueue(int index) + { + return m_Config.flushHIDQueue[index]; + } + + public static bool[] EnableTouchToggle => m_Config.enableTouchToggle; + public static bool getEnableTouchToggle(int index) + { + return m_Config.enableTouchToggle[index]; + } + + public static int[] IdleDisconnectTimeout => m_Config.idleDisconnectTimeout; + public static int getIdleDisconnectTimeout(int index) + { + return m_Config.idleDisconnectTimeout[index]; + } + + public static byte[] TouchSensitivity => m_Config.touchSensitivity; + public static byte[] getTouchSensitivity() + { + return m_Config.touchSensitivity; + } + + public static byte getTouchSensitivity(int index) + { + return m_Config.touchSensitivity[index]; + } + + public static bool[] TouchActive => touchpadActive; + public static bool GetTouchActive(int index) + { + return touchpadActive[index]; + } + + public static LightbarSettingInfo[] LightbarSettingsInfo => m_Config.lightbarSettingInfo; + public static LightbarSettingInfo getLightbarSettingsInfo(int index) + { + return m_Config.lightbarSettingInfo[index]; + } + + public static bool[] DinputOnly => m_Config.dinputOnly; + public static bool getDInputOnly(int index) + { + return m_Config.dinputOnly[index]; + } + + public static bool[] StartTouchpadOff => m_Config.startTouchpadOff; + + public static bool[] UseTPforControls => m_Config.useTPforControls; + public static bool getUseTPforControls(int index) + { + return m_Config.useTPforControls[index]; + } + + public static bool[] UseSAforMouse => m_Config.useSAforMouse; + public static bool isUsingSAforMouse(int index) + { + return m_Config.gyroOutMode[index] == DS4Windows.GyroOutMode.Mouse; + } + + public static string[] SATriggers => m_Config.sATriggers; + public static string getSATriggers(int index) + { + return m_Config.sATriggers[index]; + } + + public static bool[] SATriggerCond => m_Config.sATriggerCond; + public static bool getSATriggerCond(int index) + { + return m_Config.sATriggerCond[index]; + } + public static void SetSaTriggerCond(int index, string text) + { + m_Config.SetSaTriggerCond(index, text); + } + + + public static GyroOutMode[] GyroOutputMode => m_Config.gyroOutMode; + public static GyroOutMode GetGyroOutMode(int device) + { + return m_Config.gyroOutMode[device]; + } + + public static string[] SAMousestickTriggers => m_Config.sAMouseStickTriggers; + public static string GetSAMouseStickTriggers(int device) + { + return m_Config.sAMouseStickTriggers[device]; + } + + public static bool[] SAMouseStickTriggerCond => m_Config.sAMouseStickTriggerCond; + public static bool GetSAMouseStickTriggerCond(int device) + { + return m_Config.sAMouseStickTriggerCond[device]; + } + public static void SetSaMouseStickTriggerCond(int index, string text) + { + m_Config.SetSaMouseStickTriggerCond(index, text); + } + + public static bool[] GyroMouseStickTriggerTurns = m_Config.gyroMouseStickTriggerTurns; + public static bool GetGyroMouseStickTriggerTurns(int device) + { + return m_Config.gyroMouseStickTriggerTurns[device]; + } + + public static int[] GyroMouseStickHorizontalAxis => + m_Config.gyroMouseStickHorizontalAxis; + public static int getGyroMouseStickHorizontalAxis(int index) + { + return m_Config.gyroMouseStickHorizontalAxis[index]; + } + + public static GyroMouseStickInfo[] GyroMouseStickInf => m_Config.gyroMStickInfo; + public static GyroMouseStickInfo GetGyroMouseStickInfo(int device) + { + return m_Config.gyroMStickInfo[device]; + } + + public static bool[] GyroMouseStickToggle => m_Config.gyroMouseStickToggle; + public static void SetGyroMouseStickToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseStickToggle(index, value, control); + + public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis; + public static SASteeringWheelEmulationAxisType GetSASteeringWheelEmulationAxis(int index) + { + return m_Config.sASteeringWheelEmulationAxis[index]; + } + + public static int[] SASteeringWheelEmulationRange => m_Config.sASteeringWheelEmulationRange; + public static int GetSASteeringWheelEmulationRange(int index) + { + return m_Config.sASteeringWheelEmulationRange[index]; + } + + public static int[][] TouchDisInvertTriggers => m_Config.touchDisInvertTriggers; + public static int[] getTouchDisInvertTriggers(int index) + { + return m_Config.touchDisInvertTriggers[index]; + } + + public static int[] GyroSensitivity => m_Config.gyroSensitivity; + public static int getGyroSensitivity(int index) + { + return m_Config.gyroSensitivity[index]; + } + + public static int[] GyroSensVerticalScale => m_Config.gyroSensVerticalScale; + public static int getGyroSensVerticalScale(int index) + { + return m_Config.gyroSensVerticalScale[index]; + } + + public static int[] GyroInvert => m_Config.gyroInvert; + public static int getGyroInvert(int index) + { + return m_Config.gyroInvert[index]; + } + + public static bool[] GyroTriggerTurns => m_Config.gyroTriggerTurns; + public static bool getGyroTriggerTurns(int index) + { + return m_Config.gyroTriggerTurns[index]; + } + + public static bool[] GyroSmoothing => m_Config.gyroSmoothing; + public static bool getGyroSmoothing(int index) + { + return m_Config.gyroSmoothing[index]; + } + + public static double[] GyroSmoothingWeight => m_Config.gyroSmoothWeight; + public static double getGyroSmoothingWeight(int index) + { + return m_Config.gyroSmoothWeight[index]; + } + + public static int[] GyroMouseHorizontalAxis => m_Config.gyroMouseHorizontalAxis; + public static int getGyroMouseHorizontalAxis(int index) + { + return m_Config.gyroMouseHorizontalAxis[index]; + } + + public static int[] GyroMouseDeadZone => m_Config.gyroMouseDZ; + public static int GetGyroMouseDeadZone(int index) + { + return m_Config.gyroMouseDZ[index]; + } + + public static void SetGyroMouseDeadZone(int index, int value, ControlService control) + { + m_Config.SetGyroMouseDZ(index, value, control); + } + + public static bool[] GyroMouseToggle => m_Config.gyroMouseToggle; + public static void SetGyroMouseToggle(int index, bool value, ControlService control) + => m_Config.SetGyroMouseToggle(index, value, control); + + //public static DS4Color[] MainColor => m_Config.m_Leds; + public static ref DS4Color getMainColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_Led; + //return ref m_Config.m_Leds[index]; + } + + //public static DS4Color[] LowColor => m_Config.m_LowLeds; + public static ref DS4Color getLowColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_LowLed; + //return ref m_Config.m_LowLeds[index]; + } + + //public static DS4Color[] ChargingColor => m_Config.m_ChargingLeds; + public static ref DS4Color getChargingColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_ChargingLed; + //return ref m_Config.m_ChargingLeds[index]; + } + + //public static DS4Color[] CustomColor => m_Config.m_CustomLeds; + public static ref DS4Color getCustomColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_CustomLed; + //return ref m_Config.m_CustomLeds[index]; + } + + //public static bool[] UseCustomLed => m_Config.useCustomLeds; + public static bool getUseCustomLed(int index) + { + return m_Config.lightbarSettingInfo[index].ds4winSettings.useCustomLed; + //return m_Config.useCustomLeds[index]; + } + + //public static DS4Color[] FlashColor => m_Config.m_FlashLeds; + public static ref DS4Color getFlashColor(int index) + { + return ref m_Config.lightbarSettingInfo[index].ds4winSettings.m_FlashLed; + //return ref m_Config.m_FlashLeds[index]; + } + + public static byte[] TapSensitivity => m_Config.tapSensitivity; + public static byte getTapSensitivity(int index) + { + return m_Config.tapSensitivity[index]; + } + + public static bool[] DoubleTap => m_Config.doubleTap; + public static bool getDoubleTap(int index) + { + return m_Config.doubleTap[index]; + } + + public static int[] ScrollSensitivity => m_Config.scrollSensitivity; + public static int[] getScrollSensitivity() + { + return m_Config.scrollSensitivity; + } + public static int getScrollSensitivity(int index) + { + return m_Config.scrollSensitivity[index]; + } + + public static bool[] LowerRCOn => m_Config.lowerRCOn; + public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static bool getTouchpadJitterCompensation(int index) + { + return m_Config.touchpadJitterCompensation[index]; + } + + public static int[] TouchpadInvert => m_Config.touchpadInvert; + public static int getTouchpadInvert(int index) + { + return m_Config.touchpadInvert[index]; + } + + public static TriggerDeadZoneZInfo[] L2ModInfo => m_Config.l2ModInfo; + public static TriggerDeadZoneZInfo GetL2ModInfo(int index) + { + return m_Config.l2ModInfo[index]; + } + + //public static byte[] L2Deadzone => m_Config.l2Deadzone; + public static byte getL2Deadzone(int index) + { + return m_Config.l2ModInfo[index].deadZone; + //return m_Config.l2Deadzone[index]; + } + + public static TriggerDeadZoneZInfo[] R2ModInfo => m_Config.r2ModInfo; + public static TriggerDeadZoneZInfo GetR2ModInfo(int index) + { + return m_Config.r2ModInfo[index]; + } + + //public static byte[] R2Deadzone => m_Config.r2Deadzone; + public static byte getR2Deadzone(int index) + { + return m_Config.r2ModInfo[index].deadZone; + //return m_Config.r2Deadzone[index]; + } + + public static double[] SXDeadzone => m_Config.SXDeadzone; + public static double getSXDeadzone(int index) + { + return m_Config.SXDeadzone[index]; + } + + public static double[] SZDeadzone => m_Config.SZDeadzone; + public static double getSZDeadzone(int index) + { + return m_Config.SZDeadzone[index]; + } + + //public static int[] LSDeadzone => m_Config.LSDeadzone; + public static int getLSDeadzone(int index) + { + return m_Config.lsModInfo[index].deadZone; + //return m_Config.LSDeadzone[index]; + } + + //public static int[] RSDeadzone => m_Config.RSDeadzone; + public static int getRSDeadzone(int index) + { + return m_Config.rsModInfo[index].deadZone; + //return m_Config.RSDeadzone[index]; + } + + //public static int[] LSAntiDeadzone => m_Config.LSAntiDeadzone; + public static int getLSAntiDeadzone(int index) + { + return m_Config.lsModInfo[index].antiDeadZone; + //return m_Config.LSAntiDeadzone[index]; + } + + //public static int[] RSAntiDeadzone => m_Config.RSAntiDeadzone; + public static int getRSAntiDeadzone(int index) + { + return m_Config.rsModInfo[index].antiDeadZone; + //return m_Config.RSAntiDeadzone[index]; + } + + public static StickDeadZoneInfo[] LSModInfo => m_Config.lsModInfo; + public static StickDeadZoneInfo GetLSDeadInfo(int index) + { + return m_Config.lsModInfo[index]; + } + + public static StickDeadZoneInfo[] RSModInfo => m_Config.rsModInfo; + public static StickDeadZoneInfo GetRSDeadInfo(int index) + { + return m_Config.rsModInfo[index]; + } + + public static double[] SXAntiDeadzone => m_Config.SXAntiDeadzone; + public static double getSXAntiDeadzone(int index) + { + return m_Config.SXAntiDeadzone[index]; + } + + public static double[] SZAntiDeadzone => m_Config.SZAntiDeadzone; + public static double getSZAntiDeadzone(int index) + { + return m_Config.SZAntiDeadzone[index]; + } + + //public static int[] LSMaxzone => m_Config.LSMaxzone; + public static int getLSMaxzone(int index) + { + return m_Config.lsModInfo[index].maxZone; + //return m_Config.LSMaxzone[index]; + } + + //public static int[] RSMaxzone => m_Config.RSMaxzone; + public static int getRSMaxzone(int index) + { + return m_Config.rsModInfo[index].maxZone; + //return m_Config.RSMaxzone[index]; + } + + public static double[] SXMaxzone => m_Config.SXMaxzone; + public static double getSXMaxzone(int index) + { + return m_Config.SXMaxzone[index]; + } + + public static double[] SZMaxzone => m_Config.SZMaxzone; + public static double getSZMaxzone(int index) + { + return m_Config.SZMaxzone[index]; + } + + //public static int[] L2AntiDeadzone => m_Config.l2AntiDeadzone; + public static int getL2AntiDeadzone(int index) + { + return m_Config.l2ModInfo[index].antiDeadZone; + //return m_Config.l2AntiDeadzone[index]; + } + + //public static int[] R2AntiDeadzone => m_Config.r2AntiDeadzone; + public static int getR2AntiDeadzone(int index) + { + return m_Config.r2ModInfo[index].antiDeadZone; + //return m_Config.r2AntiDeadzone[index]; + } + + //public static int[] L2Maxzone => m_Config.l2Maxzone; + public static int getL2Maxzone(int index) + { + return m_Config.l2ModInfo[index].maxZone; + //return m_Config.l2Maxzone[index]; + } + + //public static int[] R2Maxzone => m_Config.r2Maxzone; + public static int getR2Maxzone(int index) + { + return m_Config.r2ModInfo[index].maxZone; + //return m_Config.r2Maxzone[index]; + } + + public static int[] LSCurve => m_Config.lsCurve; + public static int getLSCurve(int index) + { + return m_Config.lsCurve[index]; + } + + public static int[] RSCurve => m_Config.rsCurve; + public static int getRSCurve(int index) + { + return m_Config.rsCurve[index]; + } + + public static double[] LSRotation => m_Config.LSRotation; + public static double getLSRotation(int index) + { + return m_Config.LSRotation[index]; + } + + public static double[] RSRotation => m_Config.RSRotation; + public static double getRSRotation(int index) + { + return m_Config.RSRotation[index]; + } + + public static double[] L2Sens => m_Config.l2Sens; + public static double getL2Sens(int index) + { + return m_Config.l2Sens[index]; + } + + public static double[] R2Sens => m_Config.r2Sens; + public static double getR2Sens(int index) + { + return m_Config.r2Sens[index]; + } + + public static double[] SXSens => m_Config.SXSens; + public static double getSXSens(int index) + { + return m_Config.SXSens[index]; + } + + public static double[] SZSens => m_Config.SZSens; + public static double getSZSens(int index) + { + return m_Config.SZSens[index]; + } + + public static double[] LSSens => m_Config.LSSens; + public static double getLSSens(int index) + { + return m_Config.LSSens[index]; + } + + public static double[] RSSens => m_Config.RSSens; + public static double getRSSens(int index) + { + return m_Config.RSSens[index]; + } + + public static bool[] MouseAccel => m_Config.mouseAccel; + public static bool getMouseAccel(int device) + { + return m_Config.mouseAccel[device]; + } + + public static int[] BTPollRate => m_Config.btPollRate; + public static int getBTPollRate(int index) + { + return m_Config.btPollRate[index]; + } + + public static SquareStickInfo[] SquStickInfo = m_Config.squStickInfo; + public static SquareStickInfo GetSquareStickInfo(int device) + { + return m_Config.squStickInfo[device]; + } + + public static void setLsOutCurveMode(int index, int value) + { + m_Config.setLsOutCurveMode(index, value); + } + public static int getLsOutCurveMode(int index) + { + return m_Config.getLsOutCurveMode(index); + } + public static BezierCurve[] lsOutBezierCurveObj => m_Config.lsOutBezierCurveObj; + + public static void setRsOutCurveMode(int index, int value) + { + m_Config.setRsOutCurveMode(index, value); + } + public static int getRsOutCurveMode(int index) + { + return m_Config.getRsOutCurveMode(index); + } + public static BezierCurve[] rsOutBezierCurveObj => m_Config.rsOutBezierCurveObj; - public static DS4Color[] FlashColor => m_Config.m_FlashLeds; - public static byte[] TapSensitivity => m_Config.tapSensitivity; - public static bool[] DoubleTap => m_Config.doubleTap; - public static int[] ScrollSensitivity => m_Config.scrollSensitivity; - public static bool[] LowerRCOn => m_Config.lowerRCOn; - public static bool[] TouchpadJitterCompensation => m_Config.touchpadJitterCompensation; + public static void setL2OutCurveMode(int index, int value) + { + m_Config.setL2OutCurveMode(index, value); + } + public static int getL2OutCurveMode(int index) + { + return m_Config.getL2OutCurveMode(index); + } + public static BezierCurve[] l2OutBezierCurveObj => m_Config.l2OutBezierCurveObj; - public static byte[] L2Deadzone => m_Config.l2Deadzone; - public static byte[] R2Deadzone => m_Config.r2Deadzone; - public static double[] SXDeadzone => m_Config.SXDeadzone; - public static double[] SZDeadzone => m_Config.SZDeadzone; - public static int[] LSDeadzone => m_Config.LSDeadzone; - public static int[] RSDeadzone => m_Config.RSDeadzone; - public static int[] LSCurve => m_Config.lsCurve; - public static int[] RSCurve => m_Config.rsCurve; - public static double[] L2Sens => m_Config.l2Sens; - public static double[] R2Sens => m_Config.r2Sens; - public static double[] SXSens => m_Config.SXSens; - public static double[] SZSens => m_Config.SZSens; - public static double[] LSSens => m_Config.LSSens; - public static double[] RSSens => m_Config.RSSens; - public static bool[] MouseAccel => m_Config.mouseAccel; + public static void setR2OutCurveMode(int index, int value) + { + m_Config.setR2OutCurveMode(index, value); + } + public static int getR2OutCurveMode(int index) + { + return m_Config.getR2OutCurveMode(index); + } + public static BezierCurve[] r2OutBezierCurveObj => m_Config.r2OutBezierCurveObj; + + public static void setSXOutCurveMode(int index, int value) + { + m_Config.setSXOutCurveMode(index, value); + } + public static int getSXOutCurveMode(int index) + { + return m_Config.getSXOutCurveMode(index); + } + public static BezierCurve[] sxOutBezierCurveObj => m_Config.sxOutBezierCurveObj; + + public static void setSZOutCurveMode(int index, int value) + { + m_Config.setSZOutCurveMode(index, value); + } + public static int getSZOutCurveMode(int index) + { + return m_Config.getSZOutCurveMode(index); + } + public static BezierCurve[] szOutBezierCurveObj => m_Config.szOutBezierCurveObj; + + public static bool[] TrackballMode => m_Config.trackballMode; + public static bool getTrackballMode(int index) + { + return m_Config.trackballMode[index]; + } + + public static double[] TrackballFriction => m_Config.trackballFriction; + public static double getTrackballFriction(int index) + { + return m_Config.trackballFriction[index]; + } + + public static OutContType[] OutContType => m_Config.outputDevType; public static string[] LaunchProgram => m_Config.launchProgram; public static string[] ProfilePath => m_Config.profilePath; + public static string[] OlderProfilePath => m_Config.olderProfilePath; + public static bool[] DistanceProfiles = m_Config.distanceProfiles; + public static List[] ProfileActions => m_Config.profileActions; + public static int getProfileActionCount(int index) + { + return m_Config.profileActionCount[index]; + } + + public static void calculateProfileActionCount(int index) + { + m_Config.profileActionCount[index] = m_Config.profileActions[index].Count; + } + + public static List getProfileActions(int index) + { + return m_Config.profileActions[index]; + } public static void UpdateDS4CSetting (int deviceNum, string buttonName, bool shift, object action, string exts, DS4KeyType kt, int trigger = 0) { m_Config.UpdateDS4CSetting(deviceNum, buttonName, shift, action, exts, kt, trigger); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } + public static void UpdateDS4Extra(int deviceNum, string buttonName, bool shift, string exts) { m_Config.UpdateDS4CExtra(deviceNum, buttonName, shift, exts); + m_Config.containsCustomAction[deviceNum] = m_Config.HasCustomActions(deviceNum); + m_Config.containsCustomExtras[deviceNum] = m_Config.HasCustomExtras(deviceNum); } - public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Action(deviceNum, buttonName, shift); + public static object GetDS4Action(int deviceNum, DS4Controls control, bool shift) => m_Config.GetDS4Action(deviceNum, control, shift); public static DS4KeyType GetDS4KeyType(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4KeyType(deviceNum, buttonName, shift); public static string GetDS4Extra(int deviceNum, string buttonName, bool shift) => m_Config.GetDS4Extra(deviceNum, buttonName, shift); public static int GetDS4STrigger(int deviceNum, string buttonName) => m_Config.GetDS4STrigger(deviceNum, buttonName); + public static int GetDS4STrigger(int deviceNum, DS4Controls control) => m_Config.GetDS4STrigger(deviceNum, control); public static List getDS4CSettings(int device) => m_Config.ds4settings[device]; public static DS4ControlSettings getDS4CSetting(int deviceNum, string control) => m_Config.getDS4CSetting(deviceNum, control); - public static bool HasCustomAction(int deviceNum) => m_Config.HasCustomActions(deviceNum); + public static DS4ControlSettings getDS4CSetting(int deviceNum, DS4Controls control) => m_Config.getDS4CSetting(deviceNum, control); + public static bool HasCustomActions(int deviceNum) => m_Config.HasCustomActions(deviceNum); public static bool HasCustomExtras(int deviceNum) => m_Config.HasCustomExtras(deviceNum); - public static void SaveAction(string name, string controls, int mode, string details, bool edit, string extras = "") + public static bool containsCustomAction(int deviceNum) + { + return m_Config.containsCustomAction[deviceNum]; + } + + public static bool containsCustomExtras(int deviceNum) + { + return m_Config.containsCustomExtras[deviceNum]; + } + + public static void SaveAction(string name, string controls, int mode, + string details, bool edit, string extras = "") { m_Config.SaveAction(name, controls, mode, details, edit, extras); Mapping.actionDone.Add(new Mapping.ActionState()); @@ -356,66 +1640,155 @@ public static void RemoveAction(string name) public static int GetActionIndexOf(string name) { - for (int i = 0; i < m_Config.actions.Count; i++) + for (int i = 0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { if (m_Config.actions[i].name == name) return i; + } + return -1; } + public static int GetProfileActionIndexOf(int device, string name) + { + int index = -1; + m_Config.profileActionIndexDict[device].TryGetValue(name, out index); + return index; + } + public static SpecialAction GetAction(string name) { - foreach (SpecialAction sA in m_Config.actions) + //foreach (SpecialAction sA in m_Config.actions) + for (int i=0, actionCount = m_Config.actions.Count; i < actionCount; i++) + { + SpecialAction sA = m_Config.actions[i]; if (sA.name == name) return sA; + } + return new SpecialAction("null", "null", "null", "null"); } + public static SpecialAction GetProfileAction(int device, string name) + { + SpecialAction sA = null; + m_Config.profileActionDict[device].TryGetValue(name, out sA); + return sA; + } + + public static void calculateProfileActionDicts(int device) + { + m_Config.profileActionDict[device].Clear(); + m_Config.profileActionIndexDict[device].Clear(); + + foreach (string actionname in m_Config.profileActions[device]) + { + m_Config.profileActionDict[device][actionname] = GetAction(actionname); + m_Config.profileActionIndexDict[device][actionname] = GetActionIndexOf(actionname); + } + } + + public static void cacheProfileCustomsFlags(int device) + { + bool customAct = false; + m_Config.containsCustomAction[device] = customAct = HasCustomActions(device); + m_Config.containsCustomExtras[device] = HasCustomExtras(device); + + if (!customAct) + { + customAct = m_Config.gyroOutMode[device] == GyroOutMode.MouseJoystick; + customAct = customAct || m_Config.sASteeringWheelEmulationAxis[device] >= SASteeringWheelEmulationAxisType.VJoy1X; + m_Config.containsCustomAction[device] = customAct; + } + } + + public static void CacheExtraProfileInfo(int device) + { + calculateProfileActionCount(device); + calculateProfileActionDicts(device); + cacheProfileCustomsFlags(device); + } + + public static X360Controls getX360ControlsByName(string key) + { + return m_Config.getX360ControlsByName(key); + } + + public static string getX360ControlString(X360Controls key) + { + return m_Config.getX360ControlString(key); + } + + public static DS4Controls getDS4ControlsByName(string key) + { + return m_Config.getDS4ControlsByName(key); + } + + public static X360Controls getDefaultX360ControlBinding(DS4Controls dc) + { + return defaultButtonMapping[(int)dc]; + } + + public static bool containsLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + return m_Config.linkedProfiles.ContainsKey(tempSerial); + } + + public static string getLinkedProfile(string serial) + { + string temp = string.Empty; + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + temp = m_Config.linkedProfiles[tempSerial]; + } + + return temp; + } + + public static void changeLinkedProfile(string serial, string profile) + { + string tempSerial = serial.Replace(":", string.Empty); + m_Config.linkedProfiles[tempSerial] = profile; + } + + public static void removeLinkedProfile(string serial) + { + string tempSerial = serial.Replace(":", string.Empty); + if (m_Config.linkedProfiles.ContainsKey(tempSerial)) + { + m_Config.linkedProfiles.Remove(tempSerial); + } + } - /*public static X360Controls getCustomButton(int device, DS4Controls controlName) => m_Config.GetCustomButton(device, controlName); - - public static ushort getCustomKey(int device, DS4Controls controlName) => m_Config.GetCustomKey(device, controlName); - - public static string getCustomMacro(int device, DS4Controls controlName) => m_Config.GetCustomMacro(device, controlName); - - public static string getCustomExtras(int device, DS4Controls controlName) => m_Config.GetCustomExtras(device, controlName); - - public static DS4KeyType getCustomKeyType(int device, DS4Controls controlName) => m_Config.GetCustomKeyType(device, controlName); - - public static bool getHasCustomKeysorButtons(int device) => m_Config.customMapButtons[device].Count > 0 - || m_Config.customMapKeys[device].Count > 0; - - public static bool getHasCustomExtras(int device) => m_Config.customMapExtras[device].Count > 0; - public static Dictionary getCustomButtons(int device) => m_Config.customMapButtons[device]; - public static Dictionary getCustomKeys(int device) => m_Config.customMapKeys[device]; - public static Dictionary getCustomMacros(int device) => m_Config.customMapMacros[device]; - public static Dictionary getCustomExtras(int device) => m_Config.customMapExtras[device]; - public static Dictionary getCustomKeyTypes(int device) => m_Config.customMapKeyTypes[device]; - - public static X360Controls getShiftCustomButton(int device, DS4Controls controlName) => m_Config.GetShiftCustomButton(device, controlName); - public static ushort getShiftCustomKey(int device, DS4Controls controlName) => m_Config.GetShiftCustomKey(device, controlName); - public static string getShiftCustomMacro(int device, DS4Controls controlName) => m_Config.GetShiftCustomMacro(device, controlName); - public static string getShiftCustomExtras(int device, DS4Controls controlName) => m_Config.GetShiftCustomExtras(device, controlName); - public static DS4KeyType getShiftCustomKeyType(int device, DS4Controls controlName) => m_Config.GetShiftCustomKeyType(device, controlName); - public static bool getHasShiftCustomKeysorButtons(int device) => m_Config.shiftCustomMapButtons[device].Count > 0 - || m_Config.shiftCustomMapKeys[device].Count > 0; - public static bool getHasShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device].Count > 0; - public static Dictionary getShiftCustomButtons(int device) => m_Config.shiftCustomMapButtons[device]; - public static Dictionary getShiftCustomKeys(int device) => m_Config.shiftCustomMapKeys[device]; - public static Dictionary getShiftCustomMacros(int device) => m_Config.shiftCustomMapMacros[device]; - public static Dictionary getShiftCustomExtras(int device) => m_Config.shiftCustomMapExtras[device]; - public static Dictionary getShiftCustomKeyTypes(int device) => m_Config.shiftCustomMapKeyTypes[device]; */ public static bool Load() => m_Config.Load(); - public static void LoadProfile(int device, bool launchprogram, ControlService control) + public static void LoadProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) { - m_Config.LoadProfile(device, launchprogram, control); + m_Config.LoadProfile(device, launchprogram, control, "", xinputChange, postLoad); tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } - public static void LoadTempProfile(int device, string name, bool launchprogram, ControlService control) + public static void LoadTempProfile(int device, string name, bool launchprogram, + ControlService control, bool xinputChange = true) { m_Config.LoadProfile(device, launchprogram, control, appdatapath + @"\Profiles\" + name + ".xml"); tempprofilename[device] = name; + useTempProfile[device] = true; + tempprofileDistance[device] = name.ToLower().Contains("distance"); + } + + public static void LoadBlankDevProfile(int device, bool launchprogram, ControlService control, + bool xinputChange = true, bool postLoad = true) + { + m_Config.LoadBlankProfile(device, launchprogram, control, "", xinputChange, postLoad); + tempprofilename[device] = string.Empty; + useTempProfile[device] = false; + tempprofileDistance[device] = false; } public static bool Save() @@ -428,22 +1801,66 @@ public static void SaveProfile(int device, string propath) m_Config.SaveProfile(device, propath); } + public static void SaveAsNewProfile(int device, string propath) + { + m_Config.SaveAsNewProfile(device, propath); + } + + public static bool SaveLinkedProfiles() + { + return m_Config.SaveLinkedProfiles(); + } + + public static bool LoadLinkedProfiles() + { + return m_Config.LoadLinkedProfiles(); + } + + public static bool SaveControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.SaveControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.SaveControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + + public static bool LoadControllerConfigs(DS4Device device = null) + { + if (device != null) + return m_Config.LoadControllerConfigsForDevice(device); + + for (int idx = 0; idx < ControlService.DS4_CONTROLLER_COUNT; idx++) + if (Program.rootHub.DS4Controllers[idx] != null) + m_Config.LoadControllerConfigsForDevice(Program.rootHub.DS4Controllers[idx]); + + return true; + } + private static byte applyRatio(byte b1, byte b2, double r) { - if (r > 100) - r = 100; - else if (r < 0) - r = 0; - r /= 100f; - return (byte)Math.Round((b1 * (1 - r) + b2 *r),0); + if (r > 100.0) + r = 100.0; + else if (r < 0.0) + r = 0.0; + + r *= 0.01; + return (byte)Math.Round((b1 * (1 - r)) + b2 * r, 0); } - public static DS4Color getTransitionedColor(DS4Color c1, DS4Color c2, double ratio) - {//; + + public static DS4Color getTransitionedColor(ref DS4Color c1, ref DS4Color c2, double ratio) + { //Color cs = Color.FromArgb(c1.red, c1.green, c1.blue); - c1.red = applyRatio(c1.red, c2.red, ratio); - c1.green = applyRatio(c1.green, c2.green, ratio); - c1.blue = applyRatio(c1.blue, c2.blue, ratio); - return c1; + DS4Color cs = new DS4Color + { + red = applyRatio(c1.red, c2.red, ratio), + green = applyRatio(c1.green, c2.green, ratio), + blue = applyRatio(c1.blue, c2.blue, ratio) + }; + return cs; } private static Color applyRatio(Color c1, Color c2, uint r) @@ -463,6 +1880,7 @@ private static Color applyRatio(Color c1, Color c2, uint r) csR = HuetoRGB(hue2,sat2,bri2 - br*ratio); else csR = HuetoRGB(hue2 - hr * ratio, sat2 - sr * ratio, bri2 - br * ratio); + return csR; } @@ -473,234 +1891,546 @@ public static Color HuetoRGB(float hue, float sat, float bri) float m = bri - C / 2; float R, G, B; if (0 <= hue && hue < 60) - { R = C; G = X; B = 0;} + { + R = C; G = X; B = 0; + } else if (60 <= hue && hue < 120) - {R = X; G = C; B = 0; } + { + R = X; G = C; B = 0; + } else if (120 <= hue && hue < 180) - { R = 0; G = C; B = X; } + { + R = 0; G = C; B = X; + } else if (180 <= hue && hue < 240) - { R = 0; G = X; B = C; } + { + R = 0; G = X; B = C; + } else if (240 <= hue && hue < 300) - { R = X; G = 0; B = C; } + { + R = X; G = 0; B = C; + } else if (300 <= hue && hue < 360) - { R = C; G = 0; B = X; } + { + R = C; G = 0; B = X; + } else - { R = 255; G = 0; B = 0; } + { + R = 255; G = 0; B = 0; + } + R += m; G += m; B += m; - R *= 255; G *= 255; B *= 255; + R *= 255.0f; G *= 255.0f; B *= 255.0f; return Color.FromArgb((int)R, (int)G, (int)B); } - } - + public static double Clamp(double min, double value, double max) + { + return (value < min) ? min : (value > max) ? max : value; + } + private static int ClampInt(int min, int value, int max) + { + return (value < min) ? min : (value > max) ? max : value; + } + } public class BackingStore { //public String m_Profile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DS4Tool" + "\\Profiles.xml"; public String m_Profile = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName + "\\Profiles.xml"; public String m_Actions = Global.appdatapath + "\\Actions.xml"; + public string m_linkedProfiles = Global.appdatapath + "\\LinkedProfiles.xml"; + public string m_controllerConfigs = Global.appdatapath + "\\ControllerConfigs.xml"; protected XmlDocument m_Xdoc = new XmlDocument(); - //fifth value used to for options, not fifth controller - public int[] buttonMouseSensitivity = { 25, 25, 25, 25, 25 }; - - public bool[] flushHIDQueue = { true, true, true, true, true }; - public int[] idleDisconnectTimeout = { 0, 0, 0, 0, 0 }; - public Boolean[] touchpadJitterCompensation = { true, true, true, true, true }; - public Boolean[] lowerRCOn = { false, false, false, false, false }; - public Boolean[] ledAsBattery = { false, false, false, false, false }; - public Byte[] flashType = { 0, 0, 0, 0, 0 }; - public String[] profilePath = { String.Empty, String.Empty, String.Empty, String.Empty, String.Empty }; - public Byte[] rumble = { 100, 100, 100, 100, 100 }; - public Byte[] touchSensitivity = { 100, 100, 100, 100, 100 }; - public Byte[] l2Deadzone = { 0, 0, 0, 0, 0 }, r2Deadzone = { 0, 0, 0, 0, 0 }; - public int[] LSDeadzone = { 0, 0, 0, 0, 0 }, RSDeadzone = { 0, 0, 0, 0, 0 }; - public double[] SXDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = { 0.25, 0.25, 0.25, 0.25, 0.25 }; - public double[] l2Sens = { 1, 1, 1, 1, 1 }, r2Sens = { 1, 1, 1, 1, 1 }; - public double[] LSSens = { 1, 1, 1, 1, 1 }, RSSens = { 1, 1, 1, 1, 1 }; - public double[] SXSens = { 1, 1, 1, 1, 1 }, SZSens = { 1, 1, 1, 1, 1 }; - public Byte[] tapSensitivity = { 0, 0, 0, 0, 0 }; - public bool[] doubleTap = { false, false, false, false, false }; - public int[] scrollSensitivity = { 0, 0, 0, 0, 0 }; - public double[] rainbow = { 0, 0, 0, 0, 0 }; - public int[] flashAt = { 0, 0, 0, 0, 0 }; - public bool[] mouseAccel = { true, true, true, true, true }; - public DS4Color[] m_LowLeds = new DS4Color[] - { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + // fifth value used for options, not fifth controller + public int[] buttonMouseSensitivity = new int[5] { 25, 25, 25, 25, 25 }; + + public bool[] flushHIDQueue = new bool[5] { false, false, false, false, false }; + public bool[] enableTouchToggle = new bool[5] { true, true, true, true, true }; + public int[] idleDisconnectTimeout = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] touchpadJitterCompensation = new bool[5] { true, true, true, true, true }; + public bool[] lowerRCOn = new bool[5] { false, false, false, false, false }; + public string[] profilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public string[] olderProfilePath = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public Dictionary linkedProfiles = new Dictionary(); + // Cache properties instead of performing a string comparison every frame + public bool[] distanceProfiles = new bool[5] { false, false, false, false, false }; + public Byte[] rumble = new Byte[5] { 100, 100, 100, 100, 100 }; + public int[] rumbleAutostopTime = new int[5] { 0, 0, 0, 0, 0 }; // Value in milliseconds (0=autustop timer disabled) + public Byte[] touchSensitivity = new Byte[5] { 100, 100, 100, 100, 100 }; + public StickDeadZoneInfo[] lsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public StickDeadZoneInfo[] rsModInfo = new StickDeadZoneInfo[5] + { + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo(), new StickDeadZoneInfo(), + new StickDeadZoneInfo() + }; + public TriggerDeadZoneZInfo[] l2ModInfo = new TriggerDeadZoneZInfo[5] + { + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_Leds = new DS4Color[] + public TriggerDeadZoneZInfo[] r2ModInfo = new TriggerDeadZoneZInfo[5] { - new DS4Color(Color.Blue), - new DS4Color(Color.Red), - new DS4Color(Color.Green), - new DS4Color(Color.Pink), - new DS4Color(Color.White) + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo(), new TriggerDeadZoneZInfo(), + new TriggerDeadZoneZInfo() }; - public DS4Color[] m_ChargingLeds = new DS4Color[] + + public double[] LSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, RSRotation = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] SXDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }, SZDeadzone = new double[5] { 0.25, 0.25, 0.25, 0.25, 0.25 }; + public double[] SXMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, + SZMaxzone = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }, + SZAntiDeadzone = new double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }; + public double[] l2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, r2Sens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] LSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, RSSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public double[] SXSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }, SZSens = new double[5] { 1.0, 1.0, 1.0, 1.0, 1.0 }; + public Byte[] tapSensitivity = new Byte[5] { 0, 0, 0, 0, 0 }; + public bool[] doubleTap = new bool[5] { false, false, false, false, false }; + public int[] scrollSensitivity = new int[5] { 0, 0, 0, 0, 0 }; + public int[] touchpadInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] mouseAccel = new bool[5] { false, false, false, false, false }; + public int[] btPollRate = new int[5] { 4, 4, 4, 4, 4 }; + public int[] gyroMouseDZ = new int[5] { MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE, MouseCursor.GYRO_MOUSE_DEADZONE, + MouseCursor.GYRO_MOUSE_DEADZONE }; + public bool[] gyroMouseToggle = new bool[5] { false, false, false, + false, false }; + + public SquareStickInfo[] squStickInfo = new SquareStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), new SquareStickInfo(), + new SquareStickInfo(), }; - public DS4Color[] m_FlashLeds = new DS4Color[] + + private void setOutBezierCurveObjArrayItem(BezierCurve[] bezierCurveArray, int device, int curveOptionValue, BezierCurve.AxisType axisType) + { + // Set bezier curve obj of axis. 0=Linear (no curve mapping), 1-5=Pre-defined curves, 6=User supplied custom curve string value of a profile (comma separated list of 4 decimal numbers) + switch (curveOptionValue) + { + // Commented out case 1..5 because Mapping.cs:SetCurveAndDeadzone function has the original IF-THEN-ELSE code logic for those original 1..5 output curve mappings (ie. no need to initialize the lookup result table). + // Only the new bezier custom curve option 6 uses the lookup result table (initialized in BezierCurve class based on an input curve definition). + //case 1: bezierCurveArray[device].InitBezierCurve(99.0, 91.0, 0.00, 0.00, axisType); break; // Enhanced Precision (hard-coded curve) (almost the same curve as bezier 0.70, 0.28, 1.00, 1.00) + //case 2: bezierCurveArray[device].InitBezierCurve(99.0, 92.0, 0.00, 0.00, axisType); break; // Quadric + //case 3: bezierCurveArray[device].InitBezierCurve(99.0, 93.0, 0.00, 0.00, axisType); break; // Cubic + //case 4: bezierCurveArray[device].InitBezierCurve(99.0, 94.0, 0.00, 0.00, axisType); break; // Easeout Quad + //case 5: bezierCurveArray[device].InitBezierCurve(99.0, 95.0, 0.00, 0.00, axisType); break; // Easeout Cubic + case 6: bezierCurveArray[device].InitBezierCurve(bezierCurveArray[device].CustomDefinition, axisType); break; // Custom output curve + } + } + + public BezierCurve[] lsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] rsOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] l2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] r2OutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] sxOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + public BezierCurve[] szOutBezierCurveObj = new BezierCurve[5] { new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve(), new BezierCurve() }; + + private int[] _lsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getLsOutCurveMode(int index) { return _lsOutCurveMode[index]; } + public void setLsOutCurveMode(int index, int value) { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + if (value >= 1) setOutBezierCurveObjArrayItem(lsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _lsOutCurveMode[index] = value; + } + + private int[] _rsOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getRsOutCurveMode(int index) { return _rsOutCurveMode[index]; } + public void setRsOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(rsOutBezierCurveObj, index, value, BezierCurve.AxisType.LSRS); + _rsOutCurveMode[index] = value; + } + + private int[] _l2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getL2OutCurveMode(int index) { return _l2OutCurveMode[index]; } + public void setL2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(l2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _l2OutCurveMode[index] = value; + } + + private int[] _r2OutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getR2OutCurveMode(int index) { return _r2OutCurveMode[index]; } + public void setR2OutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(r2OutBezierCurveObj, index, value, BezierCurve.AxisType.L2R2); + _r2OutCurveMode[index] = value; + } + + private int[] _sxOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSXOutCurveMode(int index) { return _sxOutCurveMode[index]; } + public void setSXOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(sxOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _sxOutCurveMode[index] = value; + } + + private int[] _szOutCurveMode = new int[5] { 0, 0, 0, 0, 0 }; + public int getSZOutCurveMode(int index) { return _szOutCurveMode[index]; } + public void setSZOutCurveMode(int index, int value) + { + if (value >= 1) setOutBezierCurveObjArrayItem(szOutBezierCurveObj, index, value, BezierCurve.AxisType.SA); + _szOutCurveMode[index] = value; + } + + public LightbarSettingInfo[] lightbarSettingInfo = new LightbarSettingInfo[5] + { + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo(), new LightbarSettingInfo(), + new LightbarSettingInfo() }; - public bool[] useCustomLeds = new bool[] { false, false, false, false }; - public DS4Color[] m_CustomLeds = new DS4Color[] + + public string[] launchProgram = new string[5] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; + public bool[] dinputOnly = new bool[5] { false, false, false, false, false }; + public bool[] startTouchpadOff = new bool[5] { false, false, false, false, false }; + public bool[] useTPforControls = new bool[5] { false, false, false, false, false }; + public bool[] useSAforMouse = new bool[5] { false, false, false, false, false }; + public GyroOutMode[] gyroOutMode = new GyroOutMode[5] { GyroOutMode.Controls, GyroOutMode.Controls, + GyroOutMode.Controls, GyroOutMode.Controls, GyroOutMode.Controls }; + public string[] sATriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public string[] sAMouseStickTriggers = new string[5] { "-1", "-1", "-1", "-1", "-1" }; + public bool[] sATriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] sAMouseStickTriggerCond = new bool[5] { true, true, true, true, true }; + public bool[] gyroMouseStickTriggerTurns = new bool[5] { true, true, true, true, true }; + public GyroMouseStickInfo[] gyroMStickInfo = new GyroMouseStickInfo[5] { - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black), - new DS4Color(Color.Black) + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), + new GyroMouseStickInfo(), new GyroMouseStickInfo(), + new GyroMouseStickInfo() }; - public int[] chargingType = { 0, 0, 0, 0, 0 }; - public string[] launchProgram = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty }; - public bool[] dinputOnly = { false, false, false, false, false }; - public bool[] startTouchpadOff = { false, false, false, false, false }; - public bool[] useTPforControls = { false, false, false, false, false }; - public bool[] useSAforMouse = { false, false, false, false, false }; - public string[] sATriggers = { "", "", "", "", "" }; - public int[] lsCurve = { 0, 0, 0, 0, 0 }; - public int[] rsCurve = { 0, 0, 0, 0, 0 }; + + public bool[] gyroMouseStickToggle = new bool[5] { false, false, false, + false, false }; + + public SASteeringWheelEmulationAxisType[] sASteeringWheelEmulationAxis = new SASteeringWheelEmulationAxisType[5] { SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None, SASteeringWheelEmulationAxisType.None }; + public int[] sASteeringWheelEmulationRange = new int[5] { 360, 360, 360, 360, 360 }; + public int[][] touchDisInvertTriggers = new int[5][] { new int[1] { -1 }, new int[1] { -1 }, new int[1] { -1 }, + new int[1] { -1 }, new int[1] { -1 } }; + public int[] lsCurve = new int[5] { 0, 0, 0, 0, 0 }; + public int[] rsCurve = new int[5] { 0, 0, 0, 0, 0 }; public Boolean useExclusiveMode = false; public Int32 formWidth = 782; public Int32 formHeight = 550; + public int formLocationX = 0; + public int formLocationY = 0; public Boolean startMinimized = false; + public Boolean minToTaskbar = false; public DateTime lastChecked; public int CheckWhen = 1; public int notifications = 2; public bool disconnectBTAtStop = false; public bool swipeProfiles = true; - public bool ds4Mapping = true; + public bool ds4Mapping = false; public bool quickCharge = false; - public int firstXinputPort = 1; public bool closeMini = false; public List actions = new List(); - public List[] ds4settings = { new List(), new List(), new List(), new List(), new List() }; - /*public Dictionary[] customMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] customMapKeys = { null, null, null, null, null }; - public Dictionary[] customMapMacros = { null, null, null, null, null }; - public Dictionary[] customMapButtons = { null, null, null, null, null }; - public Dictionary[] customMapExtras = { null, null, null, null, null }; - - public Dictionary[] shiftCustomMapKeyTypes = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapKeys = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapMacros = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapButtons = { null, null, null, null, null }; - public Dictionary[] shiftCustomMapExtras = { null, null, null, null, null };*/ - public List[] profileActions = { null, null, null, null, null }; + public List[] ds4settings = new List[5] + { new List(), new List(), new List(), + new List(), new List() }; + + public List[] profileActions = new List[5] { null, null, null, null, null }; + public int[] profileActionCount = new int[5] { 0, 0, 0, 0, 0 }; + public Dictionary[] profileActionDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public Dictionary[] profileActionIndexDict = new Dictionary[5] + { new Dictionary(), new Dictionary(), new Dictionary(), + new Dictionary(), new Dictionary() }; + + public string useLang = ""; public bool downloadLang = true; public bool useWhiteIcon; public bool flashWhenLate = true; - public int flashWhenLateAt = 10; - public int[] gyroSensitivity = { 100, 100, 100, 100, 100 }; - public int[] gyroInvert = { 0, 0, 0, 0, 0 }; + public int flashWhenLateAt = 20; + public bool useUDPServ = false; + public int udpServPort = 26760; + public string udpServListenAddress = "127.0.0.1"; // 127.0.0.1=IPAddress.Loopback (default), 0.0.0.0=IPAddress.Any as all interfaces, x.x.x.x = Specific ipv4 interface address or hostname + public bool useCustomSteamFolder; + public string customSteamFolder; + // Cache whether profile has custom action + public bool[] containsCustomAction = new bool[5] { false, false, false, false, false }; + + // Cache whether profile has custom extras + public bool[] containsCustomExtras = new bool[5] { false, false, false, false, false }; + + public int[] gyroSensitivity = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroSensVerticalScale = new int[5] { 100, 100, 100, 100, 100 }; + public int[] gyroInvert = new int[5] { 0, 0, 0, 0, 0 }; + public bool[] gyroTriggerTurns = new bool[5] { true, true, true, true, true }; + public bool[] gyroSmoothing = new bool[5] { false, false, false, false, false }; + public double[] gyroSmoothWeight = new double[5] { 0.5, 0.5, 0.5, 0.5, 0.5 }; + public int[] gyroMouseHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public int[] gyroMouseStickHorizontalAxis = new int[5] { 0, 0, 0, 0, 0 }; + + public bool[] trackballMode = new bool[5] { false, false, false, false, false }; + public double[] trackballFriction = new double[5] { 10.0, 10.0, 10.0, 10.0, 10.0 }; + // Used to hold the controller type desired in a profile + public OutContType[] outputDevType = new OutContType[5] { OutContType.X360, + OutContType.X360, OutContType.X360, + OutContType.X360, OutContType.X360 }; + + // TRUE=AutoProfile reverts to default profile if current foreground process is unknown, FALSE=Leave existing profile active when a foreground proces is unknown (ie. no matching auto-profile rule) + public bool autoProfileRevertDefaultProfile = true; + + bool tempBool = false; public BackingStore() { for (int i = 0; i < 5; i++) { foreach (DS4Controls dc in Enum.GetValues(typeof(DS4Controls))) + { if (dc != DS4Controls.None) ds4settings[i].Add(new DS4ControlSettings(dc)); - /*customMapKeyTypes[i] = new Dictionary(); - customMapKeys[i] = new Dictionary(); - customMapMacros[i] = new Dictionary(); - customMapButtons[i] = new Dictionary(); - customMapExtras[i] = new Dictionary(); - - shiftCustomMapKeyTypes[i] = new Dictionary(); - shiftCustomMapKeys[i] = new Dictionary(); - shiftCustomMapMacros[i] = new Dictionary(); - shiftCustomMapButtons[i] = new Dictionary(); - shiftCustomMapExtras[i] = new Dictionary();*/ + } + profileActions[i] = new List(); profileActions[i].Add("Disconnect Controller"); + profileActionCount[i] = profileActions[i].Count; + } + + SetupDefaultColors(); + } + + private void SetupDefaultColors() + { + lightbarSettingInfo[0].ds4winSettings.m_Led = new DS4Color(Color.Blue); + lightbarSettingInfo[1].ds4winSettings.m_Led = new DS4Color(Color.Red); + lightbarSettingInfo[2].ds4winSettings.m_Led = new DS4Color(Color.Green); + lightbarSettingInfo[3].ds4winSettings.m_Led = new DS4Color(Color.Pink); + lightbarSettingInfo[4].ds4winSettings.m_Led = new DS4Color(Color.White); + } + + private string stickOutputCurveString(int id) + { + string result = "linear"; + switch (id) + { + case 0: break; + case 1: result = "enhanced-precision"; break; + case 2: result = "quadratic"; break; + case 3: result = "cubic"; break; + case 4: result = "easeout-quad"; break; + case 5: result = "easeout-cubic"; break; + case 6: result = "custom"; break; + default: break; + } + + return result; + } + + private int stickOutputCurveId(string name) + { + int id = 0; + switch (name) + { + case "linear": id = 0; break; + case "enhanced-precision": id = 1; break; + case "quadratic": id = 2; break; + case "cubic": id = 3; break; + case "easeout-quad": id = 4; break; + case "easeout-cubic": id = 5; break; + case "custom": id = 6; break; + default: break; + } + + return id; + } + + private string axisOutputCurveString(int id) + { + return stickOutputCurveString(id); + } + + private int axisOutputCurveId(string name) + { + return stickOutputCurveId(name); + } + + private bool SaTriggerCondValue(string text) + { + bool result = true; + switch (text) + { + case "and": result = true; break; + case "or": result = false; break; + default: result = true; break; } + + return result; + } + + private string SaTriggerCondString(bool value) + { + string result = value ? "and" : "or"; + return result; + } + + public void SetSaTriggerCond(int index, string text) + { + sATriggerCond[index] = SaTriggerCondValue(text); } - /*public X360Controls GetCustomButton(int device, DS4Controls controlName) + public void SetSaMouseStickTriggerCond(int index, string text) { - if (customMapButtons[device].ContainsKey(controlName)) - return customMapButtons[device][controlName]; - else return X360Controls.None; + sAMouseStickTriggerCond[index] = SaTriggerCondValue(text); } - public UInt16 GetCustomKey(int device, DS4Controls controlName) + + public void SetGyroMouseDZ(int index, int value, ControlService control) + { + gyroMouseDZ[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].CursorGyroDead = value; + } + + public void SetGyroMouseToggle(int index, bool value, ControlService control) { - if (customMapKeys[device].ContainsKey(controlName)) - return customMapKeys[device][controlName]; - else return 0; + gyroMouseToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomMacro(int device, DS4Controls controlName) + + public void SetGyroMouseStickToggle(int index, bool value, ControlService control) { - if (customMapMacros[device].ContainsKey(controlName)) - return customMapMacros[device][controlName]; - else return "0"; + gyroMouseStickToggle[index] = value; + if (index < 4 && control.touchPad[index] != null) + control.touchPad[index].ToggleGyroMouse = value; } - public string GetCustomExtras(int device, DS4Controls controlName) + + private string OutContDeviceString(OutContType id) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "X360"; + switch (id) + { + case OutContType.None: + case OutContType.X360: result = "X360"; break; + case OutContType.DS4: result = "DS4"; break; + default: break; + } + + return result; } - public DS4KeyType GetCustomKeyType(int device, DS4Controls controlName) + + private OutContType OutContDeviceId(string name) { - try + OutContType id = OutContType.X360; + switch (name) { - if (customMapKeyTypes[device].ContainsKey(controlName)) - return customMapKeyTypes[device][controlName]; - else return 0; + case "None": + case "X360": id = OutContType.X360; break; + case "DS4": id = OutContType.DS4; break; + default: break; } - catch { return 0; } + + return id; } - public X360Controls GetShiftCustomButton(int device, DS4Controls controlName) + private void PortOldGyroSettings(int device) { - if (shiftCustomMapButtons[device].ContainsKey(controlName)) - return shiftCustomMapButtons[device][controlName]; - else return X360Controls.None; + if (gyroOutMode[device] == GyroOutMode.None) + { + gyroOutMode[device] = GyroOutMode.Controls; + } } - public UInt16 GetShiftCustomKey(int device, DS4Controls controlName) + + private string GetGyroOutModeString(GyroOutMode mode) { - if (shiftCustomMapKeys[device].ContainsKey(controlName)) - return shiftCustomMapKeys[device][controlName]; - else return 0; + string result = "None"; + switch(mode) + { + case GyroOutMode.Controls: + result = "Controls"; + break; + case GyroOutMode.Mouse: + result = "Mouse"; + break; + case GyroOutMode.MouseJoystick: + result = "MouseJoystick"; + break; + default: + break; + } + + return result; } - public string GetShiftCustomMacro(int device, DS4Controls controlName) + + private GyroOutMode GetGyroOutModeType(string modeString) { - if (shiftCustomMapMacros[device].ContainsKey(controlName)) - return shiftCustomMapMacros[device][controlName]; - else return "0"; + GyroOutMode result = GyroOutMode.None; + switch(modeString) + { + case "Controls": + result = GyroOutMode.Controls; + break; + case "Mouse": + result = GyroOutMode.Mouse; + break; + case "MouseJoystick": + result = GyroOutMode.MouseJoystick; + break; + default: + break; + } + + return result; } - public string GetShiftCustomExtras(int device, DS4Controls controlName) + + private string GetLightbarModeString(LightbarMode mode) { - if (customMapExtras[device].ContainsKey(controlName)) - return customMapExtras[device][controlName]; - else return "0"; + string result = "DS4Win"; + switch (mode) + { + case LightbarMode.DS4Win: + result = "DS4Win"; + break; + case LightbarMode.Passthru: + result = "Passthru"; + break; + default: + break; + } + return result; } - public DS4KeyType GetShiftCustomKeyType(int device, DS4Controls controlName) + + private LightbarMode GetLightbarModeType(string modeString) { - try + LightbarMode result = LightbarMode.DS4Win; + switch (modeString) { - if (shiftCustomMapKeyTypes[device].ContainsKey(controlName)) - return shiftCustomMapKeyTypes[device][controlName]; - else return 0; + case "DS4Win": + result = LightbarMode.DS4Win; + break; + case "Passthru": + result = LightbarMode.Passthru; + break; + default: + break; } - catch { return 0; } - }*/ + + return result; + } + + public bool SaveAsNewProfile(int device, string propath) + { + bool Saved = true; + ResetProfile(device); + Saved = SaveProfile(device, propath); + return Saved; + } public bool SaveProfile(int device, string propath) { @@ -719,49 +2449,81 @@ public bool SaveProfile(int device, string propath) Node = m_Xdoc.CreateComment(string.Format(" DS4Windows Configuration Data. {0} ", DateTime.Now)); m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateComment(string.Format(" Made with DS4Windows version {0} ", Global.exeversion)); + m_Xdoc.AppendChild(Node); + Node = m_Xdoc.CreateWhitespace("\r\n"); m_Xdoc.AppendChild(Node); Node = m_Xdoc.CreateNode(XmlNodeType.Element, "DS4Windows", null); + LightbarSettingInfo lightbarSettings = lightbarSettingInfo[device]; + LightbarDS4WinInfo lightInfo = lightbarSettings.ds4winSettings; + XmlNode xmlFlushHIDQueue = m_Xdoc.CreateNode(XmlNodeType.Element, "flushHIDQueue", null); xmlFlushHIDQueue.InnerText = flushHIDQueue[device].ToString(); Node.AppendChild(xmlFlushHIDQueue); + XmlNode xmlTouchToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "touchToggle", null); xmlTouchToggle.InnerText = enableTouchToggle[device].ToString(); Node.AppendChild(xmlTouchToggle); XmlNode xmlIdleDisconnectTimeout = m_Xdoc.CreateNode(XmlNodeType.Element, "idleDisconnectTimeout", null); xmlIdleDisconnectTimeout.InnerText = idleDisconnectTimeout[device].ToString(); Node.AppendChild(xmlIdleDisconnectTimeout); XmlNode xmlColor = m_Xdoc.CreateNode(XmlNodeType.Element, "Color", null); - xmlColor.InnerText = m_Leds[device].red.ToString() + "," + m_Leds[device].green.ToString() + "," + m_Leds[device].blue.ToString(); + xmlColor.InnerText = lightInfo.m_Led.red.ToString() + "," + lightInfo.m_Led.green.ToString() + "," + lightInfo.m_Led.blue.ToString(); Node.AppendChild(xmlColor); XmlNode xmlRumbleBoost = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleBoost", null); xmlRumbleBoost.InnerText = rumble[device].ToString(); Node.AppendChild(xmlRumbleBoost); - XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = ledAsBattery[device].ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); - XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = flashType[device].ToString(); Node.AppendChild(xmlLowBatteryFlash); - XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = flashAt[device].ToString(); Node.AppendChild(xmlFlashBatterAt); + XmlNode xmlRumbleAutostopTime = m_Xdoc.CreateNode(XmlNodeType.Element, "RumbleAutostopTime", null); xmlRumbleAutostopTime.InnerText = rumbleAutostopTime[device].ToString(); Node.AppendChild(xmlRumbleAutostopTime); + XmlNode xmlLightbarMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LightbarMode", null); xmlLightbarMode.InnerText = GetLightbarModeString(lightbarSettings.mode); Node.AppendChild(xmlLightbarMode); + XmlNode xmlLedAsBatteryIndicator = m_Xdoc.CreateNode(XmlNodeType.Element, "ledAsBatteryIndicator", null); xmlLedAsBatteryIndicator.InnerText = lightInfo.ledAsBattery.ToString(); Node.AppendChild(xmlLedAsBatteryIndicator); + XmlNode xmlLowBatteryFlash = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashType", null); xmlLowBatteryFlash.InnerText = lightInfo.flashType.ToString(); Node.AppendChild(xmlLowBatteryFlash); + XmlNode xmlFlashBatterAt = m_Xdoc.CreateNode(XmlNodeType.Element, "flashBatteryAt", null); xmlFlashBatterAt.InnerText = lightInfo.flashAt.ToString(); Node.AppendChild(xmlFlashBatterAt); XmlNode xmlTouchSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "touchSensitivity", null); xmlTouchSensitivity.InnerText = touchSensitivity[device].ToString(); Node.AppendChild(xmlTouchSensitivity); XmlNode xmlLowColor = m_Xdoc.CreateNode(XmlNodeType.Element, "LowColor", null); - xmlLowColor.InnerText = m_LowLeds[device].red.ToString() + "," + m_LowLeds[device].green.ToString() + "," + m_LowLeds[device].blue.ToString(); + xmlLowColor.InnerText = lightInfo.m_LowLed.red.ToString() + "," + lightInfo.m_LowLed.green.ToString() + "," + lightInfo.m_LowLed.blue.ToString(); Node.AppendChild(xmlLowColor); XmlNode xmlChargingColor = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingColor", null); - xmlChargingColor.InnerText = m_ChargingLeds[device].red.ToString() + "," + m_ChargingLeds[device].green.ToString() + "," + m_ChargingLeds[device].blue.ToString(); + xmlChargingColor.InnerText = lightInfo.m_ChargingLed.red.ToString() + "," + lightInfo.m_ChargingLed.green.ToString() + "," + lightInfo.m_ChargingLed.blue.ToString(); Node.AppendChild(xmlChargingColor); XmlNode xmlFlashColor = m_Xdoc.CreateNode(XmlNodeType.Element, "FlashColor", null); - xmlFlashColor.InnerText = m_FlashLeds[device].red.ToString() + "," + m_FlashLeds[device].green.ToString() + "," + m_FlashLeds[device].blue.ToString(); + xmlFlashColor.InnerText = lightInfo.m_FlashLed.red.ToString() + "," + lightInfo.m_FlashLed.green.ToString() + "," + lightInfo.m_FlashLed.blue.ToString(); Node.AppendChild(xmlFlashColor); XmlNode xmlTouchpadJitterCompensation = m_Xdoc.CreateNode(XmlNodeType.Element, "touchpadJitterCompensation", null); xmlTouchpadJitterCompensation.InnerText = touchpadJitterCompensation[device].ToString(); Node.AppendChild(xmlTouchpadJitterCompensation); XmlNode xmlLowerRCOn = m_Xdoc.CreateNode(XmlNodeType.Element, "lowerRCOn", null); xmlLowerRCOn.InnerText = lowerRCOn[device].ToString(); Node.AppendChild(xmlLowerRCOn); XmlNode xmlTapSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "tapSensitivity", null); xmlTapSensitivity.InnerText = tapSensitivity[device].ToString(); Node.AppendChild(xmlTapSensitivity); XmlNode xmlDouble = m_Xdoc.CreateNode(XmlNodeType.Element, "doubleTap", null); xmlDouble.InnerText = doubleTap[device].ToString(); Node.AppendChild(xmlDouble); XmlNode xmlScrollSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "scrollSensitivity", null); xmlScrollSensitivity.InnerText = scrollSensitivity[device].ToString(); Node.AppendChild(xmlScrollSensitivity); - XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2Deadzone[device].ToString(); Node.AppendChild(xmlLeftTriggerMiddle); - XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2Deadzone[device].ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlLeftTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "LeftTriggerMiddle", null); xmlLeftTriggerMiddle.InnerText = l2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLeftTriggerMiddle); + XmlNode xmlRightTriggerMiddle = m_Xdoc.CreateNode(XmlNodeType.Element, "RightTriggerMiddle", null); xmlRightTriggerMiddle.InnerText = r2ModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRightTriggerMiddle); + XmlNode xmlTouchpadInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchpadInvert", null); xmlTouchpadInvert.InnerText = touchpadInvert[device].ToString(); Node.AppendChild(xmlTouchpadInvert); + XmlNode xmlL2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "L2AntiDeadZone", null); xmlL2AD.InnerText = l2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlL2AD); + XmlNode xmlR2AD = m_Xdoc.CreateNode(XmlNodeType.Element, "R2AntiDeadZone", null); xmlR2AD.InnerText = r2ModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlR2AD); + XmlNode xmlL2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxZone", null); xmlL2Maxzone.InnerText = l2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlL2Maxzone); + XmlNode xmlR2Maxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxZone", null); xmlR2Maxzone.InnerText = r2ModInfo[device].maxZone.ToString(); Node.AppendChild(xmlR2Maxzone); + XmlNode xmlL2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "L2MaxOutput", null); xmlL2MaxOutput.InnerText = l2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlL2MaxOutput); + XmlNode xmlR2MaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "R2MaxOutput", null); xmlR2MaxOutput.InnerText = r2ModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlR2MaxOutput); XmlNode xmlButtonMouseSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "ButtonMouseSensitivity", null); xmlButtonMouseSensitivity.InnerText = buttonMouseSensitivity[device].ToString(); Node.AppendChild(xmlButtonMouseSensitivity); - XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = rainbow[device].ToString(); Node.AppendChild(xmlRainbow); - XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = LSDeadzone[device].ToString(); Node.AppendChild(xmlLSD); - XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = RSDeadzone[device].ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "Rainbow", null); xmlRainbow.InnerText = lightInfo.rainbow.ToString(); Node.AppendChild(xmlRainbow); + XmlNode xmlMaxSatRainbow = m_Xdoc.CreateNode(XmlNodeType.Element, "MaxSatRainbow", null); xmlMaxSatRainbow.InnerText = Convert.ToInt32(lightInfo.maxRainbowSat * 100.0).ToString(); Node.AppendChild(xmlMaxSatRainbow); + XmlNode xmlLSD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSDeadZone", null); xmlLSD.InnerText = lsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlLSD); + XmlNode xmlRSD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSDeadZone", null); xmlRSD.InnerText = rsModInfo[device].deadZone.ToString(); Node.AppendChild(xmlRSD); + XmlNode xmlLSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "LSAntiDeadZone", null); xmlLSAD.InnerText = lsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlLSAD); + XmlNode xmlRSAD = m_Xdoc.CreateNode(XmlNodeType.Element, "RSAntiDeadZone", null); xmlRSAD.InnerText = rsModInfo[device].antiDeadZone.ToString(); Node.AppendChild(xmlRSAD); + XmlNode xmlLSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxZone", null); xmlLSMaxZone.InnerText = lsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlLSMaxZone); + XmlNode xmlRSMaxZone = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxZone", null); xmlRSMaxZone.InnerText = rsModInfo[device].maxZone.ToString(); Node.AppendChild(xmlRSMaxZone); + XmlNode xmlLSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "LSMaxOutput", null); xmlLSMaxOutput.InnerText = lsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlLSMaxOutput); + XmlNode xmlRSMaxOutput = m_Xdoc.CreateNode(XmlNodeType.Element, "RSMaxOutput", null); xmlRSMaxOutput.InnerText = rsModInfo[device].maxOutput.ToString(); Node.AppendChild(xmlRSMaxOutput); + XmlNode xmlLSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "LSRotation", null); xmlLSRotation.InnerText = Convert.ToInt32(LSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlLSRotation); + XmlNode xmlRSRotation = m_Xdoc.CreateNode(XmlNodeType.Element, "RSRotation", null); xmlRSRotation.InnerText = Convert.ToInt32(RSRotation[device] * 180.0 / Math.PI).ToString(); Node.AppendChild(xmlRSRotation); + XmlNode xmlSXD = m_Xdoc.CreateNode(XmlNodeType.Element, "SXDeadZone", null); xmlSXD.InnerText = SXDeadzone[device].ToString(); Node.AppendChild(xmlSXD); XmlNode xmlSZD = m_Xdoc.CreateNode(XmlNodeType.Element, "SZDeadZone", null); xmlSZD.InnerText = SZDeadzone[device].ToString(); Node.AppendChild(xmlSZD); + XmlNode xmlSXMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXMaxZone", null); xmlSXMaxzone.InnerText = Convert.ToInt32(SXMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXMaxzone); + XmlNode xmlSZMaxzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZMaxZone", null); xmlSZMaxzone.InnerText = Convert.ToInt32(SZMaxzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZMaxzone); + + XmlNode xmlSXAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SXAntiDeadZone", null); xmlSXAntiDeadzone.InnerText = Convert.ToInt32(SXAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSXAntiDeadzone); + XmlNode xmlSZAntiDeadzone = m_Xdoc.CreateNode(XmlNodeType.Element, "SZAntiDeadZone", null); xmlSZAntiDeadzone.InnerText = Convert.ToInt32(SZAntiDeadzone[device] * 100.0).ToString(); Node.AppendChild(xmlSZAntiDeadzone); + XmlNode xmlSens = m_Xdoc.CreateNode(XmlNodeType.Element, "Sensitivity", null); xmlSens.InnerText = $"{LSSens[device]}|{RSSens[device]}|{l2Sens[device]}|{r2Sens[device]}|{SXSens[device]}|{SZSens[device]}"; Node.AppendChild(xmlSens); - XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = chargingType[device].ToString(); Node.AppendChild(xmlChargingType); + XmlNode xmlChargingType = m_Xdoc.CreateNode(XmlNodeType.Element, "ChargingType", null); xmlChargingType.InnerText = lightInfo.chargingType.ToString(); Node.AppendChild(xmlChargingType); XmlNode xmlMouseAccel = m_Xdoc.CreateNode(XmlNodeType.Element, "MouseAcceleration", null); xmlMouseAccel.InnerText = mouseAccel[device].ToString(); Node.AppendChild(xmlMouseAccel); //XmlNode xmlShiftMod = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftModifier", null); xmlShiftMod.InnerText = shiftModifier[device].ToString(); Node.AppendChild(xmlShiftMod); XmlNode xmlLaunchProgram = m_Xdoc.CreateNode(XmlNodeType.Element, "LaunchProgram", null); xmlLaunchProgram.InnerText = launchProgram[device].ToString(); Node.AppendChild(xmlLaunchProgram); @@ -770,11 +2532,75 @@ public bool SaveProfile(int device, string propath) XmlNode xmlUseTPforControls = m_Xdoc.CreateNode(XmlNodeType.Element, "UseTPforControls", null); xmlUseTPforControls.InnerText = useTPforControls[device].ToString(); Node.AppendChild(xmlUseTPforControls); XmlNode xmlUseSAforMouse = m_Xdoc.CreateNode(XmlNodeType.Element, "UseSAforMouse", null); xmlUseSAforMouse.InnerText = useSAforMouse[device].ToString(); Node.AppendChild(xmlUseSAforMouse); XmlNode xmlSATriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggers", null); xmlSATriggers.InnerText = sATriggers[device].ToString(); Node.AppendChild(xmlSATriggers); + XmlNode xmlSATriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "SATriggerCond", null); xmlSATriggerCond.InnerText = SaTriggerCondString(sATriggerCond[device]); Node.AppendChild(xmlSATriggerCond); + XmlNode xmlSASteeringWheelEmulationAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationAxis", null); xmlSASteeringWheelEmulationAxis.InnerText = sASteeringWheelEmulationAxis[device].ToString("G"); Node.AppendChild(xmlSASteeringWheelEmulationAxis); + XmlNode xmlSASteeringWheelEmulationRange = m_Xdoc.CreateNode(XmlNodeType.Element, "SASteeringWheelEmulationRange", null); xmlSASteeringWheelEmulationRange.InnerText = sASteeringWheelEmulationRange[device].ToString(); Node.AppendChild(xmlSASteeringWheelEmulationRange); + + + + XmlNode xmlTouchDisInvTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "TouchDisInvTriggers", null); + string tempTouchDisInv = string.Join(",", touchDisInvertTriggers[device]); + xmlTouchDisInvTriggers.InnerText = tempTouchDisInv; + Node.AppendChild(xmlTouchDisInvTriggers); + XmlNode xmlGyroSensitivity = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensitivity", null); xmlGyroSensitivity.InnerText = gyroSensitivity[device].ToString(); Node.AppendChild(xmlGyroSensitivity); + XmlNode xmlGyroSensVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSensVerticalScale", null); xmlGyroSensVerticalScale.InnerText = gyroSensVerticalScale[device].ToString(); Node.AppendChild(xmlGyroSensVerticalScale); XmlNode xmlGyroInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroInvert", null); xmlGyroInvert.InnerText = gyroInvert[device].ToString(); Node.AppendChild(xmlGyroInvert); + XmlNode xmlGyroTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroTriggerTurns", null); xmlGyroTriggerTurns.InnerText = gyroTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroTriggerTurns); + XmlNode xmlGyroSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothingWeight", null); xmlGyroSmoothWeight.InnerText = Convert.ToInt32(gyroSmoothWeight[device] * 100).ToString(); Node.AppendChild(xmlGyroSmoothWeight); + XmlNode xmlGyroSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroSmoothing", null); xmlGyroSmoothing.InnerText = gyroSmoothing[device].ToString(); Node.AppendChild(xmlGyroSmoothing); + XmlNode xmlGyroMouseHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseHAxis", null); xmlGyroMouseHAxis.InnerText = gyroMouseHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMouseHAxis); + XmlNode xmlGyroMouseDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseDeadZone", null); xmlGyroMouseDZ.InnerText = gyroMouseDZ[device].ToString(); Node.AppendChild(xmlGyroMouseDZ); + XmlNode xmlGyroMouseToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseToggle", null); xmlGyroMouseToggle.InnerText = gyroMouseToggle[device].ToString(); Node.AppendChild(xmlGyroMouseToggle); + + XmlNode xmlGyroOutMode = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroOutputMode", null); xmlGyroOutMode.InnerText = GetGyroOutModeString(gyroOutMode[device]); Node.AppendChild(xmlGyroOutMode); + XmlNode xmlGyroMStickTriggers = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggers", null); xmlGyroMStickTriggers.InnerText = sAMouseStickTriggers[device].ToString(); Node.AppendChild(xmlGyroMStickTriggers); + XmlNode xmlGyroMStickTriggerCond = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerCond", null); xmlGyroMStickTriggerCond.InnerText = SaTriggerCondString(sAMouseStickTriggerCond[device]); Node.AppendChild(xmlGyroMStickTriggerCond); + XmlNode xmlGyroMStickTriggerTurns = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickTriggerTurns", null); xmlGyroMStickTriggerTurns.InnerText = gyroMouseStickTriggerTurns[device].ToString(); Node.AppendChild(xmlGyroMStickTriggerTurns); + XmlNode xmlGyroMStickHAxis = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickHAxis", null); xmlGyroMStickHAxis.InnerText = gyroMouseStickHorizontalAxis[device].ToString(); Node.AppendChild(xmlGyroMStickHAxis); + XmlNode xmlGyroMStickDZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickDeadZone", null); xmlGyroMStickDZ.InnerText = gyroMStickInfo[device].deadZone.ToString(); Node.AppendChild(xmlGyroMStickDZ); + XmlNode xmlGyroMStickMaxZ = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickMaxZone", null); xmlGyroMStickMaxZ.InnerText = gyroMStickInfo[device].maxZone.ToString(); Node.AppendChild(xmlGyroMStickMaxZ); + XmlNode xmlGyroMStickAntiDX = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadX", null); xmlGyroMStickAntiDX.InnerText = gyroMStickInfo[device].antiDeadX.ToString(); Node.AppendChild(xmlGyroMStickAntiDX); + XmlNode xmlGyroMStickAntiDY = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickAntiDeadY", null); xmlGyroMStickAntiDY.InnerText = gyroMStickInfo[device].antiDeadY.ToString(); Node.AppendChild(xmlGyroMStickAntiDY); + XmlNode xmlGyroMStickInvert = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickInvert", null); xmlGyroMStickInvert.InnerText = gyroMStickInfo[device].inverted.ToString(); Node.AppendChild(xmlGyroMStickInvert); + XmlNode xmlGyroMStickToggle = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickToggle", null); xmlGyroMStickToggle.InnerText = gyroMouseStickToggle[device].ToString(); Node.AppendChild(xmlGyroMStickToggle); + XmlNode xmlGyroMStickVerticalScale = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickVerticalScale", null); xmlGyroMStickVerticalScale.InnerText = gyroMStickInfo[device].vertScale.ToString(); Node.AppendChild(xmlGyroMStickVerticalScale); + XmlNode xmlGyroMStickSmoothing = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothing", null); xmlGyroMStickSmoothing.InnerText = gyroMStickInfo[device].useSmoothing.ToString(); Node.AppendChild(xmlGyroMStickSmoothing); + XmlNode xmlGyroMStickSmoothWeight = m_Xdoc.CreateNode(XmlNodeType.Element, "GyroMouseStickSmoothingWeight", null); xmlGyroMStickSmoothWeight.InnerText = Convert.ToInt32(gyroMStickInfo[device].smoothWeight * 100).ToString(); Node.AppendChild(xmlGyroMStickSmoothWeight); + XmlNode xmlLSC = m_Xdoc.CreateNode(XmlNodeType.Element, "LSCurve", null); xmlLSC.InnerText = lsCurve[device].ToString(); Node.AppendChild(xmlLSC); XmlNode xmlRSC = m_Xdoc.CreateNode(XmlNodeType.Element, "RSCurve", null); xmlRSC.InnerText = rsCurve[device].ToString(); Node.AppendChild(xmlRSC); XmlNode xmlProfileActions = m_Xdoc.CreateNode(XmlNodeType.Element, "ProfileActions", null); xmlProfileActions.InnerText = string.Join("/", profileActions[device]); Node.AppendChild(xmlProfileActions); + XmlNode xmlBTPollRate = m_Xdoc.CreateNode(XmlNodeType.Element, "BTPollRate", null); xmlBTPollRate.InnerText = btPollRate[device].ToString(); Node.AppendChild(xmlBTPollRate); + + XmlNode xmlLsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveMode", null); xmlLsOutputCurveMode.InnerText = stickOutputCurveString(getLsOutCurveMode(device)); Node.AppendChild(xmlLsOutputCurveMode); + XmlNode xmlLsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "LSOutputCurveCustom", null); xmlLsOutputCurveCustom.InnerText = lsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlLsOutputCurveCustom); + + XmlNode xmlRsOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveMode", null); xmlRsOutputCurveMode.InnerText = stickOutputCurveString(getRsOutCurveMode(device)); Node.AppendChild(xmlRsOutputCurveMode); + XmlNode xmlRsOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "RSOutputCurveCustom", null); xmlRsOutputCurveCustom.InnerText = rsOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlRsOutputCurveCustom); + + XmlNode xmlLsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "LSSquareStick", null); xmlLsSquareStickMode.InnerText = squStickInfo[device].lsMode.ToString(); Node.AppendChild(xmlLsSquareStickMode); + XmlNode xmlRsSquareStickMode = m_Xdoc.CreateNode(XmlNodeType.Element, "RSSquareStick", null); xmlRsSquareStickMode.InnerText = squStickInfo[device].rsMode.ToString(); Node.AppendChild(xmlRsSquareStickMode); + + XmlNode xmlSquareStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareStickRoundness", null); xmlSquareStickRoundness.InnerText = squStickInfo[device].lsRoundness.ToString(); Node.AppendChild(xmlSquareStickRoundness); + XmlNode xmlSquareRStickRoundness = m_Xdoc.CreateNode(XmlNodeType.Element, "SquareRStickRoundness", null); xmlSquareRStickRoundness.InnerText = squStickInfo[device].rsRoundness.ToString(); Node.AppendChild(xmlSquareRStickRoundness); + + XmlNode xmlL2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveMode", null); xmlL2OutputCurveMode.InnerText = axisOutputCurveString(getL2OutCurveMode(device)); Node.AppendChild(xmlL2OutputCurveMode); + XmlNode xmlL2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "L2OutputCurveCustom", null); xmlL2OutputCurveCustom.InnerText = l2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlL2OutputCurveCustom); + + XmlNode xmlR2OutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveMode", null); xmlR2OutputCurveMode.InnerText = axisOutputCurveString(getR2OutCurveMode(device)); Node.AppendChild(xmlR2OutputCurveMode); + XmlNode xmlR2OutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "R2OutputCurveCustom", null); xmlR2OutputCurveCustom.InnerText = r2OutBezierCurveObj[device].ToString(); Node.AppendChild(xmlR2OutputCurveCustom); + + XmlNode xmlSXOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveMode", null); xmlSXOutputCurveMode.InnerText = axisOutputCurveString(getSXOutCurveMode(device)); Node.AppendChild(xmlSXOutputCurveMode); + XmlNode xmlSXOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SXOutputCurveCustom", null); xmlSXOutputCurveCustom.InnerText = sxOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSXOutputCurveCustom); + + XmlNode xmlSZOutputCurveMode = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveMode", null); xmlSZOutputCurveMode.InnerText = axisOutputCurveString(getSZOutCurveMode(device)); Node.AppendChild(xmlSZOutputCurveMode); + XmlNode xmlSZOutputCurveCustom = m_Xdoc.CreateNode(XmlNodeType.Element, "SZOutputCurveCustom", null); xmlSZOutputCurveCustom.InnerText = szOutBezierCurveObj[device].ToString(); Node.AppendChild(xmlSZOutputCurveCustom); + + XmlNode xmlTrackBallMode = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballMode", null); xmlTrackBallMode.InnerText = trackballMode[device].ToString(); Node.AppendChild(xmlTrackBallMode); + XmlNode xmlTrackBallFriction = m_Xdoc.CreateNode(XmlNodeType.Element, "TrackballFriction", null); xmlTrackBallFriction.InnerText = trackballFriction[device].ToString(); Node.AppendChild(xmlTrackBallFriction); + + XmlNode xmlOutContDevice = m_Xdoc.CreateNode(XmlNodeType.Element, "OutputContDevice", null); xmlOutContDevice.InnerText = OutContDeviceString(outputDevType[device]); Node.AppendChild(xmlOutContDevice); XmlNode NodeControl = m_Xdoc.CreateNode(XmlNodeType.Element, "Control", null); XmlNode Key = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); @@ -799,16 +2625,21 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.action is string) + { if (dcs.action.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.keyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; - if (dcs.keyType.HasFlag(DS4KeyType.Macro)) + else if (dcs.keyType.HasFlag(DS4KeyType.Macro)) keyType += DS4KeyType.Macro; + if (dcs.keyType.HasFlag(DS4KeyType.Toggle)) keyType += DS4KeyType.Toggle; if (dcs.keyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -828,21 +2659,31 @@ public bool SaveProfile(int device, string propath) buttonNode.InnerText = dcs.action.ToString(); Key.AppendChild(buttonNode); } - else if (dcs.action is string || dcs.action is X360Controls) + else if (dcs.action is string) { buttonNode.InnerText = dcs.action.ToString(); Button.AppendChild(buttonNode); } + else if (dcs.action is X360Controls) + { + buttonNode.InnerText = getX360ControlString((X360Controls)dcs.action); + Button.AppendChild(buttonNode); + } } bool hasvalue = false; if (!string.IsNullOrEmpty(dcs.extras)) + { foreach (string s in dcs.extras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -856,8 +2697,11 @@ public bool SaveProfile(int device, string propath) string keyType = string.Empty; if (dcs.shiftAction is string) + { if (dcs.shiftAction.ToString() == "Unbound") keyType += DS4KeyType.Unbound; + } + if (dcs.shiftKeyType.HasFlag(DS4KeyType.HoldMacro)) keyType += DS4KeyType.HoldMacro; if (dcs.shiftKeyType.HasFlag(DS4KeyType.Macro)) @@ -866,6 +2710,7 @@ public bool SaveProfile(int device, string propath) keyType += DS4KeyType.Toggle; if (dcs.shiftKeyType.HasFlag(DS4KeyType.ScanCode)) keyType += DS4KeyType.ScanCode; + if (keyType != string.Empty) { buttonNode = m_Xdoc.CreateElement(dcs.control.ToString()); @@ -892,14 +2737,20 @@ public bool SaveProfile(int device, string propath) ShiftButton.AppendChild(buttonNode); } } + hasvalue = false; if (!string.IsNullOrEmpty(dcs.shiftExtras)) + { foreach (string s in dcs.shiftExtras.Split(',')) + { if (s != "0") { hasvalue = true; break; } + } + } + if (hasvalue) { XmlNode extraNode = m_Xdoc.CreateNode(XmlNodeType.Element, dcs.control.ToString(), null); @@ -907,6 +2758,7 @@ public bool SaveProfile(int device, string propath) ShiftExtras.AppendChild(extraNode); } } + Node.AppendChild(NodeControl); if (Button.HasChildNodes) NodeControl.AppendChild(Button); @@ -932,104 +2784,7 @@ public bool SaveProfile(int device, string propath) NodeShiftControl.AppendChild(ShiftKeyType); if (ShiftExtras.HasChildNodes) NodeShiftControl.AppendChild(ShiftExtras); - /*else if (xmlControls != null) - { - Node.AppendChild(xmlControls); - }*/ - /*if (shiftModifier[device] > 0) - { - XmlNode NodeShiftControl = m_Xdoc.CreateNode(XmlNodeType.Element, "ShiftControl", null); - - XmlNode ShiftKey = m_Xdoc.CreateNode(XmlNodeType.Element, "Key", null); - XmlNode ShiftMacro = m_Xdoc.CreateNode(XmlNodeType.Element, "Macro", null); - XmlNode ShiftKeyType = m_Xdoc.CreateNode(XmlNodeType.Element, "KeyType", null); - XmlNode ShiftButton = m_Xdoc.CreateNode(XmlNodeType.Element, "Button", null); - XmlNode ShiftExtras = m_Xdoc.CreateNode(XmlNodeType.Element, "Extras", null); - if (shiftbuttons != null) - { - foreach (var button in shiftbuttons) - { - // Save even if string (for xbox controller buttons) - if (button.Tag != null) - { - XmlNode buttonNode; - string keyType = String.Empty; - if (button.Tag is KeyValuePair) - if (((KeyValuePair)button.Tag).Key == "Unbound") - keyType += DS4KeyType.Unbound; - - if (button.Font.Strikeout) - keyType += DS4KeyType.HoldMacro; - if (button.Font.Underline) - keyType += DS4KeyType.Macro; - if (button.Font.Italic) - keyType += DS4KeyType.Toggle; - if (button.Font.Bold) - keyType += DS4KeyType.ScanCode; - if (keyType != String.Empty) - { - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - buttonNode.InnerText = keyType; - ShiftKeyType.AppendChild(buttonNode); - } - - string[] extras; - buttonNode = m_Xdoc.CreateNode(XmlNodeType.Element, button.Name, null); - if (button.Tag is KeyValuePair, string> || button.Tag is KeyValuePair || button.Tag is KeyValuePair) - { - KeyValuePair