cemu-DS4Windows/DS4Windows/DS4Forms/ControllerReadingsControl.xaml.cs
Travis Nickles 033e3f0c29 Copy state data for use in Controller Readings
Frees controller input thread more quickly
2020-04-01 21:29:04 -05:00

337 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NonFormTimer = System.Timers.Timer;
using DS4Windows;
namespace DS4WinWPF.DS4Forms
{
/// <summary>
/// Interaction logic for ControllerReadingsControl.xaml
/// </summary>
public partial class ControllerReadingsControl : UserControl
{
private enum LatencyWarnMode : uint
{
None,
Caution,
Warn,
}
private int deviceNum;
private event EventHandler DeviceNumChanged;
private NonFormTimer readingTimer;
private bool useTimer;
private double lsDead;
private double rsDead;
private double sixAxisXDead;
private double sixAxisZDead;
private double l2Dead;
private double r2Dead;
public double LsDead
{
get => lsDead;
set
{
lsDead = value;
LsDeadChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler LsDeadChanged;
public double RsDead
{
get => rsDead;
set
{
rsDead = value;
RsDeadChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler RsDeadChanged;
public double SixAxisXDead
{
get => sixAxisXDead;
set
{
sixAxisXDead = value;
SixAxisDeadXChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler SixAxisDeadXChanged;
public double SixAxisZDead
{
get => sixAxisZDead;
set
{
sixAxisZDead = value;
SixAxisDeadZChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler SixAxisDeadZChanged;
public double L2Dead
{
get => l2Dead;
set
{
l2Dead = value;
L2DeadChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler L2DeadChanged;
public double R2Dead
{
get => r2Dead;
set
{
r2Dead = value;
R2DeadChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler R2DeadChanged;
private LatencyWarnMode warnMode;
private LatencyWarnMode prevWarnMode;
private DS4State baseState = new DS4State();
private DS4State interState = new DS4State();
private DS4StateExposed exposeState;
private const int CANVAS_WIDTH = 130;
private const int CANVAS_MIDPOINT = CANVAS_WIDTH / 2;
private const double TRIG_LB_TRANSFORM_OFFSETY = 66.0;
public ControllerReadingsControl()
{
InitializeComponent();
inputContNum.Content = $"#{deviceNum+1}";
exposeState = new DS4StateExposed(baseState);
readingTimer = new NonFormTimer();
readingTimer.Interval = 1000 / 60.0;
LsDeadChanged += ChangeLsDeadControls;
RsDeadChanged += ChangeRsDeadControls;
SixAxisDeadXChanged += ChangeSixAxisDeadControls;
SixAxisDeadZChanged += ChangeSixAxisDeadControls;
DeviceNumChanged += ControllerReadingsControl_DeviceNumChanged;
}
private void ControllerReadingsControl_DeviceNumChanged(object sender, EventArgs e)
{
inputContNum.Content = $"#{deviceNum+1}";
}
private void ChangeSixAxisDeadControls(object sender, EventArgs e)
{
sixAxisDeadEllipse.Width = sixAxisXDead * CANVAS_WIDTH;
sixAxisDeadEllipse.Height = sixAxisZDead * CANVAS_WIDTH;
Canvas.SetLeft(sixAxisDeadEllipse, CANVAS_MIDPOINT - (sixAxisXDead * CANVAS_WIDTH / 2.0));
Canvas.SetTop(sixAxisDeadEllipse, CANVAS_MIDPOINT - (sixAxisZDead * CANVAS_WIDTH / 2.0));
}
private void ChangeRsDeadControls(object sender, EventArgs e)
{
rsDeadEllipse.Width = rsDead * CANVAS_WIDTH;
rsDeadEllipse.Height = rsDead * CANVAS_WIDTH;
Canvas.SetLeft(rsDeadEllipse, CANVAS_MIDPOINT - (rsDead * CANVAS_WIDTH / 2.0));
Canvas.SetTop(rsDeadEllipse, CANVAS_MIDPOINT - (rsDead * CANVAS_WIDTH / 2.0));
}
private void ChangeLsDeadControls(object sender, EventArgs e)
{
lsDeadEllipse.Width = lsDead * CANVAS_WIDTH;
lsDeadEllipse.Height = lsDead * CANVAS_WIDTH;
Canvas.SetLeft(lsDeadEllipse, CANVAS_MIDPOINT - (lsDead * CANVAS_WIDTH / 2.0));
Canvas.SetTop(lsDeadEllipse, CANVAS_MIDPOINT - (lsDead * CANVAS_WIDTH / 2.0));
}
public void UseDevice(int index)
{
deviceNum = index;
DeviceNumChanged?.Invoke(this, EventArgs.Empty);
}
public void EnableControl(bool state)
{
if (state)
{
IsEnabled = true;
useTimer = true;
readingTimer.Elapsed += ControllerReadingTimer_Elapsed;
readingTimer.Start();
}
else
{
IsEnabled = false;
useTimer = false;
readingTimer.Elapsed -= ControllerReadingTimer_Elapsed;
readingTimer.Stop();
}
}
private void ControllerReadingTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
readingTimer.Stop();
DS4Device ds = Program.rootHub.DS4Controllers[deviceNum];
if (ds != null)
{
// Don't bother waiting for UI thread to grab references
//DS4StateExposed tmpexposeState = Program.rootHub.ExposedState[deviceNum];
DS4State tmpbaseState = Program.rootHub.getDS4State(deviceNum);
DS4State tmpinterState = Program.rootHub.getDS4StateTemp(deviceNum);
Dispatcher.Invoke(() =>
{
// Wait for controller to be in a wait period
ds.ReadWaitEv.Wait();
ds.ReadWaitEv.Reset();
// Make copy of current state values for UI thread
tmpbaseState.CopyTo(baseState);
tmpinterState.CopyTo(interState);
// Done with copying. Allow input thread to resume
ds.ReadWaitEv.Set();
int x = baseState.LX;
int y = baseState.LY;
Canvas.SetLeft(lsValRec, x / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(lsValRec, y / 255.0 * CANVAS_WIDTH - 3);
//bool mappedLS = interState.LX != x || interState.LY != y;
//if (mappedLS)
//{
Canvas.SetLeft(lsMapValRec, interState.LX / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(lsMapValRec, interState.LY / 255.0 * CANVAS_WIDTH - 3);
//}
x = baseState.RX;
y = baseState.RY;
Canvas.SetLeft(rsValRec, x / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(rsValRec, y / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetLeft(rsMapValRec, interState.RX / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(rsMapValRec, interState.RY / 255.0 * CANVAS_WIDTH - 3);
x = exposeState.getAccelX() + 127;
y = exposeState.getAccelZ() + 127;
Canvas.SetLeft(sixAxisValRec, x / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(sixAxisValRec, y / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetLeft(sixAxisMapValRec, Math.Min(Math.Max(interState.Motion.outputAccelX + 127.0, 0), 255.0) / 255.0 * CANVAS_WIDTH - 3);
Canvas.SetTop(sixAxisMapValRec, Math.Min(Math.Max(interState.Motion.outputAccelZ + 127.0, 0), 255.0) / 255.0 * CANVAS_WIDTH - 3);
l2Slider.Value = baseState.L2;
l2ValLbTrans.Y = Math.Min(interState.L2, Math.Max(0, 255)) / 255.0 * -70.0 + TRIG_LB_TRANSFORM_OFFSETY;
if (interState.L2 >= 255)
{
l2ValLbBrush.Color = Colors.Green;
}
else if (interState.L2 == 0)
{
l2ValLbBrush.Color = Colors.Red;
}
else
{
l2ValLbBrush.Color = Colors.Black;
}
r2Slider.Value = baseState.R2;
r2ValLbTrans.Y = Math.Min(interState.R2, Math.Max(0, 255)) / 255.0 * -70.0 + TRIG_LB_TRANSFORM_OFFSETY;
if (interState.R2 >= 255)
{
r2ValLbBrush.Color = Colors.Green;
}
else if (interState.R2 == 0)
{
r2ValLbBrush.Color = Colors.Red;
}
else
{
r2ValLbBrush.Color = Colors.Black;
}
gyroYawSlider.Value = baseState.Motion.gyroYawFull;
gyroPitchSlider.Value = baseState.Motion.gyroPitchFull;
gyroRollSlider.Value = baseState.Motion.gyroRollFull;
accelXSlider.Value = exposeState.getAccelX();
accelYSlider.Value = exposeState.getAccelY();
accelZSlider.Value = exposeState.getAccelZ();
double latency = ds.Latency;
int warnInterval = ds.getWarnInterval();
inputDelayLb.Content = string.Format(Properties.Resources.InputDelay,
latency.ToString());
if (latency > warnInterval)
{
warnMode = LatencyWarnMode.Warn;
inpuDelayBackBrush.Color = Colors.Red;
inpuDelayForeBrush.Color = Colors.White;
}
else if (latency > (warnInterval * 0.5))
{
warnMode = LatencyWarnMode.Caution;
inpuDelayBackBrush.Color = Colors.Yellow;
inpuDelayForeBrush.Color = Colors.Black;
}
else
{
warnMode = LatencyWarnMode.None;
inpuDelayBackBrush.Color = Colors.Transparent;
inpuDelayForeBrush.Color = SystemColors.WindowTextColor;
}
prevWarnMode = warnMode;
UpdateCoordLabels(baseState, interState, exposeState);
});
}
if (useTimer)
{
readingTimer.Start();
}
}
private void UpdateCoordLabels(DS4State inState, DS4State mapState,
DS4StateExposed exposeState)
{
lxInValLb.Content = inState.LX;
lxOutValLb.Content = mapState.LX;
lyInValLb.Content = inState.LY;
lyOutValLb.Content = mapState.LY;
rxInValLb.Content = inState.RX;
rxOutValLb.Content = mapState.RX;
ryInValLb.Content = inState.RY;
ryOutValLb.Content = mapState.RY;
sixAxisXInValLb.Content = exposeState.AccelX;
sixAxisXOutValLb.Content = mapState.Motion.outputAccelX;
sixAxisZInValLb.Content = exposeState.AccelZ;
sixAxisZOutValLb.Content = mapState.Motion.outputAccelZ;
l2InValLb.Content = inState.L2;
l2OutValLb.Content = mapState.L2;
r2InValLb.Content = inState.R2;
r2OutValLb.Content = mapState.R2;
}
}
}