mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-01-20 15:30:28 +01:00
SteamController: Use Roslyn Scripting to compile UserProfiles
- This looks into `UserProfiles/` and compiles user profiles - This exposes a very minimal scripting interface as defined by `Dynamic.Globals`
This commit is contained in:
parent
b24ae302b1
commit
e5debff45b
|
|
@ -10,6 +10,12 @@ namespace SteamController
|
|||
public const String Title = "Steam Controller";
|
||||
public static readonly String TitleWithVersion = Title + " v" + Application.ProductVersion.ToString();
|
||||
|
||||
public static readonly Dictionary<String, Profiles.Profile> PreconfiguredUserProfiles = new Dictionary<String, Profiles.Profile>()
|
||||
{
|
||||
{ "*.desktop.cs", new Profiles.Predefined.DesktopProfile() { Name = "Desktop" } },
|
||||
{ "*.x360.cs", new Profiles.Predefined.X360HapticProfile() { Name = "X360" } }
|
||||
};
|
||||
|
||||
Container components = new Container();
|
||||
NotifyIcon notifyIcon;
|
||||
StartupManager startupManager = new StartupManager(Title);
|
||||
|
|
@ -55,17 +61,39 @@ namespace SteamController
|
|||
startupManager.Startup = false;
|
||||
});
|
||||
|
||||
// Set available profiles
|
||||
ProfilesSettings.Helpers.ProfileStringConverter.Profiles = context.Profiles.
|
||||
Where((profile) => profile.Visible).
|
||||
Select((profile) => profile.Name).ToArray();
|
||||
|
||||
Instance.RunOnce(TitleWithVersion, "Global\\SteamController");
|
||||
Instance.RunUpdater(TitleWithVersion);
|
||||
|
||||
if (Instance.WantsRunOnStartup)
|
||||
startupManager.Startup = true;
|
||||
|
||||
notifyIcon = new NotifyIcon(components);
|
||||
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.microsoft_xbox_controller_off_white : Resources.microsoft_xbox_controller_off;
|
||||
notifyIcon.Text = TitleWithVersion;
|
||||
notifyIcon.Visible = true;
|
||||
|
||||
#if DEBUG
|
||||
foreach (var profile in Profiles.Dynamic.RoslynDynamicProfile.GetUserProfiles(PreconfiguredUserProfiles))
|
||||
{
|
||||
profile.ErrorsChanged += (errors) =>
|
||||
{
|
||||
notifyIcon.ShowBalloonTip(
|
||||
3000, profile.Name,
|
||||
String.Join("\n", errors),
|
||||
ToolTipIcon.Error
|
||||
);
|
||||
};
|
||||
profile.Compile();
|
||||
profile.Watch();
|
||||
context.Profiles.Add(profile);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set available profiles
|
||||
ProfilesSettings.Helpers.ProfileStringConverter.Profiles = context.Profiles.
|
||||
Where((profile) => profile.Visible).
|
||||
Select((profile) => profile.Name).ToArray();
|
||||
|
||||
var contextMenu = new ContextMenuStrip(components);
|
||||
|
||||
var enabledItem = new ToolStripMenuItem("&Enabled");
|
||||
|
|
@ -81,7 +109,12 @@ namespace SteamController
|
|||
|
||||
var profileItem = new ToolStripMenuItem(profile.Name);
|
||||
profileItem.Click += delegate { context.SelectProfile(profile.Name); };
|
||||
contextMenu.Opening += delegate { profileItem.Checked = context.CurrentProfile == profile; };
|
||||
contextMenu.Opening += delegate
|
||||
{
|
||||
profileItem.Checked = context.CurrentProfile == profile;
|
||||
profileItem.ToolTipText = String.Join("\n", profile.Errors ?? new string[0]);
|
||||
profileItem.Enabled = profile.Errors is null;
|
||||
};
|
||||
contextMenu.Items.Add(profileItem);
|
||||
}
|
||||
|
||||
|
|
@ -116,10 +149,6 @@ namespace SteamController
|
|||
var exitItem = contextMenu.Items.Add("&Exit");
|
||||
exitItem.Click += delegate { Application.Exit(); };
|
||||
|
||||
notifyIcon = new NotifyIcon(components);
|
||||
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.microsoft_xbox_controller_off_white : Resources.microsoft_xbox_controller_off;
|
||||
notifyIcon.Text = TitleWithVersion;
|
||||
notifyIcon.Visible = true;
|
||||
notifyIcon.ContextMenuStrip = contextMenu;
|
||||
|
||||
var contextStateUpdate = new System.Windows.Forms.Timer(components);
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ namespace SteamController.Devices
|
|||
|
||||
/// Generated when button was repeated for a given period
|
||||
/// but triggered exactly once
|
||||
public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, string consume)
|
||||
public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, string? consume)
|
||||
{
|
||||
// always generate at least one keypress
|
||||
if (Pressed(duration))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace SteamController.Devices
|
|||
private Stopwatch stopwatch = new Stopwatch();
|
||||
private TimeSpan? lastUpdate;
|
||||
private int failures;
|
||||
public long ElapsedMilliseconds { get => stopwatch.ElapsedMilliseconds; }
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
internal SteamController()
|
||||
|
|
|
|||
244
SteamController/Profiles/Dynamic/Globals.cs
Normal file
244
SteamController/Profiles/Dynamic/Globals.cs
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
using Nefarius.ViGEm.Client.Targets.Xbox360;
|
||||
using SteamController.ProfilesSettings;
|
||||
|
||||
namespace SteamController.Profiles.Dynamic
|
||||
{
|
||||
[Flags]
|
||||
public enum KeyModifiers
|
||||
{
|
||||
MOD_None = 0,
|
||||
MOD_SHIFT = 1,
|
||||
MOD_ALT = 2,
|
||||
MOD_CONTROL = 4,
|
||||
MOD_WIN = 8
|
||||
}
|
||||
|
||||
public class Globals
|
||||
{
|
||||
private const string Consumed = "RoslynGlobals";
|
||||
|
||||
private RoslynDynamicProfile _profile;
|
||||
private Context _context;
|
||||
|
||||
public class SteamAPI
|
||||
{
|
||||
internal Devices.SteamController Target;
|
||||
internal SteamAPI(Devices.SteamController target) { Target = target; }
|
||||
|
||||
public struct Button
|
||||
{
|
||||
internal Devices.SteamButton Target;
|
||||
public static implicit operator bool(Button button) => button.Target;
|
||||
public Button(Devices.SteamButton target) { Target = target; }
|
||||
}
|
||||
|
||||
public struct Axis
|
||||
{
|
||||
internal Devices.SteamAxis Target;
|
||||
public static implicit operator short(Axis button) => button.Target;
|
||||
public Axis(Devices.SteamAxis target) { Target = target; }
|
||||
}
|
||||
|
||||
public Button BtnL5 { get => new Button(Target.BtnL5); }
|
||||
public Button BtnOptions { get => new Button(Target.BtnOptions); }
|
||||
public Button BtnSteam { get => new Button(Target.BtnSteam); }
|
||||
public Button BtnMenu { get => new Button(Target.BtnMenu); }
|
||||
public Button BtnDpadDown { get => new Button(Target.BtnDpadDown); }
|
||||
public Button BtnDpadLeft { get => new Button(Target.BtnDpadLeft); }
|
||||
public Button BtnDpadRight { get => new Button(Target.BtnDpadRight); }
|
||||
public Button BtnDpadUp { get => new Button(Target.BtnDpadUp); }
|
||||
public Button BtnA { get => new Button(Target.BtnA); }
|
||||
public Button BtnX { get => new Button(Target.BtnX); }
|
||||
public Button BtnB { get => new Button(Target.BtnB); }
|
||||
public Button BtnY { get => new Button(Target.BtnY); }
|
||||
public Button BtnL1 { get => new Button(Target.BtnL1); }
|
||||
public Button BtnL2 { get => new Button(Target.BtnL2); }
|
||||
public Button BtnR1 { get => new Button(Target.BtnR1); }
|
||||
public Button BtnR2 { get => new Button(Target.BtnR2); }
|
||||
public Button BtnLeftStickPress { get => new Button(Target.BtnLeftStickPress); }
|
||||
public Button BtnLPadTouch { get => new Button(Target.BtnLPadTouch); }
|
||||
public Button BtnLPadPress { get => new Button(Target.BtnLPadPress); }
|
||||
public Button BtnRPadPress { get => new Button(Target.BtnRPadPress); }
|
||||
public Button BtnRPadTouch { get => new Button(Target.BtnRPadTouch); }
|
||||
public Button BtnR5 { get => new Button(Target.BtnR5); }
|
||||
public Button BtnRightStickPress { get => new Button(Target.BtnRightStickPress); }
|
||||
public Button BtnLStickTouch { get => new Button(Target.BtnLStickTouch); }
|
||||
public Button BtnRStickTouch { get => new Button(Target.BtnRStickTouch); }
|
||||
public Button BtnR4 { get => new Button(Target.BtnR4); }
|
||||
public Button BtnL4 { get => new Button(Target.BtnL4); }
|
||||
public Button BtnQuickAccess { get => new Button(Target.BtnQuickAccess); }
|
||||
|
||||
public Button BtnVirtualLeftThumbUp { get => new Button(Target.BtnVirtualLeftThumbUp); }
|
||||
public Button BtnVirtualLeftThumbDown { get => new Button(Target.BtnVirtualLeftThumbDown); }
|
||||
public Button BtnVirtualLeftThumbLeft { get => new Button(Target.BtnVirtualLeftThumbLeft); }
|
||||
public Button BtnVirtualLeftThumbRight { get => new Button(Target.BtnVirtualLeftThumbRight); }
|
||||
|
||||
public Axis LPadX { get => new Axis(Target.LPadX); }
|
||||
public Axis LPadY { get => new Axis(Target.LPadY); }
|
||||
public Axis RPadX { get => new Axis(Target.RPadX); }
|
||||
public Axis RPadY { get => new Axis(Target.RPadY); }
|
||||
public Axis AccelX { get => new Axis(Target.AccelX); }
|
||||
public Axis AccelY { get => new Axis(Target.AccelY); }
|
||||
public Axis AccelZ { get => new Axis(Target.AccelZ); }
|
||||
public Axis GyroPitch { get => new Axis(Target.GyroPitch); }
|
||||
public Axis GyroYaw { get => new Axis(Target.GyroYaw); }
|
||||
public Axis GyroRoll { get => new Axis(Target.GyroRoll); }
|
||||
public Axis LeftTrigger { get => new Axis(Target.LeftTrigger); }
|
||||
public Axis RightTrigger { get => new Axis(Target.RightTrigger); }
|
||||
public Axis LeftThumbX { get => new Axis(Target.LeftThumbX); }
|
||||
public Axis LeftThumbY { get => new Axis(Target.LeftThumbY); }
|
||||
public Axis RightThumbX { get => new Axis(Target.RightThumbX); }
|
||||
public Axis RightThumbY { get => new Axis(Target.RightThumbY); }
|
||||
public Axis LPadPressure { get => new Axis(Target.LPadPressure); }
|
||||
public Axis RPadPressure { get => new Axis(Target.RPadPressure); }
|
||||
}
|
||||
|
||||
public class KeyboardAPI
|
||||
{
|
||||
internal Devices.KeyboardController Target;
|
||||
internal KeyboardAPI(Devices.KeyboardController target) { Target = target; }
|
||||
|
||||
public bool this[VirtualKeyCode key]
|
||||
{
|
||||
get { return Target[key.ToWindowsInput()]; }
|
||||
set { Target.Overwrite(key.ToWindowsInput(), value); }
|
||||
}
|
||||
|
||||
public void KeyPress(params VirtualKeyCode[] keyCodes)
|
||||
{
|
||||
KeyPress(KeyModifiers.MOD_None, keyCodes);
|
||||
}
|
||||
|
||||
public void KeyPress(KeyModifiers modifiers, params VirtualKeyCode[] keyCodes)
|
||||
{
|
||||
var virtualCodes = keyCodes.Select((code) => (WindowsInput.VirtualKeyCode)code).ToArray();
|
||||
|
||||
if (modifiers != KeyModifiers.MOD_None)
|
||||
{
|
||||
List<WindowsInput.VirtualKeyCode> modifierCodes = new List<WindowsInput.VirtualKeyCode>();
|
||||
if (modifiers.HasFlag(KeyModifiers.MOD_SHIFT))
|
||||
modifierCodes.Add(WindowsInput.VirtualKeyCode.SHIFT);
|
||||
if (modifiers.HasFlag(KeyModifiers.MOD_CONTROL))
|
||||
modifierCodes.Add(WindowsInput.VirtualKeyCode.CONTROL);
|
||||
if (modifiers.HasFlag(KeyModifiers.MOD_ALT))
|
||||
modifierCodes.Add(WindowsInput.VirtualKeyCode.MENU);
|
||||
if (modifiers.HasFlag(KeyModifiers.MOD_WIN))
|
||||
modifierCodes.Add(WindowsInput.VirtualKeyCode.LWIN);
|
||||
Target.KeyPress(modifierCodes, virtualCodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Target.KeyPress(virtualCodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MouseAPI
|
||||
{
|
||||
internal Devices.MouseController Target;
|
||||
internal MouseAPI(Devices.MouseController target) { Target = target; }
|
||||
|
||||
public struct Button
|
||||
{
|
||||
internal Devices.MouseController? Controller = null;
|
||||
internal Devices.MouseController.Button Target = Devices.MouseController.Button.Left;
|
||||
internal bool Value;
|
||||
|
||||
internal Button(Devices.MouseController controller, Devices.MouseController.Button target) { Controller = controller; Target = target; Value = Controller[Target]; }
|
||||
internal Button(bool value) { this.Value = value; }
|
||||
|
||||
public void Click() { Controller?.MouseClick(Target); }
|
||||
public void DoubleClick() { Controller?.MouseClick(Target); }
|
||||
|
||||
internal void Set(Button value) { Controller?.Overwrite(Target, value.Value); }
|
||||
|
||||
public static implicit operator Button(bool value) { return new Button(value); }
|
||||
}
|
||||
|
||||
public Button BtnLeft { get => new Button(Target, Devices.MouseController.Button.Left); set => this.BtnLeft.Set(value); }
|
||||
public Button BtnRight { get => new Button(Target, Devices.MouseController.Button.Right); set => this.BtnRight.Set(value); }
|
||||
public Button BtnMiddle { get => new Button(Target, Devices.MouseController.Button.Middle); set => this.BtnMiddle.Set(value); }
|
||||
public Button BtnX { get => new Button(Target, Devices.MouseController.Button.X); set => this.BtnX.Set(value); }
|
||||
public Button BtnY { get => new Button(Target, Devices.MouseController.Button.Y); set => this.BtnY.Set(value); }
|
||||
|
||||
public void MoveBy(double pixelDeltaX, double pixelDeltaY) { Target.MoveBy(pixelDeltaX, pixelDeltaY); }
|
||||
public void MoveTo(double absoluteX, double absoluteY) { Target.MoveTo(absoluteX, absoluteY); }
|
||||
public void VerticalScroll(double scrollAmountInClicks) { Target.VerticalScroll(scrollAmountInClicks); }
|
||||
public void HorizontalScroll(double scrollAmountInClicks) { Target.HorizontalScroll(scrollAmountInClicks); }
|
||||
}
|
||||
|
||||
public class X360API
|
||||
{
|
||||
internal const int MinimumPresTimeMilliseconds = 30;
|
||||
internal Devices.Xbox360Controller Target;
|
||||
internal X360API(Devices.Xbox360Controller target) { Target = target; }
|
||||
|
||||
public bool Connected { set => Target.Connected = value; }
|
||||
|
||||
public bool BtnUp { set => Target.Overwrite(Xbox360Button.Up, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnDown { set => Target.Overwrite(Xbox360Button.Down, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnLeft { set => Target.Overwrite(Xbox360Button.Left, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnRight { set => Target.Overwrite(Xbox360Button.Right, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnA { set => Target.Overwrite(Xbox360Button.A, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnB { set => Target.Overwrite(Xbox360Button.B, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnX { set => Target.Overwrite(Xbox360Button.X, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnY { set => Target.Overwrite(Xbox360Button.Y, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnGuide { set => Target.Overwrite(Xbox360Button.Guide, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnBack { set => Target.Overwrite(Xbox360Button.Back, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnStart { set => Target.Overwrite(Xbox360Button.Start, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnLeftShoulder { set => Target.Overwrite(Xbox360Button.LeftShoulder, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnRightShoulder { set => Target.Overwrite(Xbox360Button.RightShoulder, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnLeftThumb { set => Target.Overwrite(Xbox360Button.LeftThumb, value, MinimumPresTimeMilliseconds); }
|
||||
public bool BtnRightThumb { set => Target.Overwrite(Xbox360Button.RightThumb, value, MinimumPresTimeMilliseconds); }
|
||||
public short AxisLeftThumbX { set => Target[Xbox360Axis.LeftThumbX] = value; }
|
||||
public short AxisLeftThumbY { set => Target[Xbox360Axis.LeftThumbY] = value; }
|
||||
public short AxisRightThumbX { set => Target[Xbox360Axis.RightThumbX] = value; }
|
||||
public short AxisRightThumbY { set => Target[Xbox360Axis.RightThumbY] = value; }
|
||||
public short SliderLeftTrigger { set => Target[Xbox360Slider.LeftTrigger] = value; }
|
||||
public short SliderRightTrigger { set => Target[Xbox360Slider.RightTrigger] = value; }
|
||||
}
|
||||
|
||||
private SteamAPI? _steamAPI;
|
||||
private X360API? _x360API;
|
||||
private KeyboardAPI? _keyboardAPI;
|
||||
private MouseAPI? _mouseAPI;
|
||||
|
||||
public SteamAPI Steam { get => _steamAPI ??= new SteamAPI(_context.Steam); }
|
||||
public X360API X360 { get => _x360API ??= new X360API(_context.X360); }
|
||||
public KeyboardAPI Keyboard { get => _keyboardAPI ??= new KeyboardAPI(_context.Keyboard); }
|
||||
public MouseAPI Mouse { get => _mouseAPI ??= new MouseAPI(_context.Mouse); }
|
||||
|
||||
public Globals(RoslynDynamicProfile profile, Context context)
|
||||
{
|
||||
this._profile = profile;
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
public bool Pressed(SteamAPI.Button button)
|
||||
{
|
||||
return button.Target.Pressed();
|
||||
}
|
||||
|
||||
public bool JustPressed(SteamAPI.Button button)
|
||||
{
|
||||
return button.Target.JustPressed();
|
||||
}
|
||||
|
||||
public bool HoldFor(SteamAPI.Button button, int minTimeMs)
|
||||
{
|
||||
return button.Target.Hold(TimeSpan.FromMilliseconds(minTimeMs), null);
|
||||
}
|
||||
|
||||
public bool Turbo(SteamAPI.Button button, int timesPerSec)
|
||||
{
|
||||
var interval = TimeSpan.FromMilliseconds(1000 / timesPerSec);
|
||||
return button.Target.JustPressed() || button.Target.HoldRepeat(interval, interval, null);
|
||||
}
|
||||
|
||||
public void Log(string format, params object?[] arg)
|
||||
{
|
||||
var output = String.Format(format, arg);
|
||||
CommonHelpers.Log.TraceLine("{0}: {1}: {2}", _profile.Name, _context.Steam.ElapsedMilliseconds, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
SteamController/Profiles/Dynamic/RoslynDynamicProfile.cs
Normal file
186
SteamController/Profiles/Dynamic/RoslynDynamicProfile.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using System.Reflection;
|
||||
using CommonHelpers;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
|
||||
namespace SteamController.Profiles.Dynamic
|
||||
{
|
||||
public sealed partial class RoslynDynamicProfile : Profile
|
||||
{
|
||||
private const int ScriptTimeout = 10; // max 10ms
|
||||
|
||||
private String fileName;
|
||||
private Script? compiledScript;
|
||||
private Profile? inherited;
|
||||
private DateTime? lastModifiedTime;
|
||||
private System.Windows.Forms.Timer? watchTimer;
|
||||
|
||||
public RoslynDynamicProfile(string name, string fileName, Profile? inherited = null)
|
||||
{
|
||||
this.fileName = fileName;
|
||||
this.inherited = inherited;
|
||||
this.Name = name;
|
||||
if (inherited is not null)
|
||||
this.Name = inherited.Name + ": " + name;
|
||||
this.Visible = inherited?.Visible ?? true;
|
||||
this.IsDesktop = inherited?.IsDesktop ?? false;
|
||||
}
|
||||
|
||||
private static ScriptOptions CompilationOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
var options = ScriptOptions.Default;
|
||||
|
||||
// Add Keyboard controls
|
||||
options = options.AddReferences(typeof(KeyModifiers).Assembly);
|
||||
options = options.AddImports(typeof(KeyModifiers).FullName);
|
||||
options = options.AddReferences(typeof(ProfilesSettings.VirtualKeyCode).Assembly);
|
||||
options = options.AddImports(typeof(ProfilesSettings.VirtualKeyCode).FullName);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Compile()
|
||||
{
|
||||
this.compiledScript = null;
|
||||
this.lastModifiedTime = null;
|
||||
this.Errors = null;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
this.Errors = new string[] { String.Format("File '{0}' does not exist.", fileName) };
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.lastModifiedTime = File.GetLastWriteTimeUtc(fileName);
|
||||
|
||||
using (var file = System.IO.File.OpenRead(fileName))
|
||||
{
|
||||
var options = CompilationOptions.WithFilePath(Path.GetFileName(fileName));
|
||||
var script = CSharpScript.Create(file, options, typeof(Globals));
|
||||
var compileResult = script.Compile();
|
||||
var errors = compileResult.Where((result) => result.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error);
|
||||
|
||||
if (!errors.Any())
|
||||
{
|
||||
this.compiledScript = script;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.Errors = errors.Select((result) => result.ToString()).ToArray();
|
||||
OnErrorsChanged();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.Errors = new string[] { e.Message };
|
||||
OnErrorsChanged();
|
||||
}
|
||||
|
||||
Log.TraceLine("UserProfile: {0}: Compilation Error", fileName);
|
||||
foreach (var error in this.Errors)
|
||||
Log.TraceLine("\t{0}", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Watch()
|
||||
{
|
||||
if (this.lastModifiedTime is null)
|
||||
return;
|
||||
if (this.watchTimer is not null)
|
||||
return;
|
||||
|
||||
watchTimer = new System.Windows.Forms.Timer();
|
||||
watchTimer.Interval = 1000;
|
||||
watchTimer.Tick += delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.lastModifiedTime is null)
|
||||
return;
|
||||
|
||||
var latest = File.GetLastWriteTimeUtc(fileName);
|
||||
if (this.lastModifiedTime >= latest)
|
||||
return;
|
||||
|
||||
Log.TraceLine("UserProfile: {0}. Detected modification: '{1}' vs '{2}'", fileName, this.lastModifiedTime, latest);
|
||||
}
|
||||
catch (Exception) { return; }
|
||||
|
||||
Compile();
|
||||
};
|
||||
watchTimer.Start();
|
||||
}
|
||||
|
||||
public override bool Selected(Context context)
|
||||
{
|
||||
return (this.compiledScript is not null) && (inherited?.Selected(context) ?? true);
|
||||
}
|
||||
|
||||
public override Status Run(Context context)
|
||||
{
|
||||
if (inherited?.Run(context).IsDone ?? false)
|
||||
return Status.Done;
|
||||
|
||||
if (this.compiledScript is null)
|
||||
return Status.Continue;
|
||||
|
||||
try
|
||||
{
|
||||
var cancelToken = new CancellationTokenSource();
|
||||
var task = this.compiledScript.RunAsync(new Globals(this, context), cancelToken.Token);
|
||||
if (!task.Wait(ScriptTimeout))
|
||||
{
|
||||
cancelToken.Cancel();
|
||||
task.Wait();
|
||||
Log.TraceLine("UserProfile: {0}: Timedout. Canceled.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.TraceLine("UserProfile: {0}: {1}", fileName, e);
|
||||
}
|
||||
|
||||
return Status.Continue;
|
||||
}
|
||||
|
||||
public static IEnumerable<RoslynDynamicProfile> GetUserProfiles(Dictionary<String, Profile> preconfiguredProfiles)
|
||||
{
|
||||
var files = new Dictionary<String, String>();
|
||||
|
||||
foreach (var directory in GetUserProfilesPaths())
|
||||
{
|
||||
foreach (var profile in preconfiguredProfiles)
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(directory, profile.Key))
|
||||
{
|
||||
String name = Path.GetFileNameWithoutExtension(file);
|
||||
name = Path.GetFileNameWithoutExtension(name);
|
||||
|
||||
yield return new RoslynDynamicProfile(name, file, profile.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<String> GetUserProfilesPaths()
|
||||
{
|
||||
var exePath = Assembly.GetExecutingAssembly().Location;
|
||||
var exeFolder = Path.GetDirectoryName(exePath);
|
||||
if (exeFolder is not null)
|
||||
{
|
||||
var exeUserProfiles = Path.Combine(exeFolder, "UserProfiles");
|
||||
if (Directory.Exists(exeUserProfiles))
|
||||
yield return exeUserProfiles;
|
||||
}
|
||||
|
||||
var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
var steamControllerDocumentsFolder = Path.Combine(documentsFolder, "SteamController", "UserProfiles");
|
||||
if (Directory.Exists(steamControllerDocumentsFolder))
|
||||
yield return steamControllerDocumentsFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,25 @@ namespace SteamController.Profiles
|
|||
public bool IsDone { get; set; }
|
||||
}
|
||||
|
||||
public event Action<string[]> ErrorsChanged;
|
||||
|
||||
public virtual String Name { get; set; } = "";
|
||||
public virtual bool Visible { get; set; } = true;
|
||||
public virtual bool IsDesktop { get; set; }
|
||||
public virtual string[]? Errors { get; set; }
|
||||
|
||||
public abstract bool Selected(Context context);
|
||||
|
||||
public abstract Status Run(Context context);
|
||||
|
||||
public Profile()
|
||||
{
|
||||
ErrorsChanged += delegate { };
|
||||
}
|
||||
|
||||
protected void OnErrorsChanged()
|
||||
{
|
||||
ErrorsChanged(this.Errors ?? new string[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
<ApplicationIcon>Resources\microsoft-xbox-controller.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="UserProfiles\Turbo.x360.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
|
@ -18,8 +22,15 @@
|
|||
<AdditionalFiles Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="UserProfiles\Turbo.x360.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="H.InputSimulator" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.4.0" />
|
||||
<PackageReference Include="Nefarius.ViGEm.Client" Version="1.21.232" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
9
SteamController/UserProfiles/Turbo.x360.cs
Normal file
9
SteamController/UserProfiles/Turbo.x360.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// If L5 is hold, the A, B, X, Y is turbo: 10x per second
|
||||
if (Steam.BtnL5)
|
||||
{
|
||||
X360.BtnA = Turbo(Steam.BtnA, 10);
|
||||
X360.BtnB = Turbo(Steam.BtnB, 10);
|
||||
X360.BtnX = Turbo(Steam.BtnX, 10);
|
||||
X360.BtnY = Turbo(Steam.BtnY, 10);
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue