mirror of
https://github.com/vale981/VoiDPlugins
synced 2025-03-05 09:11:38 -05:00
Generalize SharedStore
This commit is contained in:
parent
8c96ec7537
commit
3dd078ea2d
14 changed files with 101 additions and 72 deletions
|
@ -4,6 +4,7 @@ using OpenTabletDriver.Plugin.Platform.Pointer;
|
||||||
using OpenTabletDriver.Plugin.Tablet;
|
using OpenTabletDriver.Plugin.Tablet;
|
||||||
using VoiDPlugins.Library.VMulti;
|
using VoiDPlugins.Library.VMulti;
|
||||||
using VoiDPlugins.Library.VMulti.Device;
|
using VoiDPlugins.Library.VMulti.Device;
|
||||||
|
using VoiDPlugins.Library.VoiD;
|
||||||
|
|
||||||
namespace VoiDPlugins.OutputMode
|
namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
|
@ -15,7 +16,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
public VMultiAbsolutePointer(TabletReference tabletReference, IVirtualScreen virtualScreen)
|
public VMultiAbsolutePointer(TabletReference tabletReference, IVirtualScreen virtualScreen)
|
||||||
{
|
{
|
||||||
_instance = VMultiInstanceManager.RetrieveVMultiInstance("VMultiAbs", tabletReference, () => new AbsoluteInputReport());
|
_instance = GlobalStore<VMultiInstance>.GetOrInitialize(tabletReference, () => new VMultiInstance<AbsoluteInputReport>("VMultiAbs", new AbsoluteInputReport()));
|
||||||
_rawPointer = _instance.Pointer;
|
_rawPointer = _instance.Pointer;
|
||||||
_conversionFactor = new Vector2(32767, 32767) / new Vector2(virtualScreen.Width, virtualScreen.Height);
|
_conversionFactor = new Vector2(32767, 32767) / new Vector2(virtualScreen.Width, virtualScreen.Height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using OpenTabletDriver.Plugin.Platform.Pointer;
|
||||||
using OpenTabletDriver.Plugin.Tablet;
|
using OpenTabletDriver.Plugin.Tablet;
|
||||||
using VoiDPlugins.Library.VMulti;
|
using VoiDPlugins.Library.VMulti;
|
||||||
using VoiDPlugins.Library.VMulti.Device;
|
using VoiDPlugins.Library.VMulti.Device;
|
||||||
|
using VoiDPlugins.Library.VoiD;
|
||||||
|
|
||||||
namespace VoiDPlugins.OutputMode
|
namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
|
@ -14,7 +15,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
public VMultiRelativePointer(TabletReference tabletReference)
|
public VMultiRelativePointer(TabletReference tabletReference)
|
||||||
{
|
{
|
||||||
_instance = VMultiInstanceManager.RetrieveVMultiInstance("VMultiRel", tabletReference, () => new RelativeInputReport());
|
_instance = GlobalStore<VMultiInstance>.GetOrInitialize(tabletReference, () => new VMultiInstance<RelativeInputReport>("VMultiAbs", new RelativeInputReport()));
|
||||||
_rawPointer = _instance.Pointer;
|
_rawPointer = _instance.Pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using OpenTabletDriver.Plugin;
|
||||||
using OpenTabletDriver.Plugin.Attributes;
|
using OpenTabletDriver.Plugin.Attributes;
|
||||||
using OpenTabletDriver.Plugin.Tablet;
|
using OpenTabletDriver.Plugin.Tablet;
|
||||||
using VoiDPlugins.Library.VMulti;
|
using VoiDPlugins.Library.VMulti;
|
||||||
|
using VoiDPlugins.Library.VoiD;
|
||||||
|
|
||||||
namespace VoiDPlugins.OutputMode
|
namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
|
@ -29,7 +30,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
private void Initialize(TabletReference tabletReference)
|
private void Initialize(TabletReference tabletReference)
|
||||||
{
|
{
|
||||||
_instance = VMultiInstanceManager.RetrieveVMultiInstance(tabletReference);
|
_instance = GlobalStore<VMultiInstance>.Get(tabletReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Press(TabletReference tablet, IDeviceReport report)
|
public void Press(TabletReference tablet, IDeviceReport report)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VMultiLibrary>true</VMultiLibrary>
|
<VMultiLibrary>true</VMultiLibrary>
|
||||||
|
<VoiDLibrary>true</VoiDLibrary>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
private readonly Vector2 _conversionFactor;
|
private readonly Vector2 _conversionFactor;
|
||||||
|
|
||||||
public WinInkAbsolutePointer(TabletReference tabletReference, IVirtualScreen screen) : base(tabletReference)
|
public WinInkAbsolutePointer(TabletReference tabletReference, IVirtualScreen screen) : base("Windows Ink", tabletReference)
|
||||||
{
|
{
|
||||||
_conversionFactor = new Vector2(32767, 32767) / new Vector2(screen.Width, screen.Height);
|
_conversionFactor = new Vector2(32767, 32767) / new Vector2(screen.Width, screen.Height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using OpenTabletDriver.Plugin.Tablet;
|
||||||
using VoiDPlugins.Library;
|
using VoiDPlugins.Library;
|
||||||
using VoiDPlugins.Library.VMulti;
|
using VoiDPlugins.Library.VMulti;
|
||||||
using VoiDPlugins.Library.VMulti.Device;
|
using VoiDPlugins.Library.VMulti.Device;
|
||||||
|
using VoiDPlugins.Library.VoiD;
|
||||||
using static VoiDPlugins.OutputMode.WindowsInkConstants;
|
using static VoiDPlugins.OutputMode.WindowsInkConstants;
|
||||||
|
|
||||||
namespace VoiDPlugins.OutputMode
|
namespace VoiDPlugins.OutputMode
|
||||||
|
@ -12,21 +13,24 @@ namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
protected DigitizerInputReport* RawPointer { get; }
|
protected DigitizerInputReport* RawPointer { get; }
|
||||||
protected VMultiInstance<DigitizerInputReport>? Instance { get; }
|
protected VMultiInstance<DigitizerInputReport>? Instance { get; }
|
||||||
|
protected SharedStore? SharedStore { get; }
|
||||||
|
|
||||||
public WinInkBasePointer(TabletReference tabletReference)
|
public WinInkBasePointer(string name, TabletReference tabletReference)
|
||||||
{
|
{
|
||||||
Instance = VMultiInstanceManager.RetrieveVMultiInstance("WindowsInk", tabletReference, () => new DigitizerInputReport());
|
Instance = new VMultiInstance<DigitizerInputReport>(name, new DigitizerInputReport());
|
||||||
Instance.InitializeData(POINTER, this);
|
SharedStore = GlobalStore<SharedStore>.GetOrInitialize(tabletReference, () => new SharedStore());
|
||||||
Instance.InitializeData(ERASER_STATE, new Boxed<bool>(false));
|
SharedStore.InitializeData(INSTANCE, Instance);
|
||||||
Instance.InitializeData(MANUAL_ERASER, new Boxed<bool>(false));
|
SharedStore.InitializeData(POINTER, this);
|
||||||
|
SharedStore.InitializeData(ERASER_STATE, new Boxed<bool>(false));
|
||||||
|
SharedStore.InitializeData(MANUAL_ERASER, new Boxed<bool>(false));
|
||||||
RawPointer = Instance.Pointer;
|
RawPointer = Instance.Pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEraser(bool isEraser)
|
public void SetEraser(bool isEraser)
|
||||||
{
|
{
|
||||||
if (!Instance!.GetData<Boxed<bool>>(MANUAL_ERASER).Value)
|
if (!SharedStore!.GetData<Boxed<bool>>(MANUAL_ERASER).Value)
|
||||||
{
|
{
|
||||||
WindowsInkButtonHandler.EraserStateTransition(Instance, ref GetEraser(), isEraser);
|
WindowsInkButtonHandler.EraserStateTransition(Instance!, ref GetEraser(), isEraser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +47,6 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
Instance!.DisableButtonBit((int)WindowsInkButtonFlags.InRange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Flush()
|
public void Flush()
|
||||||
|
@ -53,7 +56,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
private ref Boxed<bool> GetEraser()
|
private ref Boxed<bool> GetEraser()
|
||||||
{
|
{
|
||||||
return ref Instance!.GetData<Boxed<bool>>(ERASER_STATE);
|
return ref SharedStore!.GetData<Boxed<bool>>(ERASER_STATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
private Vector2 _currentPoint;
|
private Vector2 _currentPoint;
|
||||||
private Vector2 _error;
|
private Vector2 _error;
|
||||||
|
|
||||||
public WinInkRelativePointer(TabletReference tabletReference, IVirtualScreen screen) : base(tabletReference)
|
public WinInkRelativePointer(TabletReference tabletReference, IVirtualScreen screen) : base("Windows Ink", tabletReference)
|
||||||
{
|
{
|
||||||
_maxPoint = new Vector2(screen.Width, screen.Height);
|
_maxPoint = new Vector2(screen.Width, screen.Height);
|
||||||
_currentPoint = _maxPoint / 2;
|
_currentPoint = _maxPoint / 2;
|
||||||
|
|
|
@ -4,6 +4,7 @@ using OpenTabletDriver.Plugin.Tablet;
|
||||||
using VoiDPlugins.Library;
|
using VoiDPlugins.Library;
|
||||||
using VoiDPlugins.Library.VMulti;
|
using VoiDPlugins.Library.VMulti;
|
||||||
using VoiDPlugins.Library.VMulti.Device;
|
using VoiDPlugins.Library.VMulti.Device;
|
||||||
|
using VoiDPlugins.Library.VoiD;
|
||||||
using static VoiDPlugins.OutputMode.WindowsInkConstants;
|
using static VoiDPlugins.OutputMode.WindowsInkConstants;
|
||||||
|
|
||||||
namespace VoiDPlugins.OutputMode
|
namespace VoiDPlugins.OutputMode
|
||||||
|
@ -12,6 +13,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
public unsafe partial class WindowsInkButtonHandler : IStateBinding
|
public unsafe partial class WindowsInkButtonHandler : IStateBinding
|
||||||
{
|
{
|
||||||
private VMultiInstance? _instance;
|
private VMultiInstance? _instance;
|
||||||
|
private SharedStore? _sharedStore;
|
||||||
|
|
||||||
public static string[] ValidButtons { get; } = new string[]
|
public static string[] ValidButtons { get; } = new string[]
|
||||||
{
|
{
|
||||||
|
@ -24,14 +26,13 @@ namespace VoiDPlugins.OutputMode
|
||||||
[Property("Button"), PropertyValidated(nameof(ValidButtons))]
|
[Property("Button"), PropertyValidated(nameof(ValidButtons))]
|
||||||
public string? Button { get; set; }
|
public string? Button { get; set; }
|
||||||
|
|
||||||
public bool IsManuallySet { get; set; }
|
|
||||||
|
|
||||||
[TabletReference]
|
[TabletReference]
|
||||||
public TabletReference Reference { set => Initialize(value); }
|
public TabletReference Reference { set => Initialize(value); }
|
||||||
|
|
||||||
private void Initialize(TabletReference tabletReference)
|
private void Initialize(TabletReference tabletReference)
|
||||||
{
|
{
|
||||||
_instance = VMultiInstanceManager.RetrieveVMultiInstance(tabletReference);
|
_sharedStore = GlobalStore<SharedStore>.Get(tabletReference);
|
||||||
|
_instance = _sharedStore.GetData<VMultiInstance>(INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Press(TabletReference tablet, IDeviceReport report)
|
public void Press(TabletReference tablet, IDeviceReport report)
|
||||||
|
@ -48,12 +49,12 @@ namespace VoiDPlugins.OutputMode
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Eraser (Toggle)":
|
case "Eraser (Toggle)":
|
||||||
IsManuallySet = true;
|
_sharedStore!.GetData<Boxed<bool>>(MANUAL_ERASER).Value = !eraserState.Value;
|
||||||
EraserStateTransition(_instance!, ref eraserState, !eraserState.Value);
|
EraserStateTransition(_instance!, ref eraserState, !eraserState.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Eraser (Hold)":
|
case "Eraser (Hold)":
|
||||||
IsManuallySet = true;
|
_sharedStore!.GetData<Boxed<bool>>(MANUAL_ERASER).Value = true;
|
||||||
EraserStateTransition(_instance!, ref eraserState, true);
|
EraserStateTransition(_instance!, ref eraserState, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +73,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Eraser (Hold)":
|
case "Eraser (Hold)":
|
||||||
|
_sharedStore!.GetData<Boxed<bool>>(MANUAL_ERASER).Value = false;
|
||||||
EraserStateTransition(_instance!, ref GetEraser(), false);
|
EraserStateTransition(_instance!, ref GetEraser(), false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +113,7 @@ namespace VoiDPlugins.OutputMode
|
||||||
|
|
||||||
private ref Boxed<bool> GetEraser()
|
private ref Boxed<bool> GetEraser()
|
||||||
{
|
{
|
||||||
return ref _instance!.GetData<Boxed<bool>>(ERASER_STATE);
|
return ref _sharedStore!.GetData<Boxed<bool>>(ERASER_STATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,8 +2,9 @@ namespace VoiDPlugins.OutputMode
|
||||||
{
|
{
|
||||||
public static class WindowsInkConstants
|
public static class WindowsInkConstants
|
||||||
{
|
{
|
||||||
public const int POINTER = 0;
|
public const int INSTANCE = 0;
|
||||||
public const int ERASER_STATE = 1;
|
public const int POINTER = 1;
|
||||||
public const int MANUAL_ERASER = 2;
|
public const int ERASER_STATE = 2;
|
||||||
|
public const int MANUAL_ERASER = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using HidSharp;
|
using HidSharp;
|
||||||
using OpenTabletDriver.Plugin;
|
using OpenTabletDriver.Plugin;
|
||||||
|
@ -9,17 +8,15 @@ namespace VoiDPlugins.Library.VMulti
|
||||||
{
|
{
|
||||||
public class VMultiInstance
|
public class VMultiInstance
|
||||||
{
|
{
|
||||||
private readonly object[] _data;
|
|
||||||
private readonly HidStream _device;
|
private readonly HidStream _device;
|
||||||
protected readonly byte[] Buffer;
|
protected readonly byte[] Buffer;
|
||||||
public unsafe VMultiReportHeader* Header { get; }
|
public unsafe VMultiReportHeader* Header { get; }
|
||||||
|
|
||||||
internal unsafe VMultiInstance(string name, int size)
|
public unsafe VMultiInstance(string name, int size)
|
||||||
{
|
{
|
||||||
Buffer = GC.AllocateArray<byte>(size, true);
|
Buffer = GC.AllocateArray<byte>(size, true);
|
||||||
Header = (VMultiReportHeader*)Unsafe.AsPointer(ref Buffer[0]);
|
Header = (VMultiReportHeader*)Unsafe.AsPointer(ref Buffer[0]);
|
||||||
_device = Retrieve(name);
|
_device = Retrieve(name);
|
||||||
_data = new object[32];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write()
|
public void Write()
|
||||||
|
@ -27,16 +24,6 @@ namespace VoiDPlugins.Library.VMulti
|
||||||
_device.Write(Buffer);
|
_device.Write(Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeData<T>(int i, T data)
|
|
||||||
{
|
|
||||||
_data[i] = data!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe ref T GetData<T>(int i) where T : class
|
|
||||||
{
|
|
||||||
return ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref _data[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void EnableButtonBit(int bit)
|
public unsafe void EnableButtonBit(int bit)
|
||||||
{
|
{
|
||||||
Header->Buttons = (byte)(Header->Buttons | bit);
|
Header->Buttons = (byte)(Header->Buttons | bit);
|
||||||
|
@ -84,10 +71,10 @@ namespace VoiDPlugins.Library.VMulti
|
||||||
{
|
{
|
||||||
public unsafe T* Pointer { get; }
|
public unsafe T* Pointer { get; }
|
||||||
|
|
||||||
internal unsafe VMultiInstance(string name, Func<T> initialValue) : base(name, Unsafe.SizeOf<T>())
|
public unsafe VMultiInstance(string name, T initialValue) : base(name, Unsafe.SizeOf<T>())
|
||||||
{
|
{
|
||||||
Pointer = (T*)Unsafe.AsPointer(ref Buffer[0]);
|
Pointer = (T*)Unsafe.AsPointer(ref Buffer[0]);
|
||||||
*Pointer = initialValue();
|
*Pointer = initialValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
38
VoiDPlugins.Library/VoiD/GlobalStore.cs
Normal file
38
VoiDPlugins.Library/VoiD/GlobalStore.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using OpenTabletDriver.Plugin.Tablet;
|
||||||
|
|
||||||
|
namespace VoiDPlugins.Library.VoiD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores one instance of <typeparamref name="T"/> per <see cref="TabletReference"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">A class.</typeparam>
|
||||||
|
public static class GlobalStore<T> where T : class
|
||||||
|
{
|
||||||
|
private static readonly object _syncLock = new();
|
||||||
|
private static readonly Dictionary<string, T> _map = new();
|
||||||
|
|
||||||
|
public static U GetOrInitialize<U>(TabletReference tabletReference, Func<U> initialValue) where U : T
|
||||||
|
{
|
||||||
|
lock (_syncLock)
|
||||||
|
{
|
||||||
|
ref var instance = ref CollectionsMarshal.GetValueRefOrAddDefault(_map, tabletReference.Properties.Name, out var exists);
|
||||||
|
if (!exists)
|
||||||
|
instance = initialValue();
|
||||||
|
return (U)instance!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static U Get<U>(TabletReference tabletReference) where U : T
|
||||||
|
{
|
||||||
|
return (U)_map[tabletReference.Properties.Name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Get(TabletReference tabletReference)
|
||||||
|
{
|
||||||
|
return _map[tabletReference.Properties.Name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
VoiDPlugins.Library/VoiD/SharedStore.cs
Normal file
27
VoiDPlugins.Library/VoiD/SharedStore.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VoiDPlugins.Library.VoiD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fast shared store with support for generic ref returning <see cref="GetData"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class SharedStore
|
||||||
|
{
|
||||||
|
private readonly object[] _data;
|
||||||
|
|
||||||
|
public SharedStore()
|
||||||
|
{
|
||||||
|
_data = new object[32];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeData<T>(int i, T data)
|
||||||
|
{
|
||||||
|
_data[i] = data!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe ref T GetData<T>(int i)
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref _data[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,2 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
|
||||||
<NotPluginProject>true</NotPluginProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
</Project>
|
Loading…
Add table
Reference in a new issue