Update to latest OTD API

This commit is contained in:
X9VoiD 2022-01-06 06:04:40 +08:00
parent 4e078b1436
commit 38100896f7
42 changed files with 492 additions and 632 deletions

View file

@ -0,0 +1,2 @@
<Project>
</Project>

View file

@ -0,0 +1,2 @@
<Project>
</Project>

@ -1 +1 @@
Subproject commit 267a32282ad5462a5be254e74f60edeba809831f
Subproject commit 78e9d03766e61f986d3b510dfd3b23c74917d895

View file

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System;
using System.Diagnostics;
using System.Linq;
using OpenTabletDriver.Plugin;
using OpenTabletDriver.Plugin.Attributes;
using OpenTabletDriver.Plugin.Tablet;
@ -10,82 +9,8 @@ namespace VoiDPlugins.Binding.ScriptRunner
[PluginName("Script Runner")]
public class ScriptRunner : IStateBinding
{
public static string[] ValidIndexes = Enumerable.Range(0, 10).Select(i => i.ToString()).ToArray();
private List<string> ScriptPathList = new List<string>(10);
[Property("Script Index"), PropertyValidated(nameof(ValidIndexes))]
public string ScriptIndex { set; get; }
[Property("Script Path 0")]
public string ScriptPath0
{
get => ScriptPathList[0];
set => ScriptPathList[0] = value;
}
[Property("Script Path 1")]
public string ScriptPath1
{
get => ScriptPathList[1];
set => ScriptPathList[1] = value;
}
[Property("Script Path 2")]
public string ScriptPath2
{
get => ScriptPathList[2];
set => ScriptPathList[2] = value;
}
[Property("Script Path 3")]
public string ScriptPath3
{
get => ScriptPathList[3];
set => ScriptPathList[3] = value;
}
[Property("Script Path 4")]
public string ScriptPath4
{
get => ScriptPathList[4];
set => ScriptPathList[4] = value;
}
[Property("Script Path 5")]
public string ScriptPath5
{
get => ScriptPathList[5];
set => ScriptPathList[5] = value;
}
[Property("Script Path 6")]
public string ScriptPath6
{
get => ScriptPathList[6];
set => ScriptPathList[6] = value;
}
[Property("Script Path 7")]
public string ScriptPath7
{
get => ScriptPathList[7];
set => ScriptPathList[7] = value;
}
[Property("Script Path 8")]
public string ScriptPath8
{
get => ScriptPathList[8];
set => ScriptPathList[8] = value;
}
[Property("Script Path 9")]
public string ScriptPath9
{
get => ScriptPathList[9];
set => ScriptPathList[9] = value;
}
[Property("Run")]
public string? Script { get; set; }
public void Press(TabletReference tablet, IDeviceReport report)
{
@ -93,14 +18,17 @@ namespace VoiDPlugins.Binding.ScriptRunner
{
var process = new Process
{
StartInfo = new ProcessStartInfo(ScriptPathList[int.Parse(ScriptIndex)])
StartInfo = new ProcessStartInfo(Script!)
{
UseShellExecute = true
}
};
process.Start();
}
catch { }
catch (Exception e)
{
Log.Exception(e);
}
}
public void Release(TabletReference tablet, IDeviceReport report)

View file

@ -1,17 +1,2 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
</ItemGroup>
</Project>

12
Directory.Build.props Normal file
View file

@ -0,0 +1,12 @@
<Project>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<Version>0.3.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>embedded</DebugType>
</PropertyGroup>
</Project>

7
Directory.Build.targets Normal file
View file

@ -0,0 +1,7 @@
<Project>
<ItemGroup>
<ProjectReference Condition="'$(NotPluginProject)' != 'true'" Include="$(MSBuildThisFileDirectory)/.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<ProjectReference Condition="'$(VMultiLibrary)' == 'true'" Include="$(MSBuildThisFileDirectory)/VoiDPlugins.Library/VMulti/VMulti.csproj" />
<ProjectReference Condition="'$(VoiDLibrary)' == 'true'" Include="$(MSBuildThisFileDirectory)/VoiDPlugins.Library/VoiD/VoiD.csproj" />
</ItemGroup>
</Project>

View file

@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using MathNet.Numerics;
@ -8,6 +9,28 @@ namespace VoiDPlugins.Filter.MeL.Core
{
internal partial class MLCore
{
private double[]? weights;
private Polynomial? xCoeff, yCoeff;
private RingBuffer<TimeSeriesPoint>? timeSeriesPoints;
private readonly Stopwatch watch = new();
public bool IsReady { private set; get; }
private int samples;
public int Samples
{
set
{
this.timeSeriesPoints = new RingBuffer<TimeSeriesPoint>(value);
this.samples = value;
}
get => this.samples;
}
public int Complexity { set; get; } = 2;
public float Weight { set => this.weights = CalcWeight(value).ToArray(); }
public double TimeNow { get => watch.Elapsed.TotalMilliseconds; }
public MLCore()
{
Weight = 1.4f;
@ -49,29 +72,61 @@ namespace VoiDPlugins.Filter.MeL.Core
{
var predicted = new Vector2();
double predictAhead;
predictAhead = TimeNow - this.timeSeriesPoints[0].Elapsed + offset;
predictAhead = TimeNow - this.timeSeriesPoints![0].Elapsed + offset;
predicted.X = (float)this.xCoeff.Evaluate(predictAhead);
predicted.Y = (float)this.yCoeff.Evaluate(predictAhead);
predicted.X = (float)this.xCoeff!.Evaluate(predictAhead);
predicted.Y = (float)this.yCoeff!.Evaluate(predictAhead);
return predicted;
}
public bool IsReady { private set; get; }
private int samples;
public int Samples
private bool AddTimeSeriesPoint(Vector2 point, double elapsed)
{
set
{
this.timeSeriesPoints = new RingBuffer<TimeSeriesPoint>(value);
this.samples = value;
}
get => this.samples;
this.timeSeriesPoints!.Insert(new TimeSeriesPoint(point, elapsed));
return this.timeSeriesPoints.IsFilled;
}
public int Complexity { set; get; } = 2;
public float Weight { set => this.weights = CalcWeight(value).ToArray(); }
public double TimeNow { get => watch.Elapsed.TotalMilliseconds; }
private double[] ConstructTimeDesignMatrix()
{
var baseTime = this.timeSeriesPoints![0].Elapsed;
var data = new double[Samples];
var index = 0;
foreach (var timePoint in this.timeSeriesPoints)
data[index++] = timePoint.Elapsed - baseTime;
return data;
}
private double[] ConstructTargetMatrix(Axis axis)
{
var points = new double[Samples];
var index = 0;
switch (axis)
{
case Axis.X:
foreach (var timePoint in timeSeriesPoints!)
points[index++] = timePoint.Point.X;
break;
case Axis.Y:
foreach (var timePoint in timeSeriesPoints!)
points[index++] = timePoint.Point.Y;
break;
}
return points;
}
private double[] CalcWeight(double ratio)
{
var weights = new double[Samples];
var weightsNormalized = new double[Samples];
double weight = 1;
for (int i = 0; i < Samples; i++)
weights[i] = weight *= ratio;
for (int i = 0; i < Samples; i++)
weightsNormalized[i] = weights[i] / weights[^1];
return weightsNormalized;
}
}
}

View file

@ -1,64 +0,0 @@
using System.Diagnostics;
using System.Numerics;
using MathNet.Numerics;
using VoiDPlugins.Library;
namespace VoiDPlugins.Filter.MeL.Core
{
internal partial class MLCore
{
private double[] weights;
private Polynomial xCoeff, yCoeff;
private RingBuffer<TimeSeriesPoint> timeSeriesPoints;
private readonly Stopwatch watch = new();
private bool AddTimeSeriesPoint(Vector2 point, double elapsed)
{
this.timeSeriesPoints.Insert(new TimeSeriesPoint(point, elapsed));
return this.timeSeriesPoints.IsFilled;
}
private double[] ConstructTimeDesignMatrix()
{
var baseTime = this.timeSeriesPoints[0].Elapsed;
var data = new double[Samples];
var index = 0;
foreach (var timePoint in this.timeSeriesPoints)
data[index++] = timePoint.Elapsed - baseTime;
return data;
}
private double[] ConstructTargetMatrix(Axis axis)
{
var points = new double[Samples];
var index = 0;
switch (axis)
{
case Axis.X:
foreach (var timePoint in timeSeriesPoints)
points[index++] = timePoint.Point.X;
break;
case Axis.Y:
foreach (var timePoint in timeSeriesPoints)
points[index++] = timePoint.Point.Y;
break;
}
return points;
}
private double[] CalcWeight(double ratio)
{
var weights = new double[Samples];
var weightsNormalized = new double[Samples];
double weight = 1;
for (int i = 0; i < Samples; i++)
weights[i] = weight *= ratio;
for (int i = 0; i < Samples; i++)
weightsNormalized[i] = weights[i] / weights[^1];
return weightsNormalized;
}
}
}

View file

@ -10,10 +10,10 @@ namespace VoiDPlugins.Filter.MeL
[PluginName("MeL Filter")]
public class MeLFilter : IPositionedPipelineElement<IDeviceReport>
{
private readonly MLCore Core = new MLCore();
private readonly MLCore Core = new();
private bool rateLimit;
public event Action<IDeviceReport> Emit;
public event Action<IDeviceReport>? Emit;
public PipelinePosition Position => PipelinePosition.PostTransform;

View file

@ -1,19 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
<VoiDLibrary>true</VoiDLibrary>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MathNet.Numerics" Version="4.15.0" />
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<ProjectReference Include="../../VoiDPlugins.Library/VoiD/VoiD.csproj" />
</ItemGroup>
</Project>

View file

@ -17,7 +17,7 @@ namespace VoiDPlugins.Filter
public static string[] ValidModes => new[] { "Toggle", "Hold" };
[Property("Mode"), PropertyValidated(nameof(ValidModes))]
public string Mode { set; get; }
public string? Mode { set; get; }
public void Press(TabletReference tablet, IDeviceReport report)
{
@ -39,7 +39,7 @@ namespace VoiDPlugins.Filter
[PluginName("Precision Control")]
public class PrecisionControl : IPositionedPipelineElement<IDeviceReport>
{
public event Action<IDeviceReport> Emit;
public event Action<IDeviceReport>? Emit;
[SliderProperty("Precision Multiplier", 0.0f, 10f, 0.3f), DefaultPropertyValue(0.3f)]
public float Scale { get; set; }

View file

@ -1,18 +1,2 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
</ItemGroup>
</Project>

View file

@ -25,7 +25,7 @@ namespace VoiDPlugins.Filter
get => weight;
}
public event Action<IDeviceReport> Emit;
public event Action<IDeviceReport>? Emit;
public PipelinePosition Position => PipelinePosition.PreTransform;

View file

@ -1,18 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<VoiDLibrary>true</VoiDLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<ProjectReference Include="../../VoiDPlugins.Library/VoiD/VoiD.csproj" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace VoiDPlugins.OutputMode
@ -165,7 +166,7 @@ namespace VoiDPlugins.OutputMode
[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static unsafe extern bool GetPointerDevices(out uint deviceCount, [In, Out, MarshalAs(UnmanagedType.LPArray)] POINTER_DEVICE_INFO[] pointerDevices);
public static unsafe extern bool GetPointerDevices(out uint deviceCount, [In, Out, MarshalAs(UnmanagedType.LPArray), AllowNull] POINTER_DEVICE_INFO[] pointerDevices);
[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetForegroundWindow();

View file

@ -6,7 +6,7 @@ namespace VoiDPlugins.OutputMode
public static class Touch
{
private static IntPtr _penHandle;
private static POINTER_TYPE_INFO[] pointer;
private static POINTER_TYPE_INFO[]? pointer;
private static uint _pointerId;
private static IntPtr _sourceDevice;
@ -79,7 +79,7 @@ namespace VoiDPlugins.OutputMode
public static void Inject()
{
if (!NativeMethods.InjectSyntheticPointerInput(_penHandle, pointer, 1))
if (!NativeMethods.InjectSyntheticPointerInput(_penHandle, pointer!, 1))
{
throw new Exception($"Input injection failed. Reason: {Marshal.GetLastWin32Error()}");
}
@ -87,38 +87,38 @@ namespace VoiDPlugins.OutputMode
public static void SetTarget()
{
pointer[0].penInfo.pointerInfo.hwndTarget = NativeMethods.GetForegroundWindow();
pointer![0].penInfo.pointerInfo.hwndTarget = NativeMethods.GetForegroundWindow();
}
public static void SetPosition(POINT point)
{
pointer[0].penInfo.pointerInfo.ptPixelLocation = point;
pointer![0].penInfo.pointerInfo.ptPixelLocation = point;
pointer[0].penInfo.pointerInfo.ptPixelLocationRaw = point;
}
public static void SetPressure(uint pressure)
{
pointer[0].penInfo.pressure = pressure;
pointer![0].penInfo.pressure = pressure;
}
public static void SetPointerFlags(POINTER_FLAGS flags)
{
pointer[0].penInfo.pointerInfo.pointerFlags |= flags;
pointer![0].penInfo.pointerInfo.pointerFlags |= flags;
}
public static void UnsetPointerFlags(POINTER_FLAGS flags)
{
pointer[0].penInfo.pointerInfo.pointerFlags &= ~flags;
pointer![0].penInfo.pointerInfo.pointerFlags &= ~flags;
}
public static void ClearPointerFlags()
{
pointer[0].penInfo.pointerInfo.pointerFlags = 0;
pointer![0].penInfo.pointerInfo.pointerFlags = 0;
}
public static void ClearPointerFlags(POINTER_FLAGS flags)
{
pointer[0].penInfo.pointerInfo.pointerFlags = flags;
pointer![0].penInfo.pointerInfo.pointerFlags = flags;
}
}
}

View file

@ -1,18 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<VMultiLibrary>true</VMultiLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
</ItemGroup>
</Project>

View file

@ -3,7 +3,7 @@ using OpenTabletDriver.Plugin.Platform.Pointer;
namespace VoiDPlugins.OutputMode
{
public class TouchPointerHandler : IAbsolutePointer, IVirtualTablet
public class TouchPointerHandler : IAbsolutePointer, IPressureHandler
{
private bool _inContact;
private bool _lastContact;
@ -15,16 +15,6 @@ namespace VoiDPlugins.OutputMode
_lastContact = false;
}
public void SetButtonState(uint button, bool active)
{
return;
}
public void SetEraser(bool isEraser)
{
return;
}
public void SetPosition(Vector2 pos)
{
Touch.SetPosition(new POINT((int)pos.X, (int)pos.Y));
@ -67,15 +57,5 @@ namespace VoiDPlugins.OutputMode
_inContact = false;
}
}
public void SetProximity(bool proximity, uint distance)
{
return;
}
public void SetTilt(Vector2 tilt)
{
return;
}
}
}

View file

@ -10,12 +10,24 @@ namespace VoiDPlugins.OutputMode
[PluginName("VMulti Absolute Mode")]
public class VMultiAbsoluteMode : AbsoluteOutputMode
{
private VMultiAbsolutePointer? _pointer;
[Resolved]
public IServiceProvider ServiceProvider
{
set => Pointer = new VMultiAbsolutePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen)));
set => _pointer = new VMultiAbsolutePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen))!);
}
public override IAbsolutePointer Pointer { get; set; }
[OnDependencyLoad]
public void Initialize()
{
_pointer!.Initialize(TabletReference);
}
public override IAbsolutePointer Pointer
{
get => _pointer!;
set { }
}
}
}

View file

@ -1,8 +1,6 @@
using System;
using OpenTabletDriver.Plugin.Attributes;
using OpenTabletDriver.Plugin.DependencyInjection;
using OpenTabletDriver.Plugin.Output;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
namespace VoiDPlugins.OutputMode
@ -10,12 +8,19 @@ namespace VoiDPlugins.OutputMode
[PluginName("VMulti Relative Mode")]
public class VMultiRelativeMode : RelativeOutputMode
{
[Resolved]
public IServiceProvider ServiceProvider
private VMultiRelativePointer? _pointer;
[OnDependencyLoad]
public void Initialize()
{
set => Pointer = new VMultiRelativePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen)));
_pointer = new VMultiRelativePointer();
_pointer!.Initialize(TabletReference);
}
public override IRelativePointer Pointer { get; set; }
public override IRelativePointer Pointer
{
get => _pointer!;
set { }
}
}
}

View file

@ -1,27 +1,43 @@
using System.Numerics;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
using OpenTabletDriver.Plugin.Tablet;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.OutputMode
{
public unsafe class VMultiAbsolutePointer : BasePointer<AbsoluteInputReport>, IAbsolutePointer
public unsafe class VMultiAbsolutePointer : IAbsolutePointer, ISynchronousPointer
{
public VMultiAbsolutePointer(IVirtualScreen screen) : base(screen, "VMultiAbs")
private AbsoluteInputReport* _rawPointer;
private VMultiInstance<AbsoluteInputReport>? _instance;
private Vector2 _conversionFactor;
public VMultiAbsolutePointer(IVirtualScreen virtualScreen)
{
ButtonHandler.SetReport((VMultiReportHeader*)ReportPointer, ReportBuffer);
_conversionFactor = new Vector2(virtualScreen.Width, virtualScreen.Height) / (1 / 32767);
}
protected override AbsoluteInputReport CreateReport()
public void Initialize(TabletReference tabletReference)
{
return new AbsoluteInputReport(0x09);
_instance = VMultiInstanceManager.RetrieveVMultiInstance("VMultiAbs", tabletReference, () => new AbsoluteInputReport());
_rawPointer = _instance.Pointer;
}
protected override void SetCoordinates(Vector2 pos)
public void SetPosition(Vector2 pos)
{
ReportPointer->X = (ushort)pos.X;
ReportPointer->Y = (ushort)pos.Y;
pos *= _conversionFactor;
_rawPointer->X = (ushort)pos.X;
_rawPointer->Y = (ushort)pos.Y;
}
public void Reset()
{
}
public void Flush()
{
_instance!.Write();
}
}
}

View file

@ -1,33 +1,38 @@
using System.Numerics;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
using OpenTabletDriver.Plugin.Tablet;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.OutputMode
{
public unsafe class VMultiRelativePointer : BasePointer<RelativeInputReport>, IRelativePointer
public unsafe class VMultiRelativePointer : IRelativePointer, ISynchronousPointer
{
public VMultiRelativePointer(IVirtualScreen screen) : base(screen, "VMultiRel")
private RelativeInputReport* _rawPointer;
private VMultiInstance<RelativeInputReport>? _instance;
private Vector2 _error;
public void Initialize(TabletReference tabletReference)
{
ButtonHandler.SetReport((VMultiReportHeader*)ReportPointer, ReportBuffer);
_instance = VMultiInstanceManager.RetrieveVMultiInstance("VMultiRel", tabletReference, () => new RelativeInputReport());
_rawPointer = _instance.Pointer;
}
protected override RelativeInputReport CreateReport()
public void SetPosition(Vector2 delta)
{
return new RelativeInputReport(0x04);
delta += _error;
_error = new Vector2(delta.X % 1, delta.Y % 1);
_rawPointer->X = (byte)delta.X;
_rawPointer->Y = (byte)delta.Y;
}
public override void SetPosition(Vector2 pos)
public void Reset()
{
SetCoordinates(pos);
Device.Write(ReportBuffer);
}
protected override void SetCoordinates(Vector2 pos)
public void Flush()
{
ReportPointer->X = (byte)pos.X;
ReportPointer->Y = (byte)pos.Y;
_instance!.Write();
}
}
}

View file

@ -8,8 +8,10 @@ using VoiDPlugins.Library.VMulti;
namespace VoiDPlugins.OutputMode
{
[PluginName("VMulti Mode")]
public class VMultiButtonHandler : ButtonHandler, IStateBinding
public class VMultiButtonHandler : IStateBinding
{
private VMultiInstance? _instance;
public static Dictionary<string, int> Bindings { get; } = new()
{
{ "Left", 1 },
@ -20,16 +22,24 @@ namespace VoiDPlugins.OutputMode
public static string[] ValidButtons { get; } = Bindings.Keys.ToArray();
[Property("Button"), PropertyValidated(nameof(ValidButtons))]
public string Button { get; set; }
public string? Button { get; set; }
[TabletReference]
public TabletReference Reference { set => Initialize(value); }
private void Initialize(TabletReference tabletReference)
{
_instance = VMultiInstanceManager.RetrieveVMultiInstance(tabletReference);
}
public void Press(TabletReference tablet, IDeviceReport report)
{
EnableBit(Bindings[Button]);
_instance!.EnableButtonBit(Bindings[Button!]);
}
public void Release(TabletReference tablet, IDeviceReport report)
{
DisableBit(Bindings[Button]);
_instance!.DisableButtonBit(Bindings[Button!]);
}
}
}

View file

@ -1,19 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<VMultiLibrary>true</VMultiLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<ProjectReference Include="../../VoiDPlugins.Library/VMulti/VMulti.csproj" />
</ItemGroup>
</Project>

View file

@ -10,12 +10,24 @@ namespace VoiDPlugins.OutputMode
[PluginName("Windows Ink Absolute Mode")]
public class WinInkAbsoluteMode : AbsoluteOutputMode
{
private WinInkAbsolutePointer? _pointer;
[Resolved]
public IServiceProvider ServiceProvider
{
set => Pointer = new WinInkAbsolutePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen)));
set => _pointer = new WinInkAbsolutePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen))!);
}
public override IAbsolutePointer Pointer { get; set; }
[OnDependencyLoad]
public void Initialize()
{
_pointer!.Initialize(TabletReference);
}
public override IAbsolutePointer Pointer
{
get => _pointer!;
set { }
}
}
}

View file

@ -10,12 +10,24 @@ namespace VoiDPlugins.OutputMode
[PluginName("Windows Ink Relative Mode")]
public class WinInkRelativeMode : RelativeOutputMode
{
private WinInkRelativePointer? _pointer;
[Resolved]
public IServiceProvider ServiceProvider
{
set => Pointer = new WinInkRelativePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen)));
set => _pointer = new WinInkRelativePointer((IVirtualScreen)value.GetService(typeof(IVirtualScreen))!);
}
public override IRelativePointer Pointer { get; set; }
[OnDependencyLoad]
public void Initialize()
{
_pointer!.Initialize(TabletReference);
}
public override IRelativePointer Pointer
{
get => _pointer!;
set { }
}
}
}

View file

@ -1,57 +1,23 @@
using System.Numerics;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.OutputMode
{
public unsafe class WinInkAbsolutePointer : BasePointer<DigitizerInputReport>, IVirtualTablet
public unsafe class WinInkAbsolutePointer : WinInkBasePointer, IAbsolutePointer
{
public WinInkAbsolutePointer(IVirtualScreen screen) : base(screen, "WindowsInk")
private readonly Vector2 _conversionFactor;
public WinInkAbsolutePointer(IVirtualScreen screen)
{
WinInkButtonHandler.SetReport(ReportPointer, ReportBuffer);
WinInkButtonHandler.SetDevice(Device);
_conversionFactor = new Vector2(screen.Width, screen.Height) / (1 / 32767);
}
public void SetButtonState(uint button, bool active)
public void SetPosition(Vector2 pos)
{
throw new System.NotImplementedException();
}
public void SetEraser(bool isEraser)
{
if (!WinInkButtonHandler.IsManuallySet)
{
WinInkButtonHandler.EraserStateTransition(isEraser);
}
}
public void SetPressure(float percentage)
{
ReportPointer->Pressure = (ushort)(percentage * 8191);
}
public void SetProximity(bool proximity, uint distance)
{
return;
}
public void SetTilt(Vector2 tilt)
{
ReportPointer->XTilt = (byte)tilt.X;
ReportPointer->YTilt = (byte)tilt.Y;
}
protected override DigitizerInputReport CreateReport()
{
return new DigitizerInputReport(0x05);
}
protected override void SetCoordinates(Vector2 pos)
{
ReportPointer->X = (ushort)pos.X;
ReportPointer->Y = (ushort)pos.Y;
pos *= _conversionFactor;
RawPointer->X = (ushort)pos.X;
RawPointer->Y = (ushort)pos.Y;
}
}
}

View file

@ -0,0 +1,57 @@
using System.Numerics;
using OpenTabletDriver.Plugin.Platform.Pointer;
using OpenTabletDriver.Plugin.Tablet;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
using static VoiDPlugins.OutputMode.WindowsInkConstants;
namespace VoiDPlugins.OutputMode
{
public unsafe abstract class WinInkBasePointer : IPressureHandler, ITiltHandler, IEraserHandler, ISynchronousPointer
{
protected DigitizerInputReport* RawPointer { get; private set; }
protected VMultiInstance<DigitizerInputReport>? Instance { get; private set; }
public void Initialize(TabletReference tabletReference)
{
Instance = VMultiInstanceManager.RetrieveVMultiInstance("WindowsInk", tabletReference, () => new DigitizerInputReport());
Instance.InitializeData(POINTER, this);
Instance.InitializeData(ERASER_STATE, false);
Instance.InitializeData(MANUAL_ERASER, false);
RawPointer = Instance.Pointer;
}
public void SetEraser(bool isEraser)
{
if (!Instance!.GetData<bool>(MANUAL_ERASER))
{
WinInkButtonHandler.EraserStateTransition(Instance, ref GetEraser(), isEraser);
}
}
public void SetPressure(float percentage)
{
RawPointer->Pressure = (ushort)(percentage * 8191);
}
public void SetTilt(Vector2 tilt)
{
RawPointer->XTilt = (byte)tilt.X;
RawPointer->YTilt = (byte)tilt.Y;
}
public void Reset()
{
}
public void Flush()
{
Instance!.Write();
}
private ref bool GetEraser()
{
return ref Instance!.GetData<bool>(ERASER_STATE);
}
}
}

View file

@ -1,68 +1,29 @@
using System.Numerics;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.OutputMode
{
public unsafe class WinInkRelativePointer : BasePointer<DigitizerInputReport>, IVirtualTablet
public unsafe class WinInkRelativePointer : WinInkBasePointer, IRelativePointer
{
private Vector2 maxPoint;
private Vector2 currentPoint;
private Vector2 _maxPoint;
private Vector2 _currentPoint;
private Vector2 _error;
public WinInkRelativePointer(IVirtualScreen screen) : base(screen, "WindowsInk")
public WinInkRelativePointer(IVirtualScreen screen)
{
WinInkButtonHandler.SetReport(ReportPointer, ReportBuffer);
WinInkButtonHandler.SetDevice(Device);
maxPoint = new Vector2(VirtualScreen.Width, VirtualScreen.Height);
currentPoint = maxPoint / 2;
_maxPoint = new Vector2(screen.Width, screen.Height);
_currentPoint = _maxPoint / 2;
}
public void SetButtonState(uint button, bool active)
public void SetPosition(Vector2 delta)
{
return;
}
delta += _error;
_error = new Vector2(delta.X % 1, delta.Y % 1);
public void SetEraser(bool isEraser)
{
if (!WinInkButtonHandler.IsManuallySet)
{
WinInkButtonHandler.EraserStateTransition(isEraser);
}
}
public void SetPressure(float percentage)
{
ReportPointer->Pressure = (ushort)(percentage * 8191);
}
public void SetProximity(bool proximity, uint distance)
{
return;
}
public void SetTilt(Vector2 tilt)
{
ReportPointer->XTilt = (byte)tilt.X;
ReportPointer->YTilt = (byte)tilt.Y;
}
public override void Translate(Vector2 delta)
{
currentPoint = Vector2.Clamp(currentPoint + delta, Vector2.Zero, maxPoint);
base.SetPosition(currentPoint);
}
protected override DigitizerInputReport CreateReport()
{
return new DigitizerInputReport(0x05);
}
protected override void SetCoordinates(Vector2 pos)
{
ReportPointer->X = (ushort)pos.X;
ReportPointer->Y = (ushort)pos.Y;
_currentPoint = Vector2.Clamp(_currentPoint + delta, Vector2.Zero, _maxPoint);
RawPointer->X = (ushort)_currentPoint.X;
RawPointer->Y = (ushort)_currentPoint.Y;
}
}
}

View file

@ -1,16 +1,18 @@
using System;
using HidSharp;
using OpenTabletDriver.Plugin;
using OpenTabletDriver.Plugin.Attributes;
using OpenTabletDriver.Plugin.Tablet;
using VoiDPlugins.Library.VMulti;
using VoiDPlugins.Library.VMulti.Device;
using static VoiDPlugins.OutputMode.WindowsInkConstants;
namespace VoiDPlugins.OutputMode
{
[PluginName("Windows Ink")]
public unsafe class WinInkButtonHandler : ButtonHandler, IStateBinding
public unsafe class WinInkButtonHandler : IStateBinding
{
private VMultiInstance? _instance;
public static string[] ValidButtons { get; } = new string[]
{
"Pen Tip",
@ -20,7 +22,7 @@ namespace VoiDPlugins.OutputMode
};
[Property("Button"), PropertyValidated(nameof(ValidButtons))]
public string Button { get; set; }
public string? Button { get; set; }
[Flags]
private enum ButtonBits : byte
@ -32,30 +34,37 @@ namespace VoiDPlugins.OutputMode
InRange = 16
}
public static bool IsManuallySet { get; set; }
private static bool EraserState;
private static HidStream Device;
public bool IsManuallySet { get; set; }
[TabletReference]
public TabletReference Reference { set => Initialize(value); }
private void Initialize(TabletReference tabletReference)
{
_instance = VMultiInstanceManager.RetrieveVMultiInstance(tabletReference);
}
public void Press(TabletReference tablet, IDeviceReport report)
{
ref var eraserState = ref GetEraser();
switch (Button)
{
case "Pen Tip":
EnableBit((int)(EraserState ? ButtonBits.Eraser : ButtonBits.Press));
_instance!.EnableButtonBit((int)(eraserState ? ButtonBits.Eraser : ButtonBits.Press));
break;
case "Pen Button":
EnableBit((int)ButtonBits.Barrel);
_instance!.EnableButtonBit((int)ButtonBits.Barrel);
break;
case "Eraser (Toggle)":
IsManuallySet = true;
EraserStateTransition(!EraserState);
EraserStateTransition(_instance!, ref eraserState, !eraserState);
break;
case "Eraser (Hold)":
IsManuallySet = true;
EraserStateTransition(true);
EraserStateTransition(_instance!, ref eraserState, true);
break;
}
}
@ -65,60 +74,54 @@ namespace VoiDPlugins.OutputMode
switch (Button)
{
case "Pen Tip":
DisableBit((int)(ButtonBits.Press | ButtonBits.Eraser));
_instance!.DisableButtonBit((int)(ButtonBits.Press | ButtonBits.Eraser));
break;
case "Pen Button":
DisableBit((int)ButtonBits.Barrel);
_instance!.DisableButtonBit((int)ButtonBits.Barrel);
break;
case "Eraser (Hold)":
EraserStateTransition(false);
EraserStateTransition(_instance!, ref GetEraser(), false);
break;
}
}
public static void EraserStateTransition(bool isEraser)
public static void EraserStateTransition(VMultiInstance instance, ref bool eraserState, bool isEraser)
{
if (EraserState != isEraser)
if (eraserState != isEraser)
{
EraserState = isEraser;
var report = (DigitizerInputReport*)ReportPointer;
eraserState = isEraser;
var report = (DigitizerInputReport*)instance.Header;
var buttons = report->Header.Buttons;
var pressure = report->Pressure;
// Send In-Range but no tips
DisableBit((int)(ButtonBits.Press | ButtonBits.Eraser));
instance.DisableButtonBit((int)(ButtonBits.Press | ButtonBits.Eraser));
report->Pressure = 0;
Device.Write(ReportBuffer);
instance.Write();
// Send Out-Of-Range
report->Header.Buttons = 0;
Device.Write(ReportBuffer);
instance.Write();
// Send In-Range but no tips
EnableBit((int)ButtonBits.InRange);
if (EraserState)
EnableBit((int)ButtonBits.Invert);
instance.EnableButtonBit((int)ButtonBits.InRange);
if (eraserState)
instance.EnableButtonBit((int)ButtonBits.Invert);
Device.Write(ReportBuffer);
instance.Write();
// Set Proper Report
if (HasBit(buttons, (int)(ButtonBits.Press | ButtonBits.Eraser)))
EnableBit((int)(EraserState ? ButtonBits.Eraser : ButtonBits.Press));
if (VMultiInstance.HasBit(buttons, (int)(ButtonBits.Press | ButtonBits.Eraser)))
instance.EnableButtonBit((int)(eraserState ? ButtonBits.Eraser : ButtonBits.Press));
report->Pressure = pressure;
}
}
public static void SetReport(DigitizerInputReport* report, byte[] reportBuffer)
private ref bool GetEraser()
{
SetReport((VMultiReportHeader*)report, reportBuffer);
EnableBit((int)ButtonBits.InRange);
}
public static void SetDevice(HidStream device)
{
Device = device;
return ref _instance!.GetData<bool>(ERASER_STATE);
}
}
}

View file

@ -1,19 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<VMultiLibrary>true</VMultiLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<ProjectReference Include="../../VoiDPlugins.Library/VMulti/VMulti.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,9 @@
namespace VoiDPlugins.OutputMode
{
public static class WindowsInkConstants
{
public const int POINTER = 0;
public const int ERASER_STATE = 1;
public const int MANUAL_ERASER = 2;
}
}

View file

@ -1,59 +0,0 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using HidSharp;
using OpenTabletDriver.Plugin.Attributes;
using OpenTabletDriver.Plugin.Platform.Display;
using OpenTabletDriver.Plugin.Platform.Pointer;
namespace VoiDPlugins.Library.VMulti
{
[PluginIgnore]
public abstract unsafe class BasePointer<T> : IAbsolutePointer, IRelativePointer where T : unmanaged
{
protected readonly byte[] ReportBuffer;
protected readonly T* ReportPointer;
protected readonly HidStream Device;
protected IVirtualScreen VirtualScreen;
private Vector2 ScreenToVMulti;
private Vector2 error;
public BasePointer(IVirtualScreen screen, string Name)
{
ReportBuffer = GC.AllocateArray<byte>(Unsafe.SizeOf<T>(), pinned: true);
ReportPointer = (T*)Unsafe.AsPointer(ref ReportBuffer[0]);
T report = CreateReport();
*ReportPointer = report;
VirtualScreen = screen;
Device = VMultiDevice.Retrieve(Name);
ScreenToVMulti = new Vector2(VirtualScreen.Width, VirtualScreen.Height) / 32767;
}
protected Vector2 Convert(Vector2 pos)
{
return pos / ScreenToVMulti;
}
protected abstract T CreateReport();
protected abstract void SetCoordinates(Vector2 pos);
public virtual void SetPosition(Vector2 pos)
{
SetCoordinates(Convert(pos));
Device.Write(ReportBuffer);
}
public virtual void Translate(Vector2 delta)
{
delta += error;
error = new Vector2(delta.X % 1, delta.Y % 1);
SetCoordinates(delta);
Device.Write(ReportBuffer);
}
}
}

View file

@ -1,38 +0,0 @@
using OpenTabletDriver.Plugin.Attributes;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.Library.VMulti
{
[PluginIgnore]
public unsafe class ButtonHandler
{
protected static byte[] ReportBuffer;
protected static VMultiReportHeader* ReportPointer;
public static void SetReport(VMultiReportHeader* report, byte[] reportBuffer)
{
ReportBuffer = reportBuffer;
ReportPointer = report;
}
public static void EnableBit(int bit)
{
ReportPointer->Buttons = (byte)(ReportPointer->Buttons | bit);
}
public static void DisableBit(int bit)
{
ReportPointer->Buttons = (byte)(ReportPointer->Buttons & ~bit);
}
public static bool HasBit(int bit)
{
return (ReportPointer->Buttons & bit) != 0;
}
public static bool HasBit(byte buttons, int bit)
{
return (buttons & bit) != 0;
}
}
}

View file

@ -6,9 +6,9 @@ namespace VoiDPlugins.Library.VMulti.Device
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AbsoluteInputReport
{
public AbsoluteInputReport(byte reportID)
public AbsoluteInputReport()
{
Header = new VMultiReportHeader(Unsafe.SizeOf<AbsoluteInputReport>(), reportID);
Header = new VMultiReportHeader(Unsafe.SizeOf<AbsoluteInputReport>(), 0x09);
X = 0;
Y = 0;
Pressure = 0;

View file

@ -1,19 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../.modules/OpenTabletDriver/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj" />
<PackageReference Include="HidSharpCore" Version="1.2.1.1" />
</ItemGroup>
</Project>

View file

@ -1,31 +0,0 @@
using System;
using HidSharp;
using OpenTabletDriver.Plugin;
namespace VoiDPlugins.Library.VMulti
{
public static class VMultiDevice
{
public static HidStream Retrieve(string Name)
{
HidStream VMultiDev = null;
foreach (var device in DeviceList.Local.GetHidDevices(productID: 47820))
{
if (device.GetMaxOutputReportLength() == 65 && device.GetMaxInputReportLength() == 65)
{
if (device.TryOpen(out VMultiDev))
break;
}
}
if (VMultiDev == null)
{
Log.Write(Name, "Cannot find VirtualHID", LogLevel.Error);
Log.Write(Name, "Install VMulti driver here: https://github.com/X9VoiD/vmulti-bin/releases/latest", LogLevel.Error);
throw new Exception();
}
return VMultiDev;
}
}
}

View file

@ -0,0 +1,101 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using HidSharp;
using OpenTabletDriver.Plugin;
using VoiDPlugins.Library.VMulti.Device;
namespace VoiDPlugins.Library.VMulti
{
public class VMultiInstance
{
private readonly object[] _data;
private readonly HidStream _device;
protected readonly byte[] Buffer;
public unsafe VMultiReportHeader* Header { get; }
internal unsafe VMultiInstance(string name, int size)
{
Buffer = GC.AllocateArray<byte>(size, true);
Header = (VMultiReportHeader*)Unsafe.AsPointer(ref Buffer[0]);
_device = Retrieve(name);
_data = GC.AllocateArray<object>(32, true);
}
public void Write()
{
_device.Write(Buffer);
}
public void InitializeData<T>(int i, T data)
{
Check<T>();
_data[i] = data!;
}
public unsafe ref T GetData<T>(int i)
{
return ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref _data[i]));
}
public unsafe void EnableButtonBit(int bit)
{
Header->Buttons = (byte)(Header->Buttons | bit);
}
public unsafe void DisableButtonBit(int bit)
{
Header->Buttons = (byte)(Header->Buttons & ~bit);
}
public unsafe bool HasButtonBit(int bit)
{
return HasBit(Header->Buttons, bit);
}
public static bool HasBit(byte buttons, int bit)
{
return (buttons & bit) != 0;
}
private static HidStream Retrieve(string Name)
{
HidStream? VMultiDev = null;
foreach (var device in DeviceList.Local.GetHidDevices(productID: 47820))
{
if (device.GetMaxOutputReportLength() == 65 && device.GetMaxInputReportLength() == 65)
{
if (device.TryOpen(out VMultiDev))
break;
}
}
if (VMultiDev == null)
{
Log.Write(Name, "Cannot find VirtualHID", LogLevel.Error);
Log.Write(Name, "Install VMulti driver here: https://github.com/X9VoiD/vmulti-bin/releases/latest", LogLevel.Error);
throw new Exception();
}
return VMultiDev;
}
[Conditional("DEBUG")]
private void Check<T>()
{
if (Unsafe.SizeOf<T>() > IntPtr.Size)
throw new InvalidOperationException();
}
}
public class VMultiInstance<T> : VMultiInstance where T : unmanaged
{
public unsafe T* Pointer { get; }
internal unsafe VMultiInstance(string name, Func<T> initialValue) : base(name, Unsafe.SizeOf<T>())
{
Pointer = (T*)Unsafe.AsPointer(ref Buffer[0]);
*Pointer = initialValue();
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using OpenTabletDriver.Plugin.Tablet;
namespace VoiDPlugins.Library.VMulti
{
public static class VMultiInstanceManager
{
private static readonly object _syncLock = new();
private static readonly Dictionary<string, VMultiInstance> _map = new();
public static VMultiInstance<T> RetrieveVMultiInstance<T>(string name, TabletReference tabletReference, Func<T> initialValue)
where T : unmanaged
{
lock (_syncLock)
{
ref var instance = ref CollectionsMarshal.GetValueRefOrAddDefault(_map, tabletReference.Properties.Name, out var exists);
if (!exists)
instance = new VMultiInstance<T>(name, initialValue);
return (VMultiInstance<T>)instance!;
}
}
public static VMultiInstance RetrieveVMultiInstance(TabletReference tabletReference)
{
return _map[tabletReference.Properties.Name];
}
}
}

View file

@ -1,13 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5</TargetFrameworks>
<NotPluginProject>true</NotPluginProject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
</PropertyGroup>
</Project>

View file

@ -23,10 +23,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMultiMode", "OutputMode\VM
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInk", "OutputMode\WindowsInk\WindowsInk.csproj", "{D8DBD60E-BE5C-43DE-AEB6-D6ADBAC3BE8C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tool", "Tool", "{C3E1E369-CD2C-4030-88BE-3D4E7493F8C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OemKill", "Tool\OemKill\OemKill.csproj", "{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VoiDPlugins.Library", "VoiDPlugins.Library", "{9B841994-6F85-4834-8C8C-04D196FE388A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMulti", "VoiDPlugins.Library\VMulti\VMulti.csproj", "{E6A1BA35-D3D4-4F28-9B59-FA946B3039C7}"
@ -128,18 +124,6 @@ Global
{D8DBD60E-BE5C-43DE-AEB6-D6ADBAC3BE8C}.Release|x64.Build.0 = Release|Any CPU
{D8DBD60E-BE5C-43DE-AEB6-D6ADBAC3BE8C}.Release|x86.ActiveCfg = Release|Any CPU
{D8DBD60E-BE5C-43DE-AEB6-D6ADBAC3BE8C}.Release|x86.Build.0 = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|x64.ActiveCfg = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|x64.Build.0 = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|x86.ActiveCfg = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Debug|x86.Build.0 = Debug|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|Any CPU.Build.0 = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|x64.ActiveCfg = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|x64.Build.0 = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|x86.ActiveCfg = Release|Any CPU
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C}.Release|x86.Build.0 = Release|Any CPU
{E6A1BA35-D3D4-4F28-9B59-FA946B3039C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6A1BA35-D3D4-4F28-9B59-FA946B3039C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6A1BA35-D3D4-4F28-9B59-FA946B3039C7}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -161,7 +145,6 @@ Global
{40F46E98-3485-4805-A70A-EAC5C6E0BACC} = {44DF0884-9B34-45CA-92B7-2D57DCA3A1A8}
{A0101CA9-B8C8-4715-81E6-0A1EB34CA739} = {44DF0884-9B34-45CA-92B7-2D57DCA3A1A8}
{D8DBD60E-BE5C-43DE-AEB6-D6ADBAC3BE8C} = {44DF0884-9B34-45CA-92B7-2D57DCA3A1A8}
{DE2D6E1E-F04E-4E0C-BE95-7B8E8AFBCD4C} = {C3E1E369-CD2C-4030-88BE-3D4E7493F8C7}
{E6A1BA35-D3D4-4F28-9B59-FA946B3039C7} = {9B841994-6F85-4834-8C8C-04D196FE388A}
EndGlobalSection
EndGlobal