mirror of
https://github.com/vale981/VoiDPlugins
synced 2025-03-04 16:51:38 -05:00
Update to latest OTD API
This commit is contained in:
parent
4e078b1436
commit
38100896f7
42 changed files with 492 additions and 632 deletions
2
.modules/Directory.Build.props
Normal file
2
.modules/Directory.Build.props
Normal file
|
@ -0,0 +1,2 @@
|
|||
<Project>
|
||||
</Project>
|
2
.modules/Directory.Build.targets
Normal file
2
.modules/Directory.Build.targets
Normal file
|
@ -0,0 +1,2 @@
|
|||
<Project>
|
||||
</Project>
|
|
@ -1 +1 @@
|
|||
Subproject commit 267a32282ad5462a5be254e74f60edeba809831f
|
||||
Subproject commit 78e9d03766e61f986d3b510dfd3b23c74917d895
|
|
@ -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)
|
||||
|
|
|
@ -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
12
Directory.Build.props
Normal 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
7
Directory.Build.targets
Normal 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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace VoiDPlugins.Filter
|
|||
get => weight;
|
||||
}
|
||||
|
||||
public event Action<IDeviceReport> Emit;
|
||||
public event Action<IDeviceReport>? Emit;
|
||||
|
||||
public PipelinePosition Position => PipelinePosition.PreTransform;
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
57
OutputMode/WindowsInk/Pointer/WinInkBasePointer.cs
Normal file
57
OutputMode/WindowsInk/Pointer/WinInkBasePointer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
9
OutputMode/WindowsInk/WindowsInkConstants.cs
Normal file
9
OutputMode/WindowsInk/WindowsInkConstants.cs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
101
VoiDPlugins.Library/VMulti/VMultiInstance.cs
Normal file
101
VoiDPlugins.Library/VMulti/VMultiInstance.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
30
VoiDPlugins.Library/VMulti/VMultiInstanceManager.cs
Normal file
30
VoiDPlugins.Library/VMulti/VMultiInstanceManager.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue