mirror of
https://github.com/vale981/VoiDPlugins
synced 2025-03-05 09:11:38 -05:00
[WIP] Implement Windows Ink in user-mode
This commit is contained in:
parent
218b03ed16
commit
c51d350aea
5 changed files with 451 additions and 0 deletions
|
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MLFilter", "MLFilter\MLFilt
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OemKill", "OemKill\OemKill.csproj", "{4EC8FA2D-0CCC-4FC3-A32A-BC063924A803}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInk", "WindowsInk\WindowsInk.csproj", "{FC135341-4870-4555-8917-7248CFE07FCC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -21,6 +23,10 @@ Global
|
|||
{4EC8FA2D-0CCC-4FC3-A32A-BC063924A803}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4EC8FA2D-0CCC-4FC3-A32A-BC063924A803}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4EC8FA2D-0CCC-4FC3-A32A-BC063924A803}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FC135341-4870-4555-8917-7248CFE07FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC135341-4870-4555-8917-7248CFE07FCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FC135341-4870-4555-8917-7248CFE07FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC135341-4870-4555-8917-7248CFE07FCC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
143
WindowsInk/NativeMethods.cs
Normal file
143
WindowsInk/NativeMethods.cs
Normal file
|
@ -0,0 +1,143 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WindowsInk
|
||||
{
|
||||
using HSYNTHETICPOINTERDEVICE = IntPtr;
|
||||
using HANDLE = IntPtr;
|
||||
using HWND = IntPtr;
|
||||
using DWORD = UInt32;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
}
|
||||
public enum POINTER_INPUT_TYPE
|
||||
{
|
||||
PT_POINTER,
|
||||
PT_TOUCH,
|
||||
PT_PEN,
|
||||
PT_MOUSE,
|
||||
PT_TOUCHPAD
|
||||
}
|
||||
|
||||
public enum POINTER_FEEDBACK_MODE
|
||||
{
|
||||
DEFAULT,
|
||||
INDIRECT,
|
||||
NONE
|
||||
}
|
||||
|
||||
public enum POINTER_BUTTON_CHANGE_TYPE
|
||||
{
|
||||
NONE,
|
||||
FIRSTBUTTON_DOWN,
|
||||
FIRSTBUTTON_UP,
|
||||
SECONDBUTTON_DOWN,
|
||||
SECONDBUTTON_UP,
|
||||
THIRDBUTTON_DOWN,
|
||||
THIRDBUTTON_UP,
|
||||
FOURTHBUTTON_DOWN,
|
||||
FOURTHBUTTON_UP,
|
||||
FIFTHBUTTON_DOWN,
|
||||
FIFTHBUTTON_UP
|
||||
}
|
||||
|
||||
public enum POINTER_FLAGS
|
||||
{
|
||||
POINTER_FLAG_NONE = 0x00000000,
|
||||
POINTER_FLAG_NEW = 0x00000001,
|
||||
POINTER_FLAG_INRANGE = 0x00000002,
|
||||
POINTER_FLAG_INCONTACT = 0x00000004,
|
||||
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
|
||||
POINTER_FLAG_SECONDBUTTON = 0x00000020,
|
||||
POINTER_FLAG_THIRDBUTTON = 0x00000040,
|
||||
POINTER_FLAG_FOURTHBUTTON = 0x00000080,
|
||||
POINTER_FLAG_FIFTHBUTTON = 0x00000100,
|
||||
POINTER_FLAG_PRIMARY = 0x00002000,
|
||||
POINTER_FLAG_CONFIDENCE = 0x000004000,
|
||||
POINTER_FLAG_CANCELED = 0x000008000,
|
||||
POINTER_FLAG_DOWN = 0x00010000,
|
||||
POINTER_FLAG_UPDATE = 0x00020000,
|
||||
POINTER_FLAG_UP = 0x00040000,
|
||||
POINTER_FLAG_WHEEL = 0x00080000,
|
||||
POINTER_FLAG_HWHEEL = 0x00100000,
|
||||
POINTER_FLAG_CAPTURECHANGED = 0x00200000,
|
||||
POINTER_FLAG_HASTRANSFORM = 0x00400000,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINTER_INFO
|
||||
{
|
||||
public POINTER_INPUT_TYPE pointerType;
|
||||
public uint pointerId;
|
||||
public uint frameId;
|
||||
public POINTER_FLAGS pointerFlags;
|
||||
public HANDLE sourceDevice;
|
||||
public HWND hwndTarget;
|
||||
public POINT ptPixelLocation;
|
||||
public POINT ptHimetricLocation;
|
||||
public POINT ptPixelLocationRaw;
|
||||
public POINT ptHimetricLocationRaw;
|
||||
public DWORD dwTime;
|
||||
public uint historyCount;
|
||||
public int InputData;
|
||||
public DWORD dwKeyStates;
|
||||
public ulong PerformanceCount;
|
||||
public POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
|
||||
}
|
||||
|
||||
public enum PEN_FLAGS
|
||||
{
|
||||
NONE = 0x00000000,
|
||||
BARREL = 0x00000001,
|
||||
INVERTED = 0x00000002,
|
||||
ERASER = 0x00000004
|
||||
}
|
||||
|
||||
public enum PEN_MASK
|
||||
{
|
||||
NONE = 0x00000000,
|
||||
PRESSURE = 0x00000001,
|
||||
ROTATION = 0x00000002,
|
||||
TILT_X = 0x00000004,
|
||||
TILT_Y = 0x00000008
|
||||
}
|
||||
|
||||
public struct POINTER_PEN_INFO
|
||||
{
|
||||
public POINTER_INFO pointerInfo;
|
||||
public PEN_FLAGS pointerFlags;
|
||||
public PEN_MASK penMask;
|
||||
public uint pressure;
|
||||
public uint rotation;
|
||||
public int tiltX;
|
||||
public int tiltY;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINTER_TYPE_INFO
|
||||
{
|
||||
public POINTER_INPUT_TYPE type;
|
||||
public POINTER_PEN_INFO penInfo;
|
||||
}
|
||||
|
||||
public static class NativeMethods
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
public static extern HSYNTHETICPOINTERDEVICE CreateSyntheticPointerDevice(POINTER_INPUT_TYPE pointerType,
|
||||
UInt32 maxCount, POINTER_FEEDBACK_MODE mode);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool InjectSyntheticPointerInput(HSYNTHETICPOINTERDEVICE device,
|
||||
[In, MarshalAs(UnmanagedType.LPArray)] POINTER_TYPE_INFO[] pointerInfo, UInt32 count);
|
||||
}
|
||||
}
|
102
WindowsInk/Pen.cs
Normal file
102
WindowsInk/Pen.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using TabletDriverPlugin;
|
||||
|
||||
namespace WindowsInk
|
||||
{
|
||||
using HSYNTHETICPOINTERDEVICE = IntPtr;
|
||||
|
||||
public class Pen
|
||||
{
|
||||
private POINTER_PEN_INFO _penInfo;
|
||||
private POINTER_INFO _pointerInfo;
|
||||
private HSYNTHETICPOINTERDEVICE _penHandle;
|
||||
private bool inContact = false;
|
||||
private POINTER_FLAGS pointerFlags;
|
||||
|
||||
public Pen()
|
||||
{
|
||||
_pointerInfo = new POINTER_INFO
|
||||
{
|
||||
pointerType = POINTER_INPUT_TYPE.PT_PEN,
|
||||
pointerId = 1,
|
||||
frameId = 0,
|
||||
pointerFlags = POINTER_FLAGS.POINTER_FLAG_NONE,
|
||||
sourceDevice = IntPtr.Zero,
|
||||
hwndTarget = IntPtr.Zero,
|
||||
ptPixelLocation = new POINT(),
|
||||
ptHimetricLocation = new POINT(),
|
||||
ptPixelLocationRaw = new POINT(),
|
||||
ptHimetricLocationRaw = new POINT(),
|
||||
dwTime = 0,
|
||||
historyCount = 1,
|
||||
dwKeyStates = 0,
|
||||
PerformanceCount = 0,
|
||||
ButtonChangeType = POINTER_BUTTON_CHANGE_TYPE.NONE
|
||||
};
|
||||
|
||||
_penInfo = new POINTER_PEN_INFO
|
||||
{
|
||||
pointerInfo = _pointerInfo,
|
||||
pointerFlags = PEN_FLAGS.NONE,
|
||||
penMask = PEN_MASK.NONE,
|
||||
pressure = 0,
|
||||
rotation = 0,
|
||||
tiltX = 0,
|
||||
tiltY = 0
|
||||
};
|
||||
|
||||
// Retrieve handle to custom pen
|
||||
_penHandle = NativeMethods.CreateSyntheticPointerDevice(POINTER_INPUT_TYPE.PT_PEN, 1, POINTER_FEEDBACK_MODE.INDIRECT);
|
||||
|
||||
// Notify WindowsInk
|
||||
_pointerInfo.pointerFlags = POINTER_FLAGS.POINTER_FLAG_NEW;
|
||||
Inject();
|
||||
|
||||
// Back to normal state
|
||||
_pointerInfo.pointerFlags = POINTER_FLAGS.POINTER_FLAG_INRANGE | POINTER_FLAGS.POINTER_FLAG_PRIMARY;
|
||||
}
|
||||
|
||||
public void Inject()
|
||||
{
|
||||
var pointer = new POINTER_TYPE_INFO
|
||||
{
|
||||
type = POINTER_INPUT_TYPE.PT_PEN,
|
||||
penInfo = _penInfo
|
||||
};
|
||||
if (!NativeMethods.InjectSyntheticPointerInput(_penHandle, new POINTER_TYPE_INFO[] { pointer }, 1))
|
||||
{
|
||||
Log.Write("WindowsInk", "Injection Failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(POINT point)
|
||||
{
|
||||
_pointerInfo.ptPixelLocation = point;
|
||||
_pointerInfo.ptPixelLocationRaw = point;
|
||||
_pointerInfo.ptHimetricLocation = point;
|
||||
_pointerInfo.ptHimetricLocationRaw = point;
|
||||
}
|
||||
|
||||
public void SetPressure(uint pressure)
|
||||
{
|
||||
if (pressure > 0)
|
||||
inContact = true;
|
||||
_penInfo.pressure = pressure;
|
||||
}
|
||||
|
||||
public void SetPointerFlags(POINTER_FLAGS flags)
|
||||
{
|
||||
pointerFlags |= flags;
|
||||
}
|
||||
|
||||
public void UnsetPointerFlags(POINTER_FLAGS flags)
|
||||
{
|
||||
pointerFlags &= ~flags;
|
||||
}
|
||||
|
||||
public void ClearPointerFlags()
|
||||
{
|
||||
pointerFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
189
WindowsInk/WindowsInk.cs
Normal file
189
WindowsInk/WindowsInk.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TabletDriverPlugin;
|
||||
using TabletDriverPlugin.Attributes;
|
||||
using TabletDriverPlugin.Output;
|
||||
using TabletDriverPlugin.Platform.Display;
|
||||
using TabletDriverPlugin.Platform.Pointer;
|
||||
using TabletDriverPlugin.Tablet;
|
||||
|
||||
namespace WindowsInk
|
||||
{
|
||||
[PluginName("Windows Ink")]
|
||||
public class InkOutputMode : IOutputMode
|
||||
{
|
||||
private float[] _rotationMatrix;
|
||||
private float _halfDisplayWidth, _halfDisplayHeight, _halfTabletWidth, _halfTabletHeight;
|
||||
private float _minX, _maxX, _minY, _maxY;
|
||||
|
||||
private List<IFilter> _filters, _preFilters = new List<IFilter>(), _postFilters = new List<IFilter>();
|
||||
public IEnumerable<IFilter> Filters
|
||||
{
|
||||
set
|
||||
{
|
||||
_filters = value.ToList();
|
||||
_preFilters.Clear();
|
||||
_postFilters.Clear();
|
||||
foreach (IFilter filter in _filters)
|
||||
if (filter.FilterStage == FilterStage.PreTranspose)
|
||||
_preFilters.Add(filter);
|
||||
else if (filter.FilterStage == FilterStage.PostTranspose)
|
||||
_postFilters.Add(filter);
|
||||
}
|
||||
get => _filters;
|
||||
}
|
||||
|
||||
private TabletProperties _tabletProperties;
|
||||
public TabletProperties TabletProperties
|
||||
{
|
||||
set
|
||||
{
|
||||
_tabletProperties = value;
|
||||
UpdateCache();
|
||||
}
|
||||
get => _tabletProperties;
|
||||
}
|
||||
|
||||
private Area _displayArea, _tabletArea;
|
||||
|
||||
public Area Input
|
||||
{
|
||||
set
|
||||
{
|
||||
_tabletArea = value;
|
||||
UpdateCache();
|
||||
}
|
||||
get => _tabletArea;
|
||||
}
|
||||
|
||||
public Area Output
|
||||
{
|
||||
set
|
||||
{
|
||||
_displayArea = value;
|
||||
UpdateCache();
|
||||
}
|
||||
get => _displayArea;
|
||||
}
|
||||
|
||||
public IVirtualScreen VirtualScreen { set; get; }
|
||||
public IPointerHandler PointerHandler => new InkPointerHandler();
|
||||
public bool AreaClipping { set; get; }
|
||||
|
||||
internal void UpdateCache()
|
||||
{
|
||||
_rotationMatrix = Input?.GetRotationMatrix();
|
||||
|
||||
_halfDisplayWidth = Output?.Width / 2 ?? 0;
|
||||
_halfDisplayHeight = Output?.Height / 2 ?? 0;
|
||||
_halfTabletWidth = Input?.Width / 2 ?? 0;
|
||||
_halfTabletHeight = Input?.Height / 2 ?? 0;
|
||||
|
||||
_minX = Output?.Position.X - _halfDisplayWidth ?? 0;
|
||||
_maxX = Output?.Position.X + Output?.Width - _halfDisplayWidth ?? 0;
|
||||
_minY = Output?.Position.Y - _halfDisplayHeight ?? 0;
|
||||
_maxY = Output?.Position.Y + Output?.Height - _halfDisplayHeight ?? 0;
|
||||
}
|
||||
|
||||
public void Read(IDeviceReport report)
|
||||
{
|
||||
if (report is ITabletReport tabletReport)
|
||||
{
|
||||
if (TabletProperties.ActiveReportID.IsInRange(tabletReport.ReportID))
|
||||
{
|
||||
if (PointerHandler is IPressureHandler pressureHandler)
|
||||
pressureHandler.SetPressure((float)tabletReport.Pressure / (float)TabletProperties.MaxPressure);
|
||||
|
||||
var pos = Transpose(tabletReport);
|
||||
PointerHandler.SetPosition(pos);
|
||||
}
|
||||
}
|
||||
// HandleBinding(report);
|
||||
}
|
||||
|
||||
internal Point Transpose(ITabletReport report)
|
||||
{
|
||||
var pos = new Point(report.Position.X, report.Position.Y);
|
||||
|
||||
// Pre Filter
|
||||
foreach (IFilter filter in _preFilters)
|
||||
pos = filter.Filter(pos);
|
||||
|
||||
// Normalize (ratio of 1)
|
||||
pos.X /= TabletProperties.MaxX;
|
||||
pos.Y /= TabletProperties.MaxY;
|
||||
|
||||
// Scale to tablet dimensions (mm)
|
||||
pos.X *= TabletProperties.Width;
|
||||
pos.Y *= TabletProperties.Height;
|
||||
|
||||
// Adjust area to set origin to 0,0
|
||||
pos -= Input.Position;
|
||||
|
||||
// Rotation
|
||||
if (Input.Rotation != 0f)
|
||||
{
|
||||
var tempCopy = new Point(pos.X, pos.Y);
|
||||
pos.X = (tempCopy.X * _rotationMatrix[0]) + (tempCopy.Y * _rotationMatrix[1]);
|
||||
pos.Y = (tempCopy.X * _rotationMatrix[2]) + (tempCopy.Y * _rotationMatrix[3]);
|
||||
}
|
||||
|
||||
// Move area back
|
||||
pos.X += _halfTabletWidth;
|
||||
pos.Y += _halfTabletHeight;
|
||||
|
||||
// Scale to tablet area (ratio of 1)
|
||||
pos.X /= Input.Width;
|
||||
pos.Y /= Input.Height;
|
||||
|
||||
// Scale to display area
|
||||
pos.X *= Output.Width;
|
||||
pos.Y *= Output.Height;
|
||||
|
||||
// Adjust display offset by center
|
||||
pos.X += Output.Position.X - _halfDisplayWidth;
|
||||
pos.Y += Output.Position.Y - _halfDisplayHeight;
|
||||
|
||||
// Clipping to display bounds
|
||||
if (AreaClipping)
|
||||
{
|
||||
if (pos.X < _minX)
|
||||
pos.X = _minX;
|
||||
if (pos.X > _maxX)
|
||||
pos.X = _maxX;
|
||||
if (pos.Y < _minY)
|
||||
pos.Y = _minY;
|
||||
if (pos.Y > _maxY)
|
||||
pos.Y = _maxY;
|
||||
}
|
||||
|
||||
// Post Filter
|
||||
foreach (IFilter filter in _postFilters)
|
||||
pos = filter.Filter(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
public class InkPointerHandler : IPointerHandler, IPressureHandler
|
||||
{
|
||||
private Pen pen = new Pen();
|
||||
private Point _lastPos;
|
||||
public Point GetPosition()
|
||||
{
|
||||
return _lastPos;
|
||||
}
|
||||
|
||||
public void SetPosition(Point pos)
|
||||
{
|
||||
pen.SetPosition(new POINT((int)pos.X, (int)pos.Y));
|
||||
pen.Inject();
|
||||
_lastPos = pos;
|
||||
}
|
||||
|
||||
public void SetPressure(float percentage)
|
||||
{
|
||||
pen.SetPressure((uint)(percentage * 1024));
|
||||
}
|
||||
}
|
||||
}
|
11
WindowsInk/WindowsInk.csproj
Normal file
11
WindowsInk/WindowsInk.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.1; net5.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TabletDriverPlugin" Version="0.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Add table
Reference in a new issue