diff --git a/Directory.Build.props b/Directory.Build.props index 41cfb93d7..347a37cd8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 0.6.0.4 + 0.7.0.0 net6.0 preview $(VersionBase) diff --git a/OpenTabletDriver.Benchmarks/OpenTabletDriver.Benchmarks.csproj b/OpenTabletDriver.Benchmarks/OpenTabletDriver.Benchmarks.csproj index 3682450fb..e023e4b5f 100644 --- a/OpenTabletDriver.Benchmarks/OpenTabletDriver.Benchmarks.csproj +++ b/OpenTabletDriver.Benchmarks/OpenTabletDriver.Benchmarks.csproj @@ -7,6 +7,7 @@ + diff --git a/OpenTabletDriver.Benchmarks/Output/NoopAbsoluteMode.cs b/OpenTabletDriver.Benchmarks/Output/NoopAbsoluteMode.cs index e1851a59b..6e613a849 100644 --- a/OpenTabletDriver.Benchmarks/Output/NoopAbsoluteMode.cs +++ b/OpenTabletDriver.Benchmarks/Output/NoopAbsoluteMode.cs @@ -1,6 +1,6 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Benchmarks.Output { diff --git a/OpenTabletDriver.Benchmarks/Output/OutputBenchmark.cs b/OpenTabletDriver.Benchmarks/Output/OutputBenchmark.cs index ad4462cb9..4050cf4d3 100644 --- a/OpenTabletDriver.Benchmarks/Output/OutputBenchmark.cs +++ b/OpenTabletDriver.Benchmarks/Output/OutputBenchmark.cs @@ -1,8 +1,8 @@ using System; using BenchmarkDotNet.Attributes; using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Output; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Benchmarks.Output { diff --git a/OpenTabletDriver.Benchmarks/Parser/ReportParserBenchmark.cs b/OpenTabletDriver.Benchmarks/Parser/ReportParserBenchmark.cs index 5df6b8b2f..48476ebe9 100644 --- a/OpenTabletDriver.Benchmarks/Parser/ReportParserBenchmark.cs +++ b/OpenTabletDriver.Benchmarks/Parser/ReportParserBenchmark.cs @@ -1,7 +1,7 @@ using System; using BenchmarkDotNet.Attributes; using OpenTabletDriver.Configurations.Parsers; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Benchmarks.Parser { diff --git a/OpenTabletDriver.Benchmarks/Plugin/PluginManagerBenchmark.cs b/OpenTabletDriver.Benchmarks/Plugin/PluginManagerBenchmark.cs index d34a3f151..6c1da187f 100644 --- a/OpenTabletDriver.Benchmarks/Plugin/PluginManagerBenchmark.cs +++ b/OpenTabletDriver.Benchmarks/Plugin/PluginManagerBenchmark.cs @@ -1,4 +1,6 @@ using BenchmarkDotNet.Attributes; +using Moq; +using OpenTabletDriver.Desktop.Interop.AppInfo; using OpenTabletDriver.Desktop.Reflection; namespace OpenTabletDriver.Benchmarks.Plugin @@ -6,12 +8,13 @@ namespace OpenTabletDriver.Benchmarks.Plugin [DryJob] public class PluginManagerBenchmark { - public PluginManager pluginManager; + private PluginManager _pluginManager; [Benchmark] public void PluginManagerCtor() { - pluginManager = new PluginManager(); + var appInfo = new Mock(); + _pluginManager = new PluginManager(appInfo.Object); } } } diff --git a/OpenTabletDriver.Configurations/Configurations/10moon/1060N.json b/OpenTabletDriver.Configurations/Configurations/10moon/1060N.json index b40ccfa7c..1547e0975 100644 --- a/OpenTabletDriver.Configurations/Configurations/10moon/1060N.json +++ b/OpenTabletDriver.Configurations/Configurations/10moon/1060N.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -32,7 +30,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 2290, "ProductID": 26641, @@ -53,4 +51,4 @@ "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Acepen/AP 1060.json b/OpenTabletDriver.Configurations/Configurations/Acepen/AP 1060.json index d5ee58d73..f07671420 100644 --- a/OpenTabletDriver.Configurations/Configurations/Acepen/AP 1060.json +++ b/OpenTabletDriver.Configurations/Configurations/Acepen/AP 1060.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,6 +34,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Artisul/A1201.json b/OpenTabletDriver.Configurations/Configurations/Artisul/A1201.json index bc4a3e7a1..096e97f97 100644 --- a/OpenTabletDriver.Configurations/Configurations/Artisul/A1201.json +++ b/OpenTabletDriver.Configurations/Configurations/Artisul/A1201.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Artisul/AP604.json b/OpenTabletDriver.Configurations/Configurations/Artisul/AP604.json index 386333e03..f33864773 100644 --- a/OpenTabletDriver.Configurations/Configurations/Artisul/AP604.json +++ b/OpenTabletDriver.Configurations/Configurations/Artisul/AP604.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -30,8 +28,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Artisul/M0610 Pro.json b/OpenTabletDriver.Configurations/Configurations/Artisul/M0610 Pro.json index 3be32c3cd..c971eea5b 100644 --- a/OpenTabletDriver.Configurations/Configurations/Artisul/M0610 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Artisul/M0610 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -25,7 +23,7 @@ "ProductID": 129, "InputReportLength": 10, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": { @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/1060 Pro.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/1060 Pro.json index 49dbdf735..3f3879670 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/1060 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/1060 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K Pro.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K Pro.json index 4c984ce23..4e94e1bf2 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K.json index 1264135e8..255ab9efe 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M106K.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K Pro.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K Pro.json index f4da41d17..e72b00dee 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K.json index 10169ae36..b68935e09 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M10K.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 11 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M1220.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M1220.json index dee2c8966..bbe45cf8b 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M1220.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M1220.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M1230.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M1230.json index 0d865cc10..1ba9555c9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M1230.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M1230.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/M6.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/M6.json index 27af7322a..9642c9fdd 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/M6.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/M6.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 13 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1161.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1161.json index 183a9924a..f51fcb7ca 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1161.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1161.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1560.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1560.json index a3619de7f..9760b12b8 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1560.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1560.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1561.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1561.json index ba7646e8e..99adf8289 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1561.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/PD1561.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/S56K.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/S56K.json index 94bcd3123..a792eb467 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/S56K.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/S56K.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -23,7 +21,7 @@ "ProductID": 109, "InputReportLength": 8, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": { @@ -38,7 +36,7 @@ "ProductID": 110, "InputReportLength": 8, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": { @@ -49,8 +47,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/S620.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/S620.json index 005e3d8e2..b429c9f4a 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/S620.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/S620.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Gaomon/S630.json b/OpenTabletDriver.Configurations/Configurations/Gaomon/S630.json index 2b2d0deeb..b0d3af642 100644 --- a/OpenTabletDriver.Configurations/Configurations/Gaomon/S630.json +++ b/OpenTabletDriver.Configurations/Configurations/Gaomon/S630.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Genius/G-Pen 560.json b/OpenTabletDriver.Configurations/Configurations/Genius/G-Pen 560.json index f15daf342..a0fda732c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Genius/G-Pen 560.json +++ b/OpenTabletDriver.Configurations/Configurations/Genius/G-Pen 560.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1024, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Genius/i608x.json b/OpenTabletDriver.Configurations/Configurations/Genius/i608x.json index 3f4d66a7b..e5af3af8e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Genius/i608x.json +++ b/OpenTabletDriver.Configurations/Configurations/Genius/i608x.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": { @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/420.json b/OpenTabletDriver.Configurations/Configurations/Huion/420.json index 98efbac53..dd4272c8f 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/420.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/420.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -52,8 +50,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/G10T.json b/OpenTabletDriver.Configurations/Configurations/Huion/G10T.json index ab1a9a41c..b59a43df5 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/G10T.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/G10T.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/GC610.json b/OpenTabletDriver.Configurations/Configurations/Huion/GC610.json index b48eced1e..89910f7b5 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/GC610.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/GC610.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/GT-220 V2.json b/OpenTabletDriver.Configurations/Configurations/Huion/GT-220 V2.json index bff61bdfa..33ad9b6f5 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/GT-220 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/GT-220 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -34,8 +32,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/GT-221 Pro.json b/OpenTabletDriver.Configurations/Configurations/Huion/GT-221 Pro.json index 6c02214c3..852cc9534 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/GT-221 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/GT-221 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H1060P.json b/OpenTabletDriver.Configurations/Configurations/Huion/H1060P.json index 8dd515c55..001f89a0b 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H1060P.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H1060P.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -66,8 +64,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H1161.json b/OpenTabletDriver.Configurations/Configurations/Huion/H1161.json index 006356991..3cde1a63a 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H1161.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H1161.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H420.json b/OpenTabletDriver.Configurations/Configurations/Huion/H420.json index 09ca9506d..dfe1bc829 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H420.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H420.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 3 @@ -37,8 +35,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H420X.json b/OpenTabletDriver.Configurations/Configurations/Huion/H420X.json index 6fb10058a..4c7b5b3ce 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H420X.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H420X.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -49,8 +47,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H430P.json b/OpenTabletDriver.Configurations/Configurations/Huion/H430P.json index 57d8d08a0..1fcf1fc6c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H430P.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H430P.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -66,8 +64,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H580X.json b/OpenTabletDriver.Configurations/Configurations/Huion/H580X.json index df518ccdb..670cec280 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H580X.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H580X.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro V2.json b/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro V2.json index 3c5aebd7f..463d09205 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro V2.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro.json b/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro.json index dc08e868c..f7baac484 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H610 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H610X.json b/OpenTabletDriver.Configurations/Configurations/Huion/H610X.json index cfeae5cef..b8ad3b076 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H610X.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H610X.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H640P.json b/OpenTabletDriver.Configurations/Configurations/Huion/H640P.json index fca6f0a92..d15cc6059 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H640P.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H640P.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -66,8 +64,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H642.json b/OpenTabletDriver.Configurations/Configurations/Huion/H642.json index 6017c1c4c..c0bd21ff9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H642.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H642.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/H950P.json b/OpenTabletDriver.Configurations/Configurations/Huion/H950P.json index 78012832d..66bd5adbb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/H950P.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/H950P.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/HC16.json b/OpenTabletDriver.Configurations/Configurations/Huion/HC16.json index 749dce5ed..c3f2531e9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/HC16.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/HC16.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/HS610.json b/OpenTabletDriver.Configurations/Configurations/Huion/HS610.json index 82d0ad051..84f07ac6b 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/HS610.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/HS610.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/HS611.json b/OpenTabletDriver.Configurations/Configurations/Huion/HS611.json index e7008dbd8..0b50b63de 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/HS611.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/HS611.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/HS64.json b/OpenTabletDriver.Configurations/Configurations/Huion/HS64.json index 2a3f75426..2ab79c0d7 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/HS64.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/HS64.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/HS95.json b/OpenTabletDriver.Configurations/Configurations/Huion/HS95.json index da0910436..ba5ad808e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/HS95.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/HS95.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 13.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 13.json index 9f427ca4b..422fa2498 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 13.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 13.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16 (2021).json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16 (2021).json index c63ff31c6..043e70638 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16 (2021).json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16 (2021).json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16.json index f13a47cdd..181eb5250 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 16.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 20.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 20.json index e8dcc6510..427e266bb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 20.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 20.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -34,8 +32,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 22 Plus.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 22 Plus.json index 21e46a108..12e139d29 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 22 Plus.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas 22 Plus.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 0 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 12.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 12.json index c0ff5a279..7792cdea9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 12.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 12.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13 (2.5k).json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13 (2.5k).json index 98a12424a..1577c8c8c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13 (2.5k).json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13 (2.5k).json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 7 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13.json index e513aa9be..7a9c7f1be 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 13.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 16.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 16.json index eef5ec744..24c791990 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 16.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 16.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 20.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 20.json index 5523896b8..a16f88dc1 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 20.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 20.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 16 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 24.json b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 24.json index 632c1082d..5e76ff110 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 24.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Kamvas Pro 24.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 20 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus (2048).json b/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus (2048).json index bbf59aa0e..1a9a35c01 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus (2048).json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus (2048).json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus.json b/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus.json index ba5ec2e7f..d167aaef0 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/New 1060 Plus.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Q11K V2.json b/OpenTabletDriver.Configurations/Configurations/Huion/Q11K V2.json index e028da001..02932fa1e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Q11K V2.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Q11K V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Q11K.json b/OpenTabletDriver.Configurations/Configurations/Huion/Q11K.json index 389badfc6..2a317d024 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Q11K.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Q11K.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/Q620M.json b/OpenTabletDriver.Configurations/Configurations/Huion/Q620M.json index 7d92786bf..e21710e68 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/Q620M.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/Q620M.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/WH1409 V2.json b/OpenTabletDriver.Configurations/Configurations/Huion/WH1409 V2.json index 9b1403fd7..8426653a1 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/WH1409 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/WH1409 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Huion/WH1409.json b/OpenTabletDriver.Configurations/Configurations/Huion/WH1409.json index 370839310..488e6a823 100644 --- a/OpenTabletDriver.Configurations/Configurations/Huion/WH1409.json +++ b/OpenTabletDriver.Configurations/Configurations/Huion/WH1409.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -36,8 +34,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/A609.json b/OpenTabletDriver.Configurations/Configurations/Parblo/A609.json index 317394d3f..5f647ee12 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/A609.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/A609.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 1 - } + "ButtonCount": 1 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/A610 Pro.json b/OpenTabletDriver.Configurations/Configurations/Parblo/A610 Pro.json index 9c61e98b2..336385ceb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/A610 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/A610 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/A640 V2.json b/OpenTabletDriver.Configurations/Configurations/Parblo/A640 V2.json index 368ca2d18..48e595631 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/A640 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/A640 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo M.json b/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo M.json index 340a7a57b..0ffcdd6b8 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo M.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo M.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo S.json b/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo S.json index 103d9106c..d9106d4ea 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo S.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/Intangbo S.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos M.json b/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos M.json index 83646ab35..1bb4dc06e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos M.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos M.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos S.json b/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos S.json index 65ced0d32..5743f11c2 100644 --- a/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos S.json +++ b/OpenTabletDriver.Configurations/Configurations/Parblo/Ninos S.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 5 @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/RobotPen/T9A.json b/OpenTabletDriver.Configurations/Configurations/RobotPen/T9A.json index 6c3fc9cff..9bfeb47bd 100644 --- a/OpenTabletDriver.Configurations/Configurations/RobotPen/T9A.json +++ b/OpenTabletDriver.Configurations/Configurations/RobotPen/T9A.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 1 - } + "ButtonCount": 1 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/UC-Logic/1060N.json b/OpenTabletDriver.Configurations/Configurations/UC-Logic/1060N.json index 5c77e4959..716089f5f 100644 --- a/OpenTabletDriver.Configurations/Configurations/UC-Logic/1060N.json +++ b/OpenTabletDriver.Configurations/Configurations/UC-Logic/1060N.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -38,9 +36,9 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1", "MacInterface": "0" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/UC-Logic/PF1209.json b/OpenTabletDriver.Configurations/Configurations/UC-Logic/PF1209.json index f829f696c..176ac0c9f 100644 --- a/OpenTabletDriver.Configurations/Configurations/UC-Logic/PF1209.json +++ b/OpenTabletDriver.Configurations/Configurations/UC-Logic/PF1209.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -34,8 +32,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/UGEE/U1600.json b/OpenTabletDriver.Configurations/Configurations/UGEE/U1600.json index 42fe1b3fa..efc2e744c 100644 --- a/OpenTabletDriver.Configurations/Configurations/UGEE/U1600.json +++ b/OpenTabletDriver.Configurations/Configurations/UGEE/U1600.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/UGTABLET/M708.json b/OpenTabletDriver.Configurations/Configurations/UGTABLET/M708.json index 85b0c1134..4c347529a 100644 --- a/OpenTabletDriver.Configurations/Configurations/UGTABLET/M708.json +++ b/OpenTabletDriver.Configurations/Configurations/UGTABLET/M708.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -38,8 +36,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/A15 Pro.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/A15 Pro.json index aceb9d0c6..b29a03d37 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/A15 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/A15 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/A15.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/A15.json index 802665802..3d5c6f350 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/A15.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/A15.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 12 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/A30.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/A30.json index ab4765105..831c14088 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/A30.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/A30.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/A50 V2.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/A50 V2.json index 11a5ecff0..eae0abe69 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/A50 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/A50 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/A50.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/A50.json index 1c43f990f..b3b2cefa4 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/A50.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/A50.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/S640 V2.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/S640 V2.json index 6e3f1c521..941feb3ac 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/S640 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/S640 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/S640.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/S640.json index 968c9f1b9..3170fef18 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/S640.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/S640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/VEIKK/VK640.json b/OpenTabletDriver.Configurations/Configurations/VEIKK/VK640.json index e7c23353b..5cf1f5ff5 100644 --- a/OpenTabletDriver.Configurations/Configurations/VEIKK/VK640.json +++ b/OpenTabletDriver.Configurations/Configurations/VEIKK/VK640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/ViewSonic/Woodpad PF0730.json b/OpenTabletDriver.Configurations/Configurations/ViewSonic/Woodpad PF0730.json index 7173922fe..1f163ac32 100644 --- a/OpenTabletDriver.Configurations/Configurations/ViewSonic/Woodpad PF0730.json +++ b/OpenTabletDriver.Configurations/Configurations/ViewSonic/Woodpad PF0730.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,6 +30,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-430.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-430.json index 95d60a6a7..012b26342 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-430.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-430.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-440.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-440.json index 969e3ce29..3beb69423 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-440.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-440.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 2 @@ -56,13 +54,13 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 21, "InputReportLength": 64, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.AuxReportParser", + "ReportParser": "OpenTabletDriver.Tablet.AuxReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": {}, @@ -70,4 +68,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-450.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-450.json index 0ce4a97da..40e542d86 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-450.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-450.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-460.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-460.json index af2a66561..55b1753a8 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-460.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-460.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-630.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-630.json index ed0540376..793f748f7 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-630.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-630.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-640.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-640.json index 3097d81c0..eefda4d22 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-640.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 2 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-650.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-650.json index 404832592..2d9014179 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-650.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTE-650.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTF-430.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTF-430.json index c44c21c9b..8a9c62475 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTF-430.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTF-430.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,6 +30,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-460.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-460.json index e5e4aab97..fe93e8652 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-460.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-460.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -60,7 +58,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 214, @@ -85,4 +83,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-461.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-461.json index 952b95cf3..b8b2ca933 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-461.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-461.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -60,6 +58,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-470.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-470.json index 1e83504d6..571d37bf4 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-470.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-470.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 222, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-480.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-480.json index 22bd7d829..d92b87a05 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-480.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-480.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 770, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-490.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-490.json index 5e6590d5b..9c7afa713 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-490.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-490.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 828, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-661.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-661.json index feb86c879..c9d9591bd 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-661.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-661.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-670.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-670.json index 7ac134294..44390a27f 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-670.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-670.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 223, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-680.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-680.json index 2a7e2db2c..70af85eee 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-680.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-680.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 771, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-690.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-690.json index 780feba17..1c4944228 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-690.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTH-690.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 830, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100.json index e85e08237..73039f006 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -43,6 +41,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100WL.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100WL.json index 6bfeb3f1d..84cb00e99 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100WL.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-4100WL.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -95,6 +93,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-460.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-460.json index 596b66ca4..fc2d29deb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-460.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-460.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,13 +43,13 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 212, "InputReportLength": 64, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.AuxReportParser", + "ReportParser": "OpenTabletDriver.Tablet.AuxReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": {}, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-470.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-470.json index 305fccc58..093bdfbf3 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-470.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-470.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,7 +43,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 221, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-471.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-471.json index d69a6ec9f..b0a04abbd 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-471.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-471.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,13 +43,13 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 768, "InputReportLength": 64, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.AuxReportParser", + "ReportParser": "OpenTabletDriver.Tablet.AuxReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": {}, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-472.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-472.json index b17cbdc28..cb3f5c2ce 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-472.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-472.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,7 +43,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 890, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-480.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-480.json index 3a4b41eb0..9cff5ff02 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-480.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-480.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 782, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-490.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-490.json index 578c953fe..66fd83785 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-490.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-490.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 827, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100.json index d5ace6e89..ffbe27f8e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100WL.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100WL.json index 4a647ec40..0aaf02e12 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100WL.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-6100WL.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-671.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-671.json index cbce24c4d..90afe4eab 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-671.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-671.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,13 +43,13 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 769, "InputReportLength": 64, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.AuxReportParser", + "ReportParser": "OpenTabletDriver.Tablet.AuxReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": {}, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-672.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-672.json index 4be8adcb0..56fcb06cd 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-672.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-672.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,7 +43,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 891, @@ -59,4 +57,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-680.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-680.json index 126a3ac98..04f3c014f 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-680.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-680.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 803, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-690.json b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-690.json index fc2c4ea5f..c261c69bd 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-690.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/CTL-690.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 829, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/DTC-133.json b/OpenTabletDriver.Configurations/Configurations/Wacom/DTC-133.json index ffc170e84..19c9e918f 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/DTC-133.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/DTC-133.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 4095, - "Buttons": { - "ButtonCount": 1 - } + "ButtonCount": 1 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405-U.json index 760afabdf..3a86f18f3 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -23,7 +21,7 @@ "ProductID": 16, "InputReportLength": 8, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": [ "AgI=" ], @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405A-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405A-U.json index 50b118246..241e76c2f 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405A-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/ET-0405A-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/FT-0405-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/FT-0405-U.json index 21713b67f..3349debf9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/FT-0405-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/FT-0405-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0405-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0405-U.json index 573086277..d6c063d66 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0405-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0405-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0608-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0608-U.json index cf85379ba..31a117784 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0608-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0608-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0912-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0912-U.json index 83fdd2c38..20ece3439 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0912-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-0912-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1212-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1212-U.json index 0304c4ab6..d36134759 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1212-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1212-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1218-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1218-U.json index 65aa7043e..2b41bf759 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1218-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/GD-1218-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/MTE-450.json b/OpenTabletDriver.Configurations/Configurations/Wacom/MTE-450.json index 8e2cebe77..fac476fe4 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/MTE-450.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/MTE-450.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 511, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -25,7 +23,7 @@ "ProductID": 101, "InputReportLength": 5, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": [ "AgI=" ], @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-450.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-450.json index b891d52a7..9cff00776 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-450.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-450.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 38, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-451.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-451.json index a0424fd0c..2648b95eb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-451.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-451.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 788, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-460.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-460.json index 5edc8089d..f2a4de4d9 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-460.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-460.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -49,7 +47,7 @@ ] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 914, @@ -63,4 +61,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-650.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-650.json index e37cf49a6..3a461e5bf 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-650.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-650.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 39, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-651.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-651.json index 0e8f1855a..999a82585 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-651.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-651.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 789, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-660.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-660.json index bdedc8216..8b3cabc12 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-660.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-660.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,14 +34,14 @@ "ProductID": 864, "InputReportLength": 361, "OutputReportLength": null, - "ReportParser": "OpenTabletDriver.Plugin.Tablet.TabletReportParser", + "ReportParser": "OpenTabletDriver.Tablet.TabletReportParser", "FeatureInitReport": null, "OutputInitReport": null, "DeviceStrings": {}, "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 855, @@ -57,4 +55,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-850.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-850.json index b0deea6c9..59e33981b 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-850.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-850.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 40, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-851.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-851.json index 4b94032c2..27c92abb7 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-851.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-851.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 791, @@ -61,4 +59,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-860.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-860.json index b1b032faa..9ab5ed942 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-860.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTH-860.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -43,7 +41,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 1386, "ProductID": 856, @@ -57,4 +55,4 @@ } ], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-1240.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-1240.json index b6269f074..016f652b3 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-1240.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-1240.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 9 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-440.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-440.json index 23e4c0f4c..18fbc3cc2 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-440.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-440.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 7 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-450.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-450.json index 7e96f0768..f1171f1df 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-450.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-450.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-540WL.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-540WL.json index 5857f25cc..8d65e79e1 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-540WL.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-540WL.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 9 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-640.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-640.json index 292cdda63..1747f2105 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-640.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 9 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-650.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-650.json index c99b196fa..46f781e96 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-650.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-650.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-840.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-840.json index 4cb73d00e..2df3b093c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-840.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTK-840.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 9 @@ -47,6 +45,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1230.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1230.json index e530082a6..616ac20cb 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1230.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1230.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1231W.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1231W.json index b0665de0b..605ea976e 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1231W.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-1231W.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-430.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-430.json index 4753593c4..e064ce6ab 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-430.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-430.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-431W.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-431W.json index ac02ed30c..3ffe26614 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-431W.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-431W.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-630.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-630.json index 989cfdf02..13ffc272c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-630.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-630.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-631W.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-631W.json index 5e616cd19..b1300bf12 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-631W.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-631W.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-930.json b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-930.json index 4c0ef5136..dd5946e95 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-930.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/PTZ-930.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2046, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0405-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0405-U.json index d748f4519..446109e7c 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0405-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0405-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0608-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0608-U.json index 811321774..cb7790656 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0608-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0608-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0912-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0912-U.json index 16a457161..fa8874d92 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0912-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-0912-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1212-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1212-U.json index dbdae16b5..4d398ab29 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1212-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1212-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1218-U.json b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1218-U.json index 0806d8fa5..4ab43f403 100644 --- a/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1218-U.json +++ b/OpenTabletDriver.Configurations/Configurations/Wacom/XD-1218-U.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 1023, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -47,8 +45,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "FeatureInitDelayMs": "150" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XENX/P1-640.json b/OpenTabletDriver.Configurations/Configurations/XENX/P1-640.json index 493c5c41a..0d7b07bfc 100644 --- a/OpenTabletDriver.Configurations/Configurations/XENX/P1-640.json +++ b/OpenTabletDriver.Configurations/Configurations/XENX/P1-640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XENX/P3-1060.json b/OpenTabletDriver.Configurations/Configurations/XENX/P3-1060.json index 1602a2eec..4fe343f53 100644 --- a/OpenTabletDriver.Configurations/Configurations/XENX/P3-1060.json +++ b/OpenTabletDriver.Configurations/Configurations/XENX/P3-1060.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 10 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XENX/X1-640.json b/OpenTabletDriver.Configurations/Configurations/XENX/X1-640.json index bf369d4ce..139867203 100644 --- a/OpenTabletDriver.Configurations/Configurations/XENX/X1-640.json +++ b/OpenTabletDriver.Configurations/Configurations/XENX/X1-640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 5 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 (2nd Gen).json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 (2nd Gen).json index 5bf384c89..144a26a9d 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 (2nd Gen).json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 (2nd Gen).json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 Pro.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 Pro.json index 10eeda699..ecc151568 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12.json index d79067cce..08a591474 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 12.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 13.3 Pro.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 13.3 Pro.json index 6c09c5ebb..f98f2289d 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 13.3 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 13.3 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6 Pro.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6 Pro.json index 4a69e2d08..0aeb22806 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6.json index 89aa701c5..111a464ee 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 15.6.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -47,7 +45,7 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [ + "AuxiliaryDeviceIdentifiers": [ { "VendorID": 10429, "ProductID": 12, @@ -65,4 +63,4 @@ "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 22HD.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 22HD.json index 471428ed0..322eef724 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 22HD.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Artist 22HD.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,6 +30,6 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT1060.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT1060.json index e5c8b3e21..46bf90602 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT1060.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT1060.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,6 +30,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT430.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT430.json index b52edb679..cdb28b929 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT430.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT430.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,8 +43,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT640.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT640.json index 3fe9d2f07..c1e230f69 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT640.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/CT640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -45,6 +43,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2 (variant 2).json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2 (variant 2).json index 854deb1d8..552dd6130 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2 (variant 2).json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2 (variant 2).json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -36,8 +34,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2.json index 1b51a7020..5f36081b0 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -51,8 +49,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01.json index d193bacc5..1a55c27c4 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 01.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 02.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 02.json index ba97e61fc..d7217b138 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 02.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 02.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 03.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 03.json index 1a43be010..8bf4fdb09 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 03.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco 03.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Medium.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Medium.json index 49150f774..52a52667a 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Medium.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Medium.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Small.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Small.json index 2400a732a..9fd856ead 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Small.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco Pro Small.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini4.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini4.json index ca4079cd4..3e540532d 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini4.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini4.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,6 +32,6 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": {} -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini7.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini7.json index 16f97ee3d..8c5aded81 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini7.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Deco mini7.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -73,8 +71,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Innovator 16.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Innovator 16.json index 6627d2a6e..d658d9421 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Innovator 16.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Innovator 16.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 03.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 03.json index 195a87b3b..651453e8f 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 03.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 03.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 8 @@ -60,8 +58,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 05 V3.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 05 V3.json index 67b52ba6b..1f31ea480 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 05 V3.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 05 V3.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06.json index a6b43c827..51db1723c 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06C.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06C.json index 24b702ae6..d2193244a 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06C.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star 06C.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430.json index eaddd3fa1..67df01dcc 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 2047, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -49,8 +47,8 @@ ] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S V2.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S V2.json index 668968f45..2cab11236 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S V2.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S.json index 227b2fbdb..d55882617 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G430S.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540 Pro.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540 Pro.json index 8567c4f8c..a96d2cdcc 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540 Pro.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540 Pro.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540.json index 69c3a07b9..127043d39 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G540.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640 V2.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640 V2.json index 62f38db72..27c5fd156 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640 V2.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640 V2.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640.json index 20b58ebd1..a587868bc 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": null, "MouseButtons": null, @@ -32,8 +30,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640S.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640S.json index 4bde381d2..cc683a84b 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640S.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G640S.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 6 @@ -60,8 +58,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960.json index 017df4815..f0c983f67 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S Plus.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S Plus.json index fc51ca5d0..566979e5b 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S Plus.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S Plus.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S.json b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S.json index 0d64df96d..30f9ed863 100644 --- a/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S.json +++ b/OpenTabletDriver.Configurations/Configurations/XP-Pen/Star G960S.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 2 - } + "ButtonCount": 2 }, "AuxiliaryButtons": { "ButtonCount": 4 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Medium.json b/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Medium.json index 979769e77..23e74b317 100644 --- a/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Medium.json +++ b/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Medium.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 3 - } + "ButtonCount": 3 }, "AuxiliaryButtons": { "ButtonCount": 3 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Small.json b/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Small.json index cc203a323..f67c17f05 100644 --- a/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Small.json +++ b/OpenTabletDriver.Configurations/Configurations/XenceLabs/Pen Tablet Small.json @@ -9,9 +9,7 @@ }, "Pen": { "MaxPressure": 8191, - "Buttons": { - "ButtonCount": 3 - } + "ButtonCount": 3 }, "AuxiliaryButtons": { "ButtonCount": 3 @@ -34,8 +32,8 @@ "InitializationStrings": [] } ], - "AuxilaryDeviceIdentifiers": [], + "AuxiliaryDeviceIdentifiers": [], "Attributes": { "libinputoverride": "1" } -} \ No newline at end of file +} diff --git a/OpenTabletDriver.Configurations/DeviceConfigurationProvider.cs b/OpenTabletDriver.Configurations/DeviceConfigurationProvider.cs index 5ce682fb3..0b9e4ab4e 100644 --- a/OpenTabletDriver.Configurations/DeviceConfigurationProvider.cs +++ b/OpenTabletDriver.Configurations/DeviceConfigurationProvider.cs @@ -2,8 +2,8 @@ using System.IO; using System.Linq; using Newtonsoft.Json; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Components; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations { diff --git a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj index 8e204de8d..0214493a1 100644 --- a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj +++ b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj @@ -17,7 +17,7 @@ - + diff --git a/OpenTabletDriver.Configurations/Parsers/10moon/10moonAuxParser.cs b/OpenTabletDriver.Configurations/Parsers/10moon/10moonAuxParser.cs index f3a835453..b2e2f9dbd 100644 --- a/OpenTabletDriver.Configurations/Parsers/10moon/10moonAuxParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/10moon/10moonAuxParser.cs @@ -1,6 +1,4 @@ -using System; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.TenMoon { diff --git a/OpenTabletDriver.Configurations/Parsers/10moon/10moonReportParser.cs b/OpenTabletDriver.Configurations/Parsers/10moon/10moonReportParser.cs index a8b5e4b12..a6bd429d7 100644 --- a/OpenTabletDriver.Configurations/Parsers/10moon/10moonReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/10moon/10moonReportParser.cs @@ -1,5 +1,4 @@ -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.TenMoon { diff --git a/OpenTabletDriver.Configurations/Parsers/10moon/10moonTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/10moon/10moonTabletReport.cs index 253979f5d..fd90ea2db 100644 --- a/OpenTabletDriver.Configurations/Parsers/10moon/10moonTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/10moon/10moonTabletReport.cs @@ -1,9 +1,6 @@ using System; -using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.TenMoon { diff --git a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenAuxReport.cs index de2014192..4640d45b7 100644 --- a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenAuxReport.cs @@ -1,5 +1,5 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Acepen { diff --git a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenReportParser.cs index 9c5ba5268..c19e89212 100644 --- a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Acepen { diff --git a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenTabletReport.cs index 42f025049..019527766 100644 --- a/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Acepen/AcepenTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Acepen { diff --git a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusButtonStripAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusButtonStripAuxReport.cs index 4cfcba838..088f52942 100644 --- a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusButtonStripAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusButtonStripAuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Genius { diff --git a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusMouseReport.cs b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusMouseReport.cs index 2864a5595..0831ed384 100644 --- a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusMouseReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusMouseReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Genius { diff --git a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParser.cs index ce26b7ba4..03bb000fc 100644 --- a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Genius { diff --git a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParserV2.cs b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParserV2.cs index 03cffc2c1..820be2304 100644 --- a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParserV2.cs +++ b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusReportParserV2.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Genius { diff --git a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusTabletReport.cs index 456e87cd2..ff3122538 100644 --- a/OpenTabletDriver.Configurations/Parsers/Genius/GeniusTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Genius/GeniusTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Genius { diff --git a/OpenTabletDriver.Configurations/Parsers/Huion/GianoReport.cs b/OpenTabletDriver.Configurations/Parsers/Huion/GianoReport.cs index 47181f9e3..05f88bfc6 100644 --- a/OpenTabletDriver.Configurations/Parsers/Huion/GianoReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Huion/GianoReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Huion { diff --git a/OpenTabletDriver.Configurations/Parsers/Huion/GianoReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Huion/GianoReportParser.cs index bb18ddadd..fefd39c5c 100644 --- a/OpenTabletDriver.Configurations/Parsers/Huion/GianoReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Huion/GianoReportParser.cs @@ -1,5 +1,5 @@ using OpenTabletDriver.Configurations.Parsers.UCLogic; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Huion { diff --git a/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenReportParser.cs b/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenReportParser.cs index 8ec8ebcf5..6f54923d9 100644 --- a/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.RobotPen { diff --git a/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenTabletReport.cs index b941a9942..64e632516 100644 --- a/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/RobotPen/RobotPenTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.RobotPen { @@ -17,7 +17,7 @@ public RobotPenTabletReport(byte[] report) }; Pressure = Unsafe.ReadUnaligned(ref report[10]); - PenButtons = new bool[] + PenButtons = new[] { report[11].IsBitSet(1) }; diff --git a/OpenTabletDriver.Configurations/Parsers/SkipByteTabletReportParser.cs b/OpenTabletDriver.Configurations/Parsers/SkipByteTabletReportParser.cs index 3d24330a4..b99541807 100644 --- a/OpenTabletDriver.Configurations/Parsers/SkipByteTabletReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/SkipByteTabletReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers { diff --git a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicAuxReport.cs index 24fed01b7..2d6b26aee 100644 --- a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicAuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.UCLogic { diff --git a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicReportParser.cs b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicReportParser.cs index 5e3051bd7..378633c7f 100644 --- a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.UCLogic { diff --git a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicTiltReportParser.cs b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicTiltReportParser.cs index fd48b44f8..3a59340df 100644 --- a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicTiltReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicTiltReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.UCLogic { diff --git a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicV2ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicV2ReportParser.cs index a157a0eb0..e0f0759da 100644 --- a/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicV2ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/UCLogic/UCLogicV2ReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.UCLogic { diff --git a/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkReportParser.cs index 1b31673c8..2accfa990 100644 --- a/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Veikk { diff --git a/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkTabletReport.cs index 70771f5d7..5c77ab393 100644 --- a/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Veikk/VeikkTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Veikk { diff --git a/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadParser.cs b/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadParser.cs index dc8c82598..9726bdec9 100644 --- a/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.ViewSonic { diff --git a/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadReport.cs b/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadReport.cs index 967f196d5..fd19eefeb 100644 --- a/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/ViewSonic/WoodPadReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.ViewSonic { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooAuxReport.cs index 77359203e..ed62d2d35 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooAuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Bamboo { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooMouseReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooMouseReport.cs index e1d6e62ba..e7d2c7c11 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooMouseReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooMouseReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Bamboo { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooReportParser.cs index b10de0e9d..434467ac5 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooReportParser.cs @@ -1,5 +1,5 @@ using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Bamboo { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooTabletReport.cs index 4594506e8..92898e1b7 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Bamboo/BambooTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Bamboo { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosReportParser.cs index 045c5a64d..2c286b3f7 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosTabletReport.cs index 6205631f6..a686c3619 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/IntuosTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/WacomDriverIntuosReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/WacomDriverIntuosReportParser.cs index 2b2699973..eb4e1febe 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/WacomDriverIntuosReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos/WacomDriverIntuosReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3AuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3AuxReport.cs index ae470145b..ba0fbb07c 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3AuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3AuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos3 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3MouseReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3MouseReport.cs index a7b7fb5a6..e4bb872a3 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3MouseReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3MouseReport.cs @@ -1,5 +1,5 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos3 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3ReportParser.cs index d86e798bb..b540f14f5 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/Intuos3ReportParser.cs @@ -1,5 +1,5 @@ using OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos3 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/WacomDriverIntuos3ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/WacomDriverIntuos3ReportParser.cs index 94473a32e..98bbb0960 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/WacomDriverIntuos3ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos3/WacomDriverIntuos3ReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos3 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos4/Intuos4AuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos4/Intuos4AuxReport.cs index c95752fdd..ca5947e9c 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos4/Intuos4AuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Intuos4/Intuos4AuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.Intuos4 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1AuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1AuxReport.cs index 9039e88fc..b1d69807e 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1AuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1AuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ReportParser.cs index 9ce31a7a0..349c2a90f 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ReportParser.cs @@ -1,5 +1,5 @@ using OpenTabletDriver.Configurations.Parsers.Wacom.Intuos4; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1TabletReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1TabletReport.cs index a1bbd2f8e..14bd9974b 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1TabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1TabletReport.cs @@ -1,5 +1,5 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ToolReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ToolReport.cs index 5ab1a0316..6975bda7c 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ToolReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/IntuosV1ToolReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/WacomDriverIntuosV1ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/WacomDriverIntuosV1ReportParser.cs index 1af2e0337..cecf8136e 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/WacomDriverIntuosV1ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV1/WacomDriverIntuosV1ReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV1 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2AuxReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2AuxReport.cs index e20d11b5d..a37ce5dd5 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2AuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2AuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV2 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2Report.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2Report.cs index d3bb3ccf3..c1afbe70c 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2Report.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2Report.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV2 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2ReportParser.cs index c22419749..8383f76f8 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2ReportParser.cs @@ -1,5 +1,5 @@ -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Tablet.Touch; +using OpenTabletDriver.Tablet; +using OpenTabletDriver.Tablet.Touch; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV2 { @@ -17,13 +17,13 @@ public virtual IDeviceReport Parse(byte[] data) { 0x10 => new IntuosV2Report(data), 0x11 => new IntuosV2AuxReport(data), - 0x21 => new IntuosV2TouchReport(data, ref prevTouches), - 0xD2 => new IntuosV2TouchReport(data, ref prevTouches), + 0x21 => new IntuosV2TouchReport(data, ref _prevTouches), + 0xD2 => new IntuosV2TouchReport(data, ref _prevTouches), _ => new DeviceReport(data) }; } } - private TouchPoint[] prevTouches; + private TouchPoint?[] _prevTouches; } } diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2TouchReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2TouchReport.cs index df7afd968..632e55272 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2TouchReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/IntuosV2TouchReport.cs @@ -1,15 +1,15 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet.Touch; +using OpenTabletDriver.Tablet.Touch; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV2 { public struct IntuosV2TouchReport : ITouchReport { - public IntuosV2TouchReport(byte[] report, ref TouchPoint[] prevTouches) + public IntuosV2TouchReport(byte[] report, ref TouchPoint?[] prevTouches) { Raw = report; - Touches = prevTouches ?? new TouchPoint[MAX_POINTS]; + Touches = prevTouches ?? new TouchPoint?[MAX_POINTS]; for (var i = 0; i < 5; i++) { @@ -36,12 +36,12 @@ public IntuosV2TouchReport(byte[] report, ref TouchPoint[] prevTouches) }; } } - prevTouches = (TouchPoint[])Touches.Clone(); + prevTouches = (TouchPoint?[])Touches.Clone(); } - public const int MAX_POINTS = 16; + private const int MAX_POINTS = 16; + public byte[] Raw { set; get; } - public TouchPoint[] Touches { set; get; } - public bool ShouldSerializeTouches() => true; + public TouchPoint?[] Touches { set; get; } } } diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/WacomDriverIntuosV2ReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/WacomDriverIntuosV2ReportParser.cs index fe01a4a61..4ad2f4794 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/WacomDriverIntuosV2ReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/IntuosV2/WacomDriverIntuosV2ReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.Wacom.IntuosV2 { diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/Wacom64bAuxReportParser.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/Wacom64bAuxReportParser.cs index 69edbe943..a36460f97 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/Wacom64bAuxReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/Wacom64bAuxReportParser.cs @@ -1,15 +1,15 @@ -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Tablet.Touch; +using OpenTabletDriver.Tablet; +using OpenTabletDriver.Tablet.Touch; namespace OpenTabletDriver.Configurations.Parsers.Wacom { - public class Wacom64bAuxReportParser : IReportParser + public class Wacom64BAuxReportParser : IReportParser { public virtual IDeviceReport Parse(byte[] data) { - return new WacomTouchReport(data, ref prevTouches); + return new WacomTouchReport(data, ref _prevTouches); } - private TouchPoint[] prevTouches; + private TouchPoint?[] _prevTouches; } } diff --git a/OpenTabletDriver.Configurations/Parsers/Wacom/WacomTouchReport.cs b/OpenTabletDriver.Configurations/Parsers/Wacom/WacomTouchReport.cs index 7bc2cb1ae..c1e202440 100644 --- a/OpenTabletDriver.Configurations/Parsers/Wacom/WacomTouchReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/Wacom/WacomTouchReport.cs @@ -1,21 +1,21 @@ using System; using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Tablet.Touch; +using OpenTabletDriver.Tablet; +using OpenTabletDriver.Tablet.Touch; namespace OpenTabletDriver.Configurations.Parsers.Wacom { public struct WacomTouchReport : ITouchReport, IAuxReport { - public WacomTouchReport(byte[] report, ref TouchPoint[] prevTouches) + public WacomTouchReport(byte[] report, ref TouchPoint?[] prevTouches) { Raw = report; AuxButtons = Array.Empty(); - Touches = prevTouches ?? new TouchPoint[MAX_POINTS]; + Touches = prevTouches ?? new TouchPoint?[MAX_POINTS]; if (report[2] == 0x81) { ApplyTouchMask((ushort)(Raw[3] | (Raw[4] << 8))); - prevTouches = (TouchPoint[])Touches.Clone(); + prevTouches = (TouchPoint?[])Touches.Clone(); return; } @@ -55,9 +55,11 @@ public WacomTouchReport(byte[] report, ref TouchPoint[] prevTouches) }; } } - prevTouches = (TouchPoint[])Touches.Clone(); + prevTouches = (TouchPoint?[])Touches.Clone(); } + private const int MAX_POINTS = 16; + private void ApplyTouchMask(ushort mask) { for (var i = 0; i < MAX_POINTS; i++) @@ -69,10 +71,9 @@ private void ApplyTouchMask(ushort mask) mask >>= 1; } } - public const int MAX_POINTS = 16; + public byte[] Raw { set; get; } public bool[] AuxButtons { set; get; } - public TouchPoint[] Touches { set; get; } - public bool ShouldSerializeTouches() => true; + public TouchPoint?[] Touches { set; get; } } } diff --git a/OpenTabletDriver.Configurations/Parsers/XENX/XENXAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/XENX/XENXAuxReport.cs index a14afa02d..d5433d116 100644 --- a/OpenTabletDriver.Configurations/Parsers/XENX/XENXAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XENX/XENXAuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XENX { diff --git a/OpenTabletDriver.Configurations/Parsers/XENX/XENXReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XENX/XENXReportParser.cs index af7464573..c1eeb14c6 100644 --- a/OpenTabletDriver.Configurations/Parsers/XENX/XENXReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XENX/XENXReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XENX { diff --git a/OpenTabletDriver.Configurations/Parsers/XENX/XENXTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/XENX/XENXTabletReport.cs index 5c9e3906c..d67db6818 100644 --- a/OpenTabletDriver.Configurations/Parsers/XENX/XENXTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XENX/XENXTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XENX { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenAuxReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenAuxReport.cs index 18d4d2516..f572151bf 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenAuxReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenAuxReport.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenDedicatedAuxReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenDedicatedAuxReportParser.cs index 9839573ce..f38d387a1 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenDedicatedAuxReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenDedicatedAuxReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetAuxReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetAuxReportParser.cs index 23480bf0c..0a8d9e92c 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetAuxReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetAuxReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetPressureReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetPressureReportParser.cs index 07e52db0f..b10dac405 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetPressureReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenOffsetPressureReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTabletReport.cs index cc92824cf..964a79f23 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTiltTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTiltTabletReport.cs index 870caa6f3..e67864398 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTiltTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenPressureOffsetTiltTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenReportParser.cs index 0af741899..9f53fd766 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenReportParser.cs @@ -1,4 +1,4 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletOverflowReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletOverflowReport.cs index 2b0386f00..46e0d8b4a 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletOverflowReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletOverflowReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletPressureOffsetOverflowReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletPressureOffsetOverflowReport.cs index ccfbf69b7..513a18168 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletPressureOffsetOverflowReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletPressureOffsetOverflowReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletReport.cs index baa9561b4..ea53fbe5c 100644 --- a/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XP_Pen/XP_PenTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XP_Pen { diff --git a/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsReportParser.cs b/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsReportParser.cs index 505c6cc33..0569a01f7 100644 --- a/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsReportParser.cs +++ b/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsReportParser.cs @@ -1,5 +1,5 @@ using OpenTabletDriver.Configurations.Parsers.XP_Pen; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XenceLabs { diff --git a/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsTabletReport.cs b/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsTabletReport.cs index b1b32370d..57e9e8da9 100644 --- a/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsTabletReport.cs +++ b/OpenTabletDriver.Configurations/Parsers/XenceLabs/XenceLabsTabletReport.cs @@ -1,6 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations.Parsers.XenceLabs { diff --git a/OpenTabletDriver.Configurations/ReportParserProvider.cs b/OpenTabletDriver.Configurations/ReportParserProvider.cs index 7e87c65de..23a7eb967 100644 --- a/OpenTabletDriver.Configurations/ReportParserProvider.cs +++ b/OpenTabletDriver.Configurations/ReportParserProvider.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Components; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Configurations { diff --git a/OpenTabletDriver.Console/ApplicationBuilder.cs b/OpenTabletDriver.Console/ApplicationBuilder.cs new file mode 100644 index 000000000..b790c1d96 --- /dev/null +++ b/OpenTabletDriver.Console/ApplicationBuilder.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop; + +namespace OpenTabletDriver.Console +{ + public class ApplicationBuilder : HostBuilder + { + public ApplicationBuilder() : base(new ConsoleServiceCollection()) + { + } + + public override IApplicationLifetime Build(out IServiceProvider serviceProvider) + { + serviceProvider = BuildServiceProvider(); + return serviceProvider.GetRequiredService(); + } + } +} diff --git a/OpenTabletDriver.Console/ApplicationLifetime.cs b/OpenTabletDriver.Console/ApplicationLifetime.cs new file mode 100644 index 000000000..26d7481bf --- /dev/null +++ b/OpenTabletDriver.Console/ApplicationLifetime.cs @@ -0,0 +1,31 @@ +using System; +using System.CommandLine; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.RPC; + +namespace OpenTabletDriver.Console +{ + public class ApplicationLifetime : IApplicationLifetime + { + private readonly IServiceProvider _serviceProvider; + private readonly RpcClient _rpcClient; + + public ApplicationLifetime(IServiceProvider serviceProvider, RpcClient rpcClient) + { + _serviceProvider = serviceProvider; + _rpcClient = rpcClient; + } + + public async Task Run(string[] args) + { + await _rpcClient.Connect(); + + var commands = ActivatorUtilities.CreateInstance(_serviceProvider); + var root = commands.Build("otd"); + await root.InvokeAsync(args); + } + } +} diff --git a/OpenTabletDriver.Console/Attributes/CommandAttribute.cs b/OpenTabletDriver.Console/Attributes/CommandAttribute.cs new file mode 100644 index 000000000..996e1edd7 --- /dev/null +++ b/OpenTabletDriver.Console/Attributes/CommandAttribute.cs @@ -0,0 +1,23 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Console.Attributes +{ + [AttributeUsage(AttributeTargets.Method), MeansImplicitUse] + public class CommandAttribute : Attribute + { + public CommandAttribute(string command) + { + Command = command; + } + + public CommandAttribute(string command, string description) + : this(command) + { + Description = description; + } + + public string Command { get; } + public string? Description { get; } + } +} diff --git a/OpenTabletDriver.Console/Attributes/Extensions.cs b/OpenTabletDriver.Console/Attributes/Extensions.cs new file mode 100644 index 000000000..b1a338bbb --- /dev/null +++ b/OpenTabletDriver.Console/Attributes/Extensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace OpenTabletDriver.Console.Attributes +{ + public static class Extensions + { + public static T? GetCustomAttribute(this ICustomAttributeProvider provider) where T : Attribute + { + var attrs = provider.GetCustomAttributes(true); + var attr = attrs.FirstOrDefault(a => a is T); + return (T?)attr; + } + } +} diff --git a/OpenTabletDriver.Console/CommandCollection.cs b/OpenTabletDriver.Console/CommandCollection.cs new file mode 100644 index 000000000..44bb5dc29 --- /dev/null +++ b/OpenTabletDriver.Console/CommandCollection.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Linq; +using System.Reflection; +using OpenTabletDriver.Console.Attributes; + +namespace OpenTabletDriver.Console +{ + public class CommandCollection : Collection + { + private CommandCollection(object source) + { + var module = source.GetType(); + var commandMethods = from method in module.GetMethods() + where method.GetCustomAttribute() != null + select method; + + foreach (var method in commandMethods) + { + var command = BuildCommand(method, source); + Add(command); + } + } + + public static CommandCollection Build(object source) => new CommandCollection(source); + + private static Command BuildCommand(MethodInfo method, object source) + { + var name = method.GetCustomAttribute()!.Command; + var description = GetDescription(method); + + var arguments = GetArguments(method); + var options = GetOptions(method); + + var command = new Command(name, description) + { + Handler = method.IsStatic ? CommandHandler.Create(method) : CommandHandler.Create(method, source) + }; + + foreach (var argument in arguments) + command.AddArgument(argument); + + foreach (var option in options) + command.AddOption(option); + + return command; + } + + private static IEnumerable GetArguments(MethodInfo method) + { + return from parameter in method.GetParameters() + where parameter.IsIn + where !parameter.IsOptional + select BuildArgument(parameter); + } + + private static IEnumerable diff --git a/OpenTabletDriver.Console/Program.Commands.cs b/OpenTabletDriver.Console/Program.Commands.cs deleted file mode 100644 index a972082ea..000000000 --- a/OpenTabletDriver.Console/Program.Commands.cs +++ /dev/null @@ -1,405 +0,0 @@ -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Security.Cryptography; -using System.Threading.Tasks; -using Newtonsoft.Json; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Diagnostics; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Tablet; -using static System.Console; - -namespace OpenTabletDriver.Console -{ - partial class Program - { - #region Update - - private static async Task HasUpdate() - { - var hasUpdate = await Driver.Instance.HasUpdate(); - await Out.WriteLineAsync(hasUpdate.ToString().ToLowerInvariant()); - } - - private static async Task InstallUpdate() - { - if (await Driver.Instance.HasUpdate()) - { - await Driver.Instance.InstallUpdate(); - } - } - - #endregion - - #region I/O - - private static async Task LoadSettings(FileInfo file) - { - var settings = Settings.Deserialize(file); - await ApplySettings(settings); - } - - private static async Task SaveSettings(FileInfo file) - { - var settings = await GetSettings(); - settings.Serialize(file); - } - - private static async Task ApplyPreset(string name) - { - var presetDir = new DirectoryInfo(AppInfo.Current.PresetDirectory); - - if (!presetDir.Exists) - presetDir.Create(); - AppInfo.PresetManager.Refresh(); - - var preset = AppInfo.PresetManager.FindPreset(name); - await ApplySettings(preset.GetSettings()); - } - - private static async Task SavePreset(string name) - { - var presetDir = new DirectoryInfo(AppInfo.Current.PresetDirectory); - - if (!presetDir.Exists) - presetDir.Create(); - - var settings = await GetSettings(); - var file = new FileInfo(Path.Combine(presetDir.FullName, name + ".json")); - - settings.Serialize(file); - } - - #endregion - - #region Modify Settings - - private static async Task SetDisplayArea(string tablet, float width, float height, float x, float y) - { - await ModifyProfile(tablet, p => - { - p.AbsoluteModeSettings.Display.Width = width; - p.AbsoluteModeSettings.Display.Height = height; - p.AbsoluteModeSettings.Display.X = x; - p.AbsoluteModeSettings.Display.Y = y; - }); - } - - private static async Task SetTabletArea(string tablet, float width, float height, float x, float y, float rotation = 0) - { - await ModifyProfile(tablet, p => - { - p.AbsoluteModeSettings.Tablet.Width = width; - p.AbsoluteModeSettings.Tablet.Height = height; - p.AbsoluteModeSettings.Tablet.X = x; - p.AbsoluteModeSettings.Tablet.Y = y; - p.AbsoluteModeSettings.Tablet.Rotation = rotation; - }); - } - - private static async Task SetSensitivity(string tablet, float xSens, float ySens, float rotation = 0) - { - await ModifyProfile(tablet, p => - { - p.RelativeModeSettings.XSensitivity = xSens; - p.RelativeModeSettings.YSensitivity = ySens; - p.RelativeModeSettings.RelativeRotation = rotation; - }); - } - - private static async Task SetResetTime(string tablet, int ms) - { - await ModifyProfile(tablet, p => p.RelativeModeSettings.ResetTime = TimeSpan.FromMilliseconds(ms)); - } - - private static async Task SetTipBinding(string tablet, string name, float threshold) - { - await ModifyProfile(tablet, p => - { - var tipBinding = AppInfo.PluginManager.ConstructObject(name); - - p.BindingSettings.TipButton = new PluginSettingStore(tipBinding); - p.BindingSettings.TipActivationThreshold = threshold; - }); - } - - private static async Task SetPenBinding(string tablet, string name, int index) - { - await ModifyProfile(tablet, p => - { - var binding = AppInfo.PluginManager.ConstructObject(name); - - p.BindingSettings.PenButtons[index] = new PluginSettingStore(binding); - }); - } - - private static async Task SetAuxBinding(string tablet, string name, int index) - { - await ModifyProfile(tablet, p => - { - var binding = AppInfo.PluginManager.ConstructObject(name); - - p.BindingSettings.AuxButtons[index] = new PluginSettingStore(binding); - }); - } - - private static async Task SetEnableClipping(string tablet, bool isEnabled) - { - await ModifyProfile(tablet, p => p.AbsoluteModeSettings.EnableClipping = isEnabled); - } - - private static async Task SetEnableAreaLimiting(string tablet, bool isEnabled) - { - await ModifyProfile(tablet, p => p.AbsoluteModeSettings.EnableAreaLimiting = isEnabled); - } - - private static async Task SetLockAspectRatio(string tablet, bool isEnabled) - { - await ModifyProfile(tablet, p => p.AbsoluteModeSettings.LockAspectRatio = isEnabled); - } - - private static async Task SetOutputMode(string tablet, string mode) - { - await ModifyProfile(tablet, p => p.OutputMode = new PluginSettingStore(mode)); - } - - private static async Task SetFilters(string tablet, params string[] filters) - { - await ModifyProfile(tablet, s => - { - var collection = new PluginSettingStoreCollection(); - foreach (var path in filters) - collection.Add(new PluginSettingStore(path)); - - s.Filters = collection; - }); - } - - private static async Task SetTools(params string[] tools) - { - await ModifySettings(s => - { - var collection = new PluginSettingStoreCollection(); - foreach (var path in tools) - collection.Add(new PluginSettingStore(path)); - - s.Tools = collection; - }); - } - - #endregion - - #region Request Settings - - private static async Task GetCurrentLog() - { - var log = await Driver.Instance.GetCurrentLog(); - foreach (var message in log) - await Out.WriteLineAsync(Log.GetStringFormat(message)); - } - - private static async Task GetAllSettings() - { - var settings = await GetSettings(); - - await Out.WriteLineAsync("--- Generic Settings ---"); - await GetTools(); - - foreach (var profile in settings.Profiles) - { - await Out.WriteLineAsync(); - await Out.WriteLineAsync($"--- Profile for '{profile.Tablet}' ---"); - await GetOutputMode(profile.Tablet); - await GetAreas(profile.Tablet); - await GetSensitivity(profile.Tablet); - await GetBindings(profile.Tablet); - await GetMiscSettings(profile.Tablet); - await GetFilters(profile.Tablet); - } - } - - private static async Task GetAreas(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync($"Display area: {profile.AbsoluteModeSettings.Display.Area}"); - await Out.WriteLineAsync($"Tablet area: {profile.AbsoluteModeSettings.Tablet.Area}"); - } - - private static async Task GetSensitivity(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync($"Horizontal Sensitivity: {profile.RelativeModeSettings.XSensitivity}px/mm"); - await Out.WriteLineAsync($"Vertical Sensitivity: {profile.RelativeModeSettings.YSensitivity}px/mm"); - await Out.WriteLineAsync($"Relative mode rotation: {profile.RelativeModeSettings.RelativeRotation}°"); - await Out.WriteLineAsync($"Reset time: {profile.RelativeModeSettings.ResetTime}"); - } - - private static async Task GetBindings(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync($"Tip Binding: {profile.BindingSettings.TipButton.Format() ?? "None"}@{profile.BindingSettings.TipActivationThreshold}%"); - await Out.WriteLineAsync($"Pen Bindings: {string.Join(", ", profile.BindingSettings.PenButtons.Format())}"); - await Out.WriteLineAsync($"Express Key Bindings: {string.Join(", ", profile.BindingSettings.AuxButtons.Format())}"); - } - - private static async Task GetMiscSettings(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync($"Area clipping: {profile.AbsoluteModeSettings.EnableClipping}"); - await Out.WriteLineAsync($"Tablet area limiting: {profile.AbsoluteModeSettings.EnableAreaLimiting}"); - await Out.WriteLineAsync($"Lock aspect ratio: {profile.AbsoluteModeSettings.LockAspectRatio}"); - } - - private static async Task GetOutputMode(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync("Output Mode: " + profile.OutputMode.Format()); - } - - private static async Task GetFilters(string tablet) - { - var profile = await GetProfile(tablet); - await Out.WriteLineAsync("Filters: " + string.Join(", ", profile.Filters.Format())); - } - - private static async Task GetTools() - { - var settings = await GetSettings(); - await Out.WriteLineAsync("Tools: " + string.Join(", ", settings.Tools.Format())); - } - - #endregion - - #region Actions - - private static async Task Detect() - { - await Driver.Instance.DetectTablets(); - } - - #endregion - - #region Debugging - - private static async Task GetString(int vid, int pid, int index) - { - var str = await Driver.Instance.RequestDeviceString(vid, pid, index); - await Out.WriteLineAsync(str); - } - - #endregion - - #region List Types - - private static async Task ListOutputModes() - { - await ListTypes(); - } - - private static async Task ListFilters() - { - // Using the predicate stops output mode types from being listed. - await ListTypes>(t => !t.IsAssignableTo(typeof(IOutputMode))); - } - - private static async Task ListTools() - { - await ListTypes(); - } - - private static async Task ListBindings() - { - await ListTypes(); - } - - #endregion - - #region Scripting - - private static async Task GetAllSettingsJson() - { - var settings = await GetSettings(); - await Out.WriteLineAsync(JsonConvert.SerializeObject(settings, Formatting.Indented)); - } - - private static async Task EditSettings() - { - string editor = Environment.GetEnvironmentVariable("EDITOR"); - if (!string.IsNullOrWhiteSpace(editor)) - { - var settings = await GetSettings(); - var tempDir = Environment.GetEnvironmentVariable("TEMP") ?? AppInfo.Current.TemporaryDirectory; - var tempFile = $"OpenTabletDriver-{Guid.NewGuid()}.json"; - var sha256 = SHA256.Create(); - - var path = Path.Join(tempDir, tempFile); - var cmd = $"{editor} {path}"; - var tokens = cmd.Split(' '); - - var executable = tokens[0]; - var args = string.Join(' ', tokens[1..tokens.Length]); - - if (!Directory.Exists(tempDir)) - Directory.CreateDirectory(tempDir); - - using (var fs = File.Create(path)) - Serialization.Serialize(fs, settings); - - var oldHash = GetSHA256(path); - - using (var proc = Process.Start(executable, args)) - await proc.WaitForExitAsync(); - - var newHash = GetSHA256(path); - - using (var fs = File.OpenRead(path)) - settings = Serialization.Deserialize(fs); - - if (oldHash.Equals(newHash)) - { - await Out.WriteLineAsync("The file was left unchanged. Settings will not be applied."); - } - else - { - await ApplySettings(settings); - await Out.WriteLineAsync("Settings were successfully applied."); - } - - File.Delete(path); - } - else - { - await Out.WriteLineAsync("The EDITOR environment variable is not set."); - } - } - - private static async Task GetDiagnostics() - { - try - { - var log = await Driver.Instance.GetCurrentLog(); - var diagnostics = new DiagnosticInfo(log, await Driver.Instance.GetDevices()); - await Out.WriteLineAsync(diagnostics.ToString()); - } - catch (Exception ex) - { - Log.Exception(ex); - } - } - - private static async Task STDIO() - { - while (await System.Console.In.ReadLineAsync() is string cmd) - await Root.InvokeAsync(cmd); - } - - #endregion - } -} diff --git a/OpenTabletDriver.Console/Program.IPC.cs b/OpenTabletDriver.Console/Program.IPC.cs deleted file mode 100644 index a7c6914a9..000000000 --- a/OpenTabletDriver.Console/Program.IPC.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using OpenTabletDriver.Desktop.Contracts; -using OpenTabletDriver.Desktop.RPC; - -namespace OpenTabletDriver.Console -{ - partial class Program - { - public static readonly RpcClient Driver = new RpcClient("OpenTabletDriver.Daemon"); - } -} diff --git a/OpenTabletDriver.Console/Program.Misc.cs b/OpenTabletDriver.Console/Program.Misc.cs deleted file mode 100644 index f5ecaf8fc..000000000 --- a/OpenTabletDriver.Console/Program.Misc.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Threading.Tasks; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Profiles; -using static System.Console; - -namespace OpenTabletDriver.Console -{ - partial class Program - { - static SHA256 sha256 = SHA256.Create(); - - static async Task GetSettings() - { - return await Driver.Instance.GetSettings(); - } - - static async Task ApplySettings(Settings settings) - { - await Driver.Instance.SetSettings(settings); - } - - static async Task ModifySettings(Action func) - { - var settings = await GetSettings(); - func.Invoke(settings); - await ApplySettings(settings); - } - - static async Task ModifyProfile(string profileName, Action func) - { - await ModifySettings(async s => - { - var profile = await GetProfile(profileName, s); - if (profile != null) - { - func.Invoke(profile); - } - else - { - throw new ArgumentException("No profile exists for the target tablet."); - } - }); - } - - static async Task GetProfile(string profileName, Settings settings = null) - { - const StringComparison comparer = StringComparison.InvariantCultureIgnoreCase; - settings ??= await GetSettings(); - - var profile = settings.Profiles.FirstOrDefault(p => p.Tablet.Equals(profileName, comparer)); - if (profile == null) - { - var tablets = await Driver.Instance.GetTablets(); - var tablet = tablets.FirstOrDefault(t => t.Properties.Name.Equals(profileName, comparer)); - if (tablet != null) - profile = Profile.GetDefaults(tablet); - } - - return profile; - } - - static async Task ListTypes(Func predicate = null) - { - foreach (var type in AppInfo.PluginManager.GetChildTypes()) - { - if (predicate?.Invoke(type) ?? true) - { - var name = AppInfo.PluginManager.GetFriendlyName(type.FullName); - var output = string.IsNullOrWhiteSpace(name) ? type.FullName : $"{type.FullName} [{name}]"; - await Out.WriteLineAsync(output); - } - } - } - - static string GetSHA256(string path) - { - var data = File.ReadAllBytes(path); - var hash = sha256.ComputeHash(data); - return string.Join(null, hash.Select(b => b.ToString("X"))); - } - } -} diff --git a/OpenTabletDriver.Console/Program.cs b/OpenTabletDriver.Console/Program.cs index 495cf0d30..767109e72 100644 --- a/OpenTabletDriver.Console/Program.cs +++ b/OpenTabletDriver.Console/Program.cs @@ -1,110 +1,14 @@ -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.IO; using System.Threading.Tasks; namespace OpenTabletDriver.Console { - using static CommandTools; - - partial class Program + internal class Program { public static async Task Main(string[] args) { - await Driver.Connect(); - await Root.InvokeAsync(args); - } - - private static readonly Lazy root = new Lazy(GenerateRoot); - public static RootCommand Root => root.Value; - - private static RootCommand GenerateRoot() - { - var root = new RootCommand("OpenTabletDriver Console Client") - { - Name = "otd" - }; - - root.AddRange(IOCommands); - root.AddRange(ActionCommands); - root.AddRange(DebugCommands); - root.AddRange(ModifyCommands); - root.AddRange(RequestCommands); - root.AddRange(UpdateCommands); - root.AddRange(ListCommands); - root.AddRange(ScriptingCommands); - - return root; + var builder = new ApplicationBuilder(); + var app = builder.Build(); + await app.Run(args); } - - private static readonly IEnumerable IOCommands = new Command[] - { - CreateCommand(LoadSettings, "Load settings from a file", "load"), - CreateCommand(SaveSettings, "Save settings to a file", "save"), - CreateCommand(ApplyPreset, "Apply a preset from the Presets directory", "preset"), - CreateCommand(SavePreset, "Save the current settings to the Presets directory") - }; - - private static readonly IEnumerable ActionCommands = new Command[] - { - CreateCommand(Detect, "Detects tablets") - }; - - private static readonly IEnumerable DebugCommands = new Command[] - { - CreateCommand(GetString, "Requests a device string") - }; - - private static readonly IEnumerable ModifyCommands = new Command[] - { - CreateCommand(SetOutputMode, "Sets the output mode"), - CreateCommand(SetFilters, "Sets the filters applied to the current output mode"), - CreateCommand(SetTools, "Sets the active tools"), - CreateCommand(SetDisplayArea, "Sets the display area"), - CreateCommand(SetTabletArea, "Sets the tablet area"), - CreateCommand(SetSensitivity, "Sets the relative sensitivity"), - CreateCommand(SetTipBinding, "Sets the current tip binding"), - CreateCommand(SetPenBinding, "Sets the current pen button bindings"), - CreateCommand(SetAuxBinding, "Sets the current express key bindings"), - CreateCommand(SetResetTime, "Sets the reset time in milliseconds"), - CreateCommand(SetEnableClipping, "Sets whether inputs should be limited to the specified areas"), - CreateCommand(SetEnableAreaLimiting, "Sets whether inputs outside of the tablet area should be ignored"), - CreateCommand(SetLockAspectRatio, "Sets whether to lock tablet width/height to display width/height ratio") - }; - - private static readonly IEnumerable RequestCommands = new Command[] - { - CreateCommand(GetCurrentLog, "Gets the current log", "log"), - CreateCommand(GetAllSettings, "Gets all current settings"), - CreateCommand(GetOutputMode, "Gets the current output mode"), - CreateCommand(GetAreas, "Gets the current display and tablet area"), - CreateCommand(GetSensitivity, "Gets the current relative sensitivity"), - CreateCommand(GetBindings, "Gets all current bindings"), - CreateCommand(GetMiscSettings, "Gets other uncategorized settings"), - CreateCommand(GetFilters, "Gets the currently enabled filters"), - CreateCommand(GetTools, "Gets the currently enabled tools") - }; - - private static readonly IEnumerable UpdateCommands = new Command[] - { - CreateCommand(HasUpdate, "Check for any updates"), - CreateCommand(InstallUpdate, "Install update") - }; - - private static readonly IEnumerable ListCommands = new Command[] - { - CreateCommand(ListOutputModes, "Lists all available output modes"), - CreateCommand(ListFilters, "Lists all available filters"), - CreateCommand(ListTools, "Lists all available tools"), - CreateCommand(ListBindings, "Lists all available binding types") - }; - - private static readonly IEnumerable ScriptingCommands = new Command[] - { - CreateCommand(GetDiagnostics, "Gets diagnostic information"), - CreateCommand(STDIO, "Open with standard input and output", "stdio"), - CreateCommand(EditSettings, "Opens the settings file with the editor defined in the EDITOR environment variable.", "edit") - }; } } diff --git a/OpenTabletDriver.Console/ProgramCommands.cs b/OpenTabletDriver.Console/ProgramCommands.cs new file mode 100644 index 000000000..f7d2c252b --- /dev/null +++ b/OpenTabletDriver.Console/ProgramCommands.cs @@ -0,0 +1,436 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading.Tasks; +using OpenTabletDriver.Console.Attributes; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Output; +using OpenTabletDriver.Tablet; +using static System.Console; + +namespace OpenTabletDriver.Console +{ + public class ProgramCommands : CommandModule + { + private readonly IServiceProvider _serviceProvider; + private readonly IDriverDaemon _driverDaemon; + + public ProgramCommands(IServiceProvider serviceProvider, IDriverDaemon driverDaemon) + { + _serviceProvider = serviceProvider; + _driverDaemon = driverDaemon; + } + + [Command("load", "Load settings from a file")] + public async Task LoadSettings(FileInfo file) + { + var settings = Settings.Deserialize(file)!; + await ApplySettings(settings); + } + + [Command("save", "Save settings to a file")] + public async Task SaveSettings(FileInfo file) + { + var settings = await GetSettings(); + settings.Serialize(file); + } + + [Command("apply-preset", "Apply a preset from the presets directory")] + public async Task ApplyPreset(string name) + { + await _driverDaemon.ApplyPreset(name); + } + + [Command("save-preset", "Save the current settings to the presets directory")] + public async Task SavePreset(string name) + { + var settings = await GetSettings(); + await _driverDaemon.SavePreset(name, settings); + } + + [Command("set-display-area", "Sets the display area")] + public async Task SetDisplayArea(string tablet, float width, float height, float x, float y) + { + await ModifyProfile(tablet, p => + { + p.OutputMode["Output"].SetValue(new Area + { + Width = width, + Height = height, + XPosition = x, + YPosition = y + }); + }); + } + + [Command("set-tablet-area", "Sets the tablet area")] + public async Task SetTabletArea(string tablet, float width, float height, float x, float y, float rotation = 0) + { + await ModifyProfile(tablet, p => + { + p.OutputMode["Input"].SetValue(new AngledArea + { + Width = width, + Height = height, + XPosition = x, + YPosition = y, + Rotation = rotation + }); + }); + } + + [Command("set-relative-sensitivity", "Sets the relative sensitivity")] + public async Task SetSensitivity(string tablet, float xSens, float ySens, float rotation = 0) + { + await ModifyProfile(tablet, p => + { + p.OutputMode["XSensitivity"].Value = xSens; + p.OutputMode["YSensitivity"].Value = ySens; + p.OutputMode["Rotation"].Value = rotation; + }); + } + + [Command("set-relative-reset-delay", "Sets the relative mode reset delay")] + private async Task SetResetDelay(string tablet, int ms) + { + await ModifyProfile(tablet, p => p.OutputMode["ResetDelay"].Value = TimeSpan.FromMilliseconds(ms)); + } + + [Command("set-tip-binding", "Sets the tip binding")] + public async Task SetTipBinding(string tablet, string name, float threshold) + { + await ModifyProfile(tablet, async p => + { + var binding = await _driverDaemon.GetDefaults(name); + p.BindingSettings.TipButton = binding; + p.BindingSettings.TipActivationThreshold = threshold; + }); + } + + [Command("set-pen-binding", "Sets the pen button bindings")] + public async Task SetPenBinding(string tablet, string name, int index) + { + await ModifyProfile(tablet, async p => + { + var binding = await _driverDaemon.GetDefaults(name); + + p.BindingSettings.PenButtons[index] = binding; + }); + } + + [Command("set-aux-binding", "Sets the auxiliary button bindings")] + public async Task SetAuxBinding(string tablet, string name, int index) + { + await ModifyProfile(tablet, async p => + { + var binding = await _driverDaemon.GetDefaults(name); + + p.BindingSettings.AuxButtons[index] = binding; + }); + } + + [Command("set-output-mode", "Sets the active output mode with its defaults")] + public async Task SetOutputMode(string tablet, string mode) + { + await ModifyProfile(tablet, async p => + { + var outputMode = await _driverDaemon.GetDefaults(mode); + p.OutputMode = outputMode; + }); + } + + [Command("set-filters", "Sets the filters for the active output mode")] + public async Task SetFilters(string tablet, params string[] filters) + { + await ModifyProfile(tablet, async s => + { + var collection = new PluginSettingsCollection(); + foreach (var path in filters) + { + var settings = await _driverDaemon.GetDefaults(path); + collection.Add(settings); + } + + s.Filters = collection; + }); + } + + [Command("set-tools", "Sets the tools to be enabled")] + public async Task SetTools(params string[] tools) + { + await ModifySettings(async s => + { + var collection = new PluginSettingsCollection(); + foreach (var path in tools) + { + var settings = await _driverDaemon.GetDefaults(path); + collection.Add(settings); + } + + s.Tools = collection; + }); + } + + [Command("log", "Gets the current log")] + public async Task GetCurrentLog() + { + var log = await _driverDaemon.GetCurrentLog(); + foreach (var message in log) + await Out.WriteLineAsync(Log.GetStringFormat(message)); + } + + [Command("get-all-settings", "Gets all settings")] + public async Task GetAllSettings() + { + var settings = await GetSettings(); + + await Out.WriteLineAsync("--- Generic Settings ---"); + await GetTools(); + + foreach (var profile in settings.Profiles) + { + await Out.WriteLineAsync(); + await Out.WriteLineAsync($"--- Profile for '{profile.Tablet}' ---"); + await GetOutputMode(profile.Tablet); + await GetAreas(profile.Tablet); + await GetSensitivity(profile.Tablet); + await GetBindings(profile.Tablet); + await GetFilters(profile.Tablet); + } + } + + [Command("get-area-settings", "Gets area settings")] + public async Task GetAreas(string tablet) + { + var settings = await GetSettings(); + var profile = GetProfile(settings, tablet); + await Out.WriteLineAsync($"Display area: {profile.OutputMode["Output"]}"); + await Out.WriteLineAsync($"Tablet area: {profile.OutputMode["Input"]}"); + } + + [Command("get-sensitivity", "Gets relative mode sensitivity settings")] + public async Task GetSensitivity(string tablet) + { + var settings = await GetSettings(); + var profile = GetProfile(settings, tablet); + await Out.WriteLineAsync($"Sensitivity: {profile.OutputMode["Sensitivity"]}px/mm"); + await Out.WriteLineAsync($"Rotation: {profile.OutputMode["Rotation"]}°"); + await Out.WriteLineAsync($"Reset time: {profile.OutputMode["ResetDelay"]}"); + } + + [Command("get-bindings", "Gets binding settings")] + public async Task GetBindings(string tablet) + { + var settings = await GetSettings(); + var profile = GetProfile(settings, tablet); + await Out.WriteLineAsync($"Tip Binding: {profile.BindingSettings.TipButton.Format()}@{profile.BindingSettings.TipActivationThreshold}%"); + await Out.WriteLineAsync($"Pen Bindings: {profile.BindingSettings.PenButtons.Format()}"); + await Out.WriteLineAsync($"Express Key Bindings: {profile.BindingSettings.AuxButtons.Format()}"); + } + + [Command("get-output-mode", "Gets the output mode")] + public async Task GetOutputMode(string tablet) + { + var settings = await GetSettings(); + var profile = GetProfile(settings, tablet); + await Out.WriteLineAsync("Output Mode: " + profile.OutputMode.Format()); + } + + [Command("get-filters", "Gets all filters applied to the output mode")] + public async Task GetFilters(string tablet) + { + var settings = await GetSettings(); + var profile = GetProfile(settings, tablet); + await Out.WriteLineAsync("Filters: " + profile.Filters.Format()); + } + + [Command("get-tools", "Gets all tools")] + public async Task GetTools() + { + var settings = await GetSettings(); + await Out.WriteLineAsync("Tools: " + settings.Tools.Format()); + } + + [Command("get-string", "Requests a device string")] + public async Task GetString(int vid, int pid, int index) + { + var str = await _driverDaemon.RequestDeviceString(vid, pid, index); + await Out.WriteLineAsync(str); + } + + [Command("detect", "Scan all devices for supported tablets")] + public async Task Detect() + { + await _driverDaemon.DetectTablets(); + } + + [Command("list-presets", "Lists all saved presets")] + public async Task ListPresets() + { + var presets = await _driverDaemon.GetPresets(); + var output = string.Join(", ", presets); + await Out.WriteLineAsync(output); + } + + [Command("list-tablets", "Lists all connected tablets")] + public async Task ListTablets() + { + var tablets = await _driverDaemon.GetTablets(); + var tabletNames = tablets.Select(t => t.Name); + var output = string.Join(", ", tabletNames); + await Out.WriteLineAsync(output); + } + + [Command("list-output-modes", "Lists all output modes")] + public async Task ListOutputModes() => await ListTypes(); + + [Command("list-filters", "Lists all supported filters")] + public async Task ListFilters() => await ListTypes>(); + + [Command("list-bindings", "Lists all supported bindings")] + public async Task ListBindings() => await ListTypes(); + + [Command("list-tools", "Lists all supported tools")] + public async Task ListTools() => await ListTypes(); + + private async Task ListTypes() + { + foreach (var type in await _driverDaemon.GetMatchingTypes(typeof(T).GetPath())) + { + var output = type.FriendlyName != null ? $"{type.Path} [{type.FriendlyName}]" : type.Path; + await Out.WriteLineAsync(output); + } + } + + [Command("edit-settings", "Edit settings with your editor")] + public async Task EditSettings(string? editor = null) + { + editor ??= Environment.GetEnvironmentVariable("EDITOR"); + + if (string.IsNullOrWhiteSpace(editor)) + { + await Out.WriteLineAsync("No editor is defined in the EDITOR environment variable."); + return; + } + + var settings = await GetSettings(); + var appInfo = await _driverDaemon.GetApplicationInfo(); + + var tempDir = Environment.GetEnvironmentVariable("TEMP") ?? appInfo.TemporaryDirectory; + var tempFile = $"OpenTabletDriver-{Guid.NewGuid()}.json"; + using var sha256 = SHA256.Create(); + + var path = Path.Join(tempDir, tempFile); + var cmd = $"{editor} {path}"; + var tokens = cmd.Split(' '); + + var executable = tokens[0]; + var args = string.Join(' ', tokens[1..tokens.Length]); + + if (!Directory.Exists(tempDir)) + Directory.CreateDirectory(tempDir); + + await using (var fs = File.Create(path)) + Serialization.Serialize(fs, settings); + + var oldHash = sha256.ComputeFileHash(path); + + using (var proc = Process.Start(executable, args)) + await proc.WaitForExitAsync(); + + var newHash = sha256.ComputeFileHash(path); + + if (oldHash.Equals(newHash)) + { + await Out.WriteLineAsync("The file was left unchanged. Settings will not be applied."); + } + else + { + await using (var fs = File.OpenRead(path)) + { + var newSettings = Serialization.Deserialize(fs); + await ApplySettings(newSettings); + await Out.WriteLineAsync("Settings were successfully applied."); + } + } + + if (File.Exists(path)) + File.Delete(path); + } + + [Command("diagnostics", "Gets OpenTabletDriver diagnostics")] + public async Task GetDiagnostics() + { + try + { + var diagnostics = await _driverDaemon.GetDiagnostics(); + await Out.WriteLineAsync(diagnostics.ToString()); + } + catch (Exception ex) + { + Log.Exception(ex); + } + } + + [Command("update", "Install OpenTabletDriver update if available")] + public async Task InstallUpdate() + { + if (await _driverDaemon.HasUpdate()) + { + await _driverDaemon.InstallUpdate(); + } + } + + private async Task GetSettings() + { + return await _driverDaemon.GetSettings(); + } + + private Profile GetProfile(Settings settings, string tablet) + { + return settings.Profiles.First(p => p.Tablet.Equals(tablet, StringComparison.InvariantCultureIgnoreCase)); + } + + private async Task ApplySettings(Settings settings) + { + await _driverDaemon.ApplySettings(settings); + } + + private async Task ModifySettings(Action action) + { + var settings = await GetSettings(); + action.Invoke(settings); + await ApplySettings(settings); + } + + private async Task ModifySettings(Func func) + { + var settings = await GetSettings(); + await func.Invoke(settings); + await ApplySettings(settings); + } + + private async Task ModifyProfile(string tablet, Action action) + { + await ModifySettings(s => + { + var profile = GetProfile(s, tablet); + action.Invoke(profile); + }); + } + + private async Task ModifyProfile(string tablet, Func func) + { + await ModifySettings(async s => + { + var profile = GetProfile(s, tablet); + await func.Invoke(profile); + }); + } + } +} diff --git a/OpenTabletDriver.Daemon/DriverDaemon.cs b/OpenTabletDriver.Daemon/DriverDaemon.cs index 7235f4b91..784af2fe6 100644 --- a/OpenTabletDriver.Daemon/DriverDaemon.cs +++ b/OpenTabletDriver.Daemon/DriverDaemon.cs @@ -4,25 +4,26 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Components; using OpenTabletDriver.Desktop; using OpenTabletDriver.Desktop.Binding; +using OpenTabletDriver.Desktop.Components; using OpenTabletDriver.Desktop.Contracts; -using OpenTabletDriver.Desktop.Interop; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop.AppInfo; using OpenTabletDriver.Desktop.Migration; using OpenTabletDriver.Desktop.Profiles; using OpenTabletDriver.Desktop.Reflection; using OpenTabletDriver.Desktop.Reflection.Metadata; using OpenTabletDriver.Desktop.RPC; using OpenTabletDriver.Desktop.Updater; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Logging; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Logging; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Display; using OpenTabletDriver.SystemDrivers; +using OpenTabletDriver.Tablet; #nullable enable @@ -30,10 +31,41 @@ namespace OpenTabletDriver.Daemon { public class DriverDaemon : IDriverDaemon { - public DriverDaemon(Driver driver) + private readonly IServiceProvider _serviceProvider; + private readonly IDriver _driver; + private readonly ICompositeDeviceHub _deviceHub; + private readonly IAppInfo _appInfo; + private readonly ISettingsManager _settingsManager; + private readonly IPluginManager _pluginManager; + private readonly IPluginFactory _pluginFactory; + private readonly IPresetManager _presetManager; + private readonly IUpdater? _updater; + + public DriverDaemon( + IServiceProvider serviceProvider, + IDriver driver, + ICompositeDeviceHub deviceHub, + IAppInfo appInfo, + ISettingsManager settingsManager, + IPluginManager pluginManager, + IPluginFactory pluginFactory, + IPresetManager presetManager + ) { - Driver = driver; + _serviceProvider = serviceProvider; + _driver = driver; + _deviceHub = deviceHub; + _appInfo = appInfo; + _settingsManager = settingsManager; + _pluginManager = pluginManager; + _pluginFactory = pluginFactory; + _presetManager = presetManager; + + _updater = serviceProvider.GetService(); + } + public async Task Initialize() + { Log.Output += (sender, message) => { LogMessages.Add(message); @@ -41,13 +73,14 @@ public DriverDaemon(Driver driver) Message?.Invoke(sender, message); }; - Driver.TabletsChanged += (sender, e) => TabletsChanged?.Invoke(sender, e); - Driver.CompositeDeviceHub.DevicesChanged += async (sender, args) => + _driver.InputDevicesChanged += + (sender, e) => TabletsChanged?.Invoke(sender, e.Select(c => c.Configuration)); + _deviceHub.DevicesChanged += async (_, args) => { if (args.Additions.Any()) { await DetectTablets(); - await SetSettings(Settings); + await ApplySettings(_settingsManager.Settings); } }; @@ -60,14 +93,14 @@ public DriverDaemon(Driver driver) Log.Write("Detect", $"Detected input coming from {driverInfo.Name} driver", LogLevel.Error); } - LoadUserSettings(); + _pluginManager.Clean(); + await LoadUserSettings(); #if !DEBUG - SleepDetection = new(async () => + SleepDetection = new SleepDetectionThread(() => { - Log.Write(nameof(SleepDetectionThread), "Sleep detected...", LogLevel.Info); - await DetectTablets(); - await SetSettings(Settings); + Log.Write(nameof(SleepDetectionThread), "Sleep detected...", LogLevel.Debug); + DetectTablets().RunSynchronously(); }); SleepDetection.Start(); @@ -76,18 +109,15 @@ public DriverDaemon(Driver driver) public event EventHandler? Message; public event EventHandler? DeviceReport; - public event EventHandler>? TabletsChanged; + public event EventHandler>? TabletsChanged; - public Driver Driver { get; } - private Settings? Settings { set; get; } - private Collection LogMessages { set; get; } = new Collection(); - private Collection Tools { set; get; } = new Collection(); - private IUpdater Updater = DesktopInterop.Updater; + private Collection LogMessages { get; } = new Collection(); + private Collection Tools { get; } = new Collection(); #if !DEBUG - private readonly SleepDetectionThread SleepDetection; + private SleepDetectionThread? SleepDetection { set; get; } #endif - private bool debugging; + private bool _debugging; public Task WriteMessage(LogMessage message) { @@ -97,139 +127,155 @@ public Task WriteMessage(LogMessage message) public Task LoadPlugins() { - var pluginDir = new DirectoryInfo(AppInfo.Current.PluginDirectory); - - if (!pluginDir.Exists) - { - pluginDir.Create(); - Log.Write("Plugin", $"The plugin directory '{pluginDir.FullName}' has been created"); - } - - AppInfo.PluginManager.Load(); - - // Add services to inject on plugin construction - AppInfo.PluginManager.AddService(() => this.Driver); - + _pluginManager.Load(); return Task.CompletedTask; } + public Task CheckAssemblyHashes(string remoteHash) + { + var localHash = _pluginManager.GetStateHash(); + return Task.FromResult(localHash == remoteHash); + } + public Task InstallPlugin(string filePath) { - return Task.FromResult(AppInfo.PluginManager.InstallPlugin(filePath)); + return Task.FromResult(_pluginManager.InstallPlugin(filePath)); } - public Task UninstallPlugin(string directoryPath) + public Task UninstallPlugin(PluginMetadata metadata) { - var plugins = AppInfo.PluginManager.GetLoadedPlugins(); - var context = plugins.First(ctx => ctx.Directory.FullName == directoryPath); - return Task.FromResult(AppInfo.PluginManager.UninstallPlugin(context)); + var context = _pluginManager.Plugins.First(ctx => ctx.GetMetadata().Match(metadata)); + return Task.FromResult(_pluginManager.UninstallPlugin(context)); } public Task DownloadPlugin(PluginMetadata metadata) { - return AppInfo.PluginManager.DownloadPlugin(metadata); + return _pluginManager.DownloadPlugin(metadata); } - public Task> GetTablets() + public async Task> GetRemotePlugins(string owner, string name, string gitRef) { - return Task.FromResult(Driver.Tablets); + return await PluginMetadataCollection.DownloadAsync(_appInfo, owner, name, gitRef); } - public async Task> DetectTablets() + public Task> GetInstalledPlugins() { - Driver.Detect(); - await Task.Run(CheckForProblematicProcesses).ConfigureAwait(false); + return Task.FromResult(_pluginManager.Plugins.Select(p => p.GetMetadata())); + } - foreach (var tablet in Driver.InputDevices) + public Task> GetTablets() + { + return Task.FromResult(_driver.InputDevices.Select(c => c.Configuration)); + } + + public async Task> DetectTablets() + { + _driver.Detect(); + + foreach (var tablet in _driver.InputDevices) { - foreach (var dev in tablet.InputDevices) + foreach (var dev in tablet.Endpoints) { - dev.RawReport += (_, report) => PostDebugReport(tablet, report); - dev.RawClone = debugging; + dev.RawReport += (_, report) => PostDebugReport(dev.Configuration.Name, report); + dev.RawClone = _debugging; } } return await GetTablets(); } - public Task SetSettings(Settings? settings) + public async Task SaveSettings(Settings settings) + { + settings.Serialize(new FileInfo(_appInfo.SettingsFile)); + Log.Write("Settings", $"Settings saved to '{_appInfo.SettingsFile}'"); + await ApplySettings(settings); + } + + public Task ApplySettings(Settings? settings) { // Dispose filters that implement IDisposable interface - foreach (var obj in Driver.InputDevices.SelectMany(d => d.OutputMode?.Elements ?? (IEnumerable)Array.Empty())) + foreach (var obj in _driver.InputDevices.SelectMany(d => + d.OutputMode?.Elements ?? (IEnumerable) Array.Empty())) if (obj is IDisposable disposable) disposable.Dispose(); - Settings = settings ??= Settings.GetDefaults(); + _settingsManager.Settings = settings ?? Settings.GetDefaults(); - foreach (InputDeviceTree? dev in Driver.InputDevices) + foreach (var device in _driver.InputDevices) { - var tabletReference = dev.CreateReference(); - string group = dev.Properties.Name; - var profile = Settings.Profiles[dev]; - - profile.BindingSettings.MatchSpecifications(dev.Properties.Specifications); + var group = device.Configuration.Name; - dev.OutputMode = profile.OutputMode.Construct(tabletReference); + var profile = _settingsManager.Settings.Profiles.GetOrSetDefaults(_serviceProvider, device); + device.OutputMode = _pluginFactory.Construct(profile.OutputMode, device); - if (dev.OutputMode != null) - Log.Write(group, $"Output mode: {profile.OutputMode.Name}"); + if (device.OutputMode != null) + { + var outputModeName = _pluginFactory.GetName(profile.OutputMode); + Log.Write(group, $"Output mode: {outputModeName}"); + } - if (dev.OutputMode is AbsoluteOutputMode absoluteMode) - SetAbsoluteModeSettings(dev, absoluteMode, profile.AbsoluteModeSettings); + if (device.OutputMode is IOutputMode outputMode) + { + // TODO: Rework BindingHandler to proper pipeline element without this constructor + SetOutputModeSettings(device, outputMode, profile); - if (dev.OutputMode is RelativeOutputMode relativeMode) - SetRelativeModeSettings(dev, relativeMode, profile.RelativeModeSettings); + var mouseButtonHandler = (outputMode as IMouseButtonSource)?.MouseButtonHandler; + var bindingHandler = + _serviceProvider.CreateInstance(device, profile.BindingSettings, + mouseButtonHandler); - if (dev.OutputMode is IOutputMode outputMode) - { - SetOutputModeSettings(dev, outputMode, profile, tabletReference); - SetBindingHandlerSettings(dev, outputMode, profile.BindingSettings, tabletReference); + var lastElement = outputMode.Elements?.LastOrDefault() ?? + outputMode as IPipelineElement; + lastElement.Emit += bindingHandler.Consume; } } - if (Driver.InputDevices.Count > 0) - Log.Write("Settings", "Driver is enabled."); + Log.Write("Settings", "Driver is enabled."); SetToolSettings(); return Task.CompletedTask; } - public async Task ResetSettings() + public async Task ResetSettings() { - await SetSettings(Settings.GetDefaults()); + await ApplySettings(Settings.GetDefaults()); + return await GetSettings(); } - private async void LoadUserSettings() + private async Task LoadUserSettings() { - AppInfo.PluginManager.Clean(); + _pluginManager.Clean(); await LoadPlugins(); await DetectTablets(); - var appdataDir = new DirectoryInfo(AppInfo.Current.AppDataDirectory); + var appdataDir = new DirectoryInfo(_appInfo.AppDataDirectory); if (!appdataDir.Exists) { appdataDir.Create(); Log.Write("Settings", $"Created OpenTabletDriver application data directory: {appdataDir.FullName}"); } - var settingsFile = new FileInfo(AppInfo.Current.SettingsFile); + var settingsFile = new FileInfo(_appInfo.SettingsFile); if (settingsFile.Exists) { - SettingsMigrator.Migrate(AppInfo.Current); + var migrator = new SettingsMigrator(_serviceProvider); + migrator.Migrate(_appInfo); + var settings = Settings.Deserialize(settingsFile); if (settings != null) { - await SetSettings(settings); + await ApplySettings(settings); } else { Log.Write("Settings", "Invalid settings detected. Attempting recovery.", LogLevel.Error); settings = Settings.GetDefaults(); + Settings.Recover(settingsFile, settings); Log.Write("Settings", "Recovery complete"); - await SetSettings(settings); + await ApplySettings(settings); } } else @@ -238,238 +284,164 @@ private async void LoadUserSettings() } } - private void SetOutputModeSettings(InputDeviceTree dev, IOutputMode outputMode, Profile profile, TabletReference tabletReference) + private void SetOutputModeSettings(InputDevice dev, IOutputMode outputMode, Profile profile) { - string group = dev.Properties.Name; - outputMode.Tablet = dev; + string group = dev.Configuration.Name; var elements = from store in profile.Filters - where store.Enable == true - let filter = store.Construct>(tabletReference) - where filter != null - select filter; + where store.Enable + let filter = _pluginFactory.Construct(store, dev) + where filter != null + select filter; + outputMode.Elements = elements.ToList(); - if (outputMode.Elements != null && outputMode.Elements.Count > 0) + if (outputMode.Elements.Any()) Log.Write(group, $"Filters: {string.Join(", ", outputMode.Elements)}"); } - private void SetAbsoluteModeSettings(InputDeviceTree dev, AbsoluteOutputMode absoluteMode, AbsoluteModeSettings settings) + private void SetToolSettings() { - string group = dev.Properties.Name; - absoluteMode.Output = settings.Display.Area; - - Log.Write(group, $"Display area: {absoluteMode.Output}"); + foreach (var runningTool in Tools) + runningTool.Dispose(); + Tools.Clear(); - absoluteMode.Input = settings.Tablet.Area; - Log.Write(group, $"Tablet area: {absoluteMode.Input}"); + foreach (var settings in _settingsManager.Settings.Tools) + { + if (settings.Enable == false) + continue; - absoluteMode.AreaClipping = settings.EnableClipping; - Log.Write(group, $"Clipping: {(absoluteMode.AreaClipping ? "Enabled" : "Disabled")}"); + var tool = _pluginFactory.Construct(settings); - absoluteMode.AreaLimiting = settings.EnableAreaLimiting; - Log.Write(group, $"Ignoring reports outside area: {(absoluteMode.AreaLimiting ? "Enabled" : "Disabled")}"); + if (tool?.Initialize() ?? false) + { + Tools.Add(tool); + } + else + { + var name = _pluginFactory.GetName(settings); + Log.Write("Tool", $"Failed to initialize {name} tool.", LogLevel.Error); + } + } } - private void SetRelativeModeSettings(InputDeviceTree dev, RelativeOutputMode relativeMode, RelativeModeSettings settings) + public Task GetSettings() { - string group = dev.Properties.Name; - relativeMode.Sensitivity = settings.Sensitivity; - - Log.Write(group, $"Relative Mode Sensitivity (X, Y): {relativeMode.Sensitivity}"); - - relativeMode.Rotation = settings.RelativeRotation; - Log.Write(group, $"Relative Mode Rotation: {relativeMode.Rotation}"); - - relativeMode.ResetTime = settings.ResetTime; - Log.Write(group, $"Reset time: {relativeMode.ResetTime}"); + return Task.FromResult(_settingsManager.Settings); } - /// - /// Checks for any problematic processes running on the user's computer that may - /// impair function or detection of tablets, such as video game anti-cheat software. - /// - private void CheckForProblematicProcesses() + public async Task ApplyPreset(string name) { - if (SystemInterop.CurrentPlatform == PluginPlatform.Windows) - { - if (Process.GetProcessesByName("vgc").Any()) - Log.Write("Detect", "Valorant's anti-cheat program Vanguard is detected. Tablet function may be impaired.", LogLevel.Warning); - if (Process.GetProcessesByName("VALORANT-Win64-Shipping").Any()) - Log.Write("Detect", "Valorant is detected. Tablet function may be impaired.", LogLevel.Warning); - } + _presetManager.Refresh(); + if (_presetManager.FindPreset(name) is Preset preset) + await ApplySettings(preset.Settings); + else + Log.Write("Presets", $"Unable apply preset \"{name}\" as it could not be found."); } - private void SetBindingHandlerSettings(InputDeviceTree dev, IOutputMode outputMode, BindingSettings settings, TabletReference tabletReference) + public Task> GetPresets() { - string group = dev.Properties.Name; - var bindingHandler = new BindingHandler(outputMode); - - var bindingServiceProvider = new ServiceManager(); - object? pointer = outputMode switch - { - AbsoluteOutputMode absoluteOutputMode => absoluteOutputMode.Pointer, - RelativeOutputMode relativeOutputMode => relativeOutputMode.Pointer, - _ => null - }; - - if (pointer is IMouseButtonHandler mouseButtonHandler) - bindingServiceProvider.AddService(() => mouseButtonHandler); - - var tip = bindingHandler.Tip = new ThresholdBindingState - { - Binding = settings.TipButton?.Construct(bindingServiceProvider, tabletReference), - - ActivationThreshold = settings.TipActivationThreshold - }; - - if (tip.Binding != null) - { - Log.Write(group, $"Tip Binding: [{tip.Binding}]@{tip.ActivationThreshold}%"); - } - - var eraser = bindingHandler.Eraser = new ThresholdBindingState - { - Binding = settings.EraserButton?.Construct(bindingServiceProvider, tabletReference), - ActivationThreshold = settings.EraserActivationThreshold - }; - - if (eraser.Binding != null) - { - Log.Write(group, $"Eraser Binding: [{eraser.Binding}]@{eraser.ActivationThreshold}%"); - } - - if (settings.PenButtons != null && settings.PenButtons.Any(b => b?.Path != null)) - { - SetBindingHandlerCollectionSettings(bindingServiceProvider, settings.PenButtons, bindingHandler.PenButtons, tabletReference); - Log.Write(group, $"Pen Bindings: " + string.Join(", ", bindingHandler.PenButtons.Select(b => b.Value?.Binding))); - } - - if (settings.AuxButtons != null && settings.AuxButtons.Any(b => b?.Path != null)) - { - SetBindingHandlerCollectionSettings(bindingServiceProvider, settings.AuxButtons, bindingHandler.AuxButtons, tabletReference); - Log.Write(group, $"Express Key Bindings: " + string.Join(", ", bindingHandler.AuxButtons.Select(b => b.Value?.Binding))); - } - - if (settings.MouseButtons != null && settings.MouseButtons.Any(b => b?.Path != null)) - { - SetBindingHandlerCollectionSettings(bindingServiceProvider, settings.MouseButtons, bindingHandler.MouseButtons, tabletReference); - Log.Write(group, $"Mouse Button Bindings: [" + string.Join("], [", bindingHandler.MouseButtons.Select(b => b.Value?.Binding)) + "]"); - } - - var scrollUp = bindingHandler.MouseScrollUp = new BindingState - { - Binding = settings.MouseScrollUp?.Construct(bindingServiceProvider, tabletReference) - }; - - var scrollDown = bindingHandler.MouseScrollDown = new BindingState - { - Binding = settings.MouseScrollDown?.Construct(bindingServiceProvider, tabletReference) - }; - - if (scrollUp.Binding != null || scrollDown.Binding != null) - { - Log.Write(group, $"Mouse Scroll: Up: [{scrollUp?.Binding}] Down: [{scrollDown?.Binding}]"); - } + _presetManager.Refresh(); + return Task.FromResult(_presetManager.GetPresets().Select(p => p.Name)); } - private void SetBindingHandlerCollectionSettings(IServiceManager serviceManager, PluginSettingStoreCollection collection, Dictionary targetDict, TabletReference tabletReference) + public Task SavePreset(string name, Settings settings) { - for (int index = 0; index < collection.Count; index++) - { - var binding = collection[index]?.Construct(serviceManager, tabletReference); - var state = binding == null ? null : new BindingState - { - Binding = binding - }; - - if (!targetDict.TryAdd(index, state)) - targetDict[index] = state; - } + _presetManager.Save(name, settings); + return Task.CompletedTask; } - private void SetToolSettings() + public Task> GetDevices() { - foreach (var runningTool in Tools) - runningTool.Dispose(); - Tools.Clear(); - - if (Settings != null) - { - foreach (PluginSettingStore store in Settings.Tools) - { - if (store.Enable == false) - continue; - - var tool = store.Construct(); - - if (tool?.Initialize() ?? false) - Tools.Add(tool); - else - Log.Write("Tool", $"Failed to initialize {store.Name} tool.", LogLevel.Error); - } - } + return Task.FromResult(_deviceHub.GetDevices()); } - public Task GetSettings() + public Task> GetDisplays() { - return Task.FromResult(Settings); + return Task.FromResult(_serviceProvider.GetRequiredService().Displays + .Where(t => t is not IVirtualScreen)); } - public Task> GetDevices() + public Task GetApplicationInfo() { - return Task.FromResult(Driver.CompositeDeviceHub.GetDevices().Select(d => new SerializedDeviceEndpoint(d))); + return Task.FromResult(_appInfo); } - public Task GetApplicationInfo() + public async Task GetDiagnostics() { - return Task.FromResult(AppInfo.Current); + var devices = await GetDevices(); + return ActivatorUtilities.CreateInstance(_serviceProvider, LogMessages, devices); } public Task SetTabletDebug(bool enabled) { - debugging = enabled; - foreach (var dev in Driver.InputDevices.SelectMany(d => d.InputDevices)) - dev.RawClone = debugging; + _debugging = enabled; + foreach (var endpoint in _driver.InputDevices.SelectMany(d => d.Endpoints)) + { + endpoint.RawClone = _debugging; + } - Log.Debug("Tablet", $"Tablet debugging is {(debugging ? "enabled" : "disabled")}"); + Log.Debug("Tablet", $"Tablet debugging is {(_debugging ? "enabled" : "disabled")}"); return Task.CompletedTask; } - public Task RequestDeviceString(int vid, int pid, int index) + public Task RequestDeviceString(int vid, int pid, int index) { - var tablet = Driver.CompositeDeviceHub.GetDevices().Where(d => d.VendorID == vid && d.ProductID == pid).FirstOrDefault(); + var tablet = _deviceHub.GetDevices().FirstOrDefault(d => d.VendorID == vid && d.ProductID == pid); if (tablet == null) - throw new IOException("Device not found"); + throw new IOException($"Device not found ({vid:X2}:{pid:X2})"); - return Task.FromResult(tablet.GetDeviceString((byte)index)); + return Task.FromResult(tablet.GetDeviceString((byte) index)); } public Task> GetCurrentLog() { - return Task.FromResult((IEnumerable)LogMessages); + return Task.FromResult((IEnumerable) LogMessages); + } + + public Task GetDefaults(string path) + { + var type = _pluginFactory.GetPluginType(path)!; + var settings = type.GetDefaultSettings(_serviceProvider, this); + return Task.FromResult(settings); } - private void PostDebugReport(TabletReference tablet, IDeviceReport report) + public Task GetProxiedType(string typeName) { - if (report != null && tablet != null) - DeviceReport?.Invoke(this, new DebugReportData(tablet, report)); + var type = _pluginManager.ExportedTypes.First(t => t.GetPath() == typeName); + var proxy = ActivatorUtilities.CreateInstance(_serviceProvider, type); + return Task.FromResult(proxy); + } + + public Task> GetMatchingTypes(string typeName) + { + var baseType = _pluginManager.ExportedTypes.First(t => t.GetPath() == typeName); + var matchingTypes = from type in _pluginFactory.GetMatchingTypes(baseType) + select ActivatorUtilities.CreateInstance(_serviceProvider, type); + return Task.FromResult(matchingTypes); } public Task HasUpdate() { - return Updater?.CheckForUpdates() ?? Task.FromResult(false); + return _updater?.CheckForUpdates() ?? Task.FromResult(false); } - public Task GetUpdateInfo() + public async Task GetUpdateInfo() { - return Updater.GetInfo(); + if (await _updater?.GetInfo()! is UpdateInfo updateInfo) + return updateInfo; + return null; } public Task InstallUpdate() { - return Updater?.InstallUpdate() ?? Task.CompletedTask; + return _updater?.InstallUpdate() ?? Task.CompletedTask; + } + + private void PostDebugReport(string tablet, IDeviceReport report) + { + DeviceReport?.Invoke(this, new DebugReportData(tablet, report)); } } } diff --git a/OpenTabletDriver.Daemon/Extensions.cs b/OpenTabletDriver.Daemon/Extensions.cs new file mode 100644 index 000000000..828865e75 --- /dev/null +++ b/OpenTabletDriver.Daemon/Extensions.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Desktop.Reflection; + +#nullable enable + +namespace OpenTabletDriver.Daemon +{ + public static class Extensions + { + public static string? GetName(this IPluginFactory pluginFactory, PluginSettings? settingStore) => GetName(pluginFactory, settingStore?.Path); + + public static string? GetName(this IPluginFactory pluginFactory, string? path) + { + if (path == null) + return null; + + var type = pluginFactory.GetPluginType(path); + var attr = type?.GetCustomAttribute(); + return attr != null ? attr.Name : type?.Name; + } + } +} diff --git a/OpenTabletDriver.Daemon/OpenTabletDriver.Daemon.csproj b/OpenTabletDriver.Daemon/OpenTabletDriver.Daemon.csproj index 82f8871bc..b68dd5083 100644 --- a/OpenTabletDriver.Daemon/OpenTabletDriver.Daemon.csproj +++ b/OpenTabletDriver.Daemon/OpenTabletDriver.Daemon.csproj @@ -4,6 +4,7 @@ Exe $(FrameworkBase) VSTHRD100; VSTHRD101; VSTHRD110; VSTHRD200 + enable diff --git a/OpenTabletDriver.Daemon/Program.cs b/OpenTabletDriver.Daemon/Program.cs index 343b253d0..4a5af1181 100644 --- a/OpenTabletDriver.Daemon/Program.cs +++ b/OpenTabletDriver.Daemon/Program.cs @@ -1,20 +1,20 @@ using System; using System.CommandLine; using System.CommandLine.Invocation; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using OpenTabletDriver.Desktop; +using OpenTabletDriver.Desktop.Interop.AppInfo; using OpenTabletDriver.Desktop.RPC; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Components; namespace OpenTabletDriver.Daemon { - partial class Program + public class Program { - static async Task Main(string[] args) + private static async Task Main(string[] args) { using (var instance = new Instance("OpenTabletDriver.Daemon")) { @@ -25,60 +25,60 @@ static async Task Main(string[] args) return; } - AppDomain.CurrentDomain.UnhandledException += (sender, e) => - { - var exception = (Exception)e.ExceptionObject; - File.WriteAllLines(Path.Join(AppInfo.Current.AppDataDirectory, "daemon.log"), - new string[] - { - DateTime.Now.ToString(), - exception.GetType().FullName, - exception.Message, - exception.Source, - exception.StackTrace, - exception.TargetSite.Name - } - ); - }; - var rootCommand = new RootCommand("OpenTabletDriver") { - new Option(new string[] { "--appdata", "-a" }, "Application data directory") + new Option(new[] { "--appdata", "-a" }, "Application data directory") { - Argument = new Argument("appdata") + Argument = new Argument("appdata") }, - new Option(new string[] { "--config", "-c" }, "Configuration directory") + new Option(new[] { "--config", "-c" }, "Configuration directory") { - Argument = new Argument ("config") + Argument = new Argument ("config") } }; - rootCommand.Handler = CommandHandler.Create((appdata, config) => - { - if (!string.IsNullOrWhiteSpace(appdata?.FullName)) - AppInfo.Current.AppDataDirectory = appdata.FullName; - if (!string.IsNullOrWhiteSpace(config?.FullName)) - AppInfo.Current.ConfigurationDirectory = config.FullName; - }); - rootCommand.Invoke(args); - var host = new RpcHost("OpenTabletDriver.Daemon"); - host.ConnectionStateChanged += (sender, state) => - Log.Write("IPC", $"{(state ? "Connected to" : "Disconnected from")} a client.", LogLevel.Debug); - - await host.Run(BuildDaemon()); + rootCommand.Handler = CommandHandler.Create(Run); + await rootCommand.InvokeAsync(args); } } - static DriverDaemon BuildDaemon() + private static async Task Run(string appdata, string config) { - return new DriverDaemon(new DriverBuilder() - .ConfigureServices(serviceCollection => - { - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - }) - .Build(out _) - ); + var serviceCollection = DesktopServiceCollection.GetPlatformServiceCollection(); + var appInfo = AppInfo.GetPlatformAppInfo(); + + if (!string.IsNullOrWhiteSpace(appdata)) + appInfo.AppDataDirectory = FileUtilities.InjectEnvironmentVariables(appdata); + if (!string.IsNullOrWhiteSpace(config)) + appInfo.ConfigurationDirectory = FileUtilities.InjectEnvironmentVariables(config); + + serviceCollection.AddSingleton(appInfo); + + AppDomain.CurrentDomain.UnhandledException += (_, e) => + { + var exception = (Exception)e.ExceptionObject; + File.AppendAllLines(Path.Join(appInfo.AppDataDirectory, "daemon.log"), + new[] + { + DateTime.Now.ToString(CultureInfo.InvariantCulture), + exception.GetType().FullName!, + exception.Message, + exception.Source ?? string.Empty, + exception.StackTrace ?? string.Empty, + exception.TargetSite?.Name ?? string.Empty + } + ); + }; + + var serviceProvider = serviceCollection.BuildServiceProvider(); + var daemon = serviceProvider.CreateInstance(); + + var rpcHost = new RpcHost("OpenTabletDriver.Daemon"); + rpcHost.ConnectionStateChanged += (_, state) => + Log.Write("IPC", $"{(state ? "Connected to" : "Disconnected from")} a client.", LogLevel.Debug); + + await daemon.Initialize(); + await rpcHost.Run(daemon); } } } diff --git a/OpenTabletDriver.Desktop/AppInfo.cs b/OpenTabletDriver.Desktop/AppInfo.cs deleted file mode 100644 index a4ead9dd2..000000000 --- a/OpenTabletDriver.Desktop/AppInfo.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.Desktop -{ - using static FileUtilities; - - public class AppInfo - { - private string configurationDirectory, - settingsFile, - pluginDirectory, - presetDirectory, - temporaryDirectory, - cacheDirectory, - backupDirectory, - trashDirectory; - - private static AppInfo current; - public static AppInfo Current - { - set => current = value; - get => current ??= SystemInterop.CurrentPlatform switch - { - PluginPlatform.Windows => new AppInfo - { - AppDataDirectory = GetExistingPathOrLast(Path.Join(ProgramDirectory, "userdata"), "$LOCALAPPDATA\\OpenTabletDriver") - }, - PluginPlatform.Linux => new AppInfo - { - ConfigurationDirectory = GetExistingPath("$XDG_DATA_HOME/OpenTabletDriver/Configurations", "~/.local/share/OpenTabletDriver/Configurations"), - AppDataDirectory = GetPath("$XDG_CONFIG_HOME/OpenTabletDriver", "~/.config/OpenTabletDriver"), - TemporaryDirectory = GetPath("$XDG_RUNTIME_DIR/OpenTabletDriver", "$TEMP/OpenTabletDriver"), - CacheDirectory = GetPath("$XDG_CACHE_HOME/OpenTabletDriver", "~/.cache/OpenTabletDriver"), - }, - PluginPlatform.MacOS => new AppInfo() - { - AppDataDirectory = GetPath("~/Library/Application Support/OpenTabletDriver"), - TemporaryDirectory = GetPath("$TMPDIR/OpenTabletDriver"), - CacheDirectory = GetPath("~/Library/Caches/OpenTabletDriver") - }, - _ => null - }; - } - - public static DesktopPluginManager PluginManager { get; } = new DesktopPluginManager(); - - public static PresetManager PresetManager { get; } = new PresetManager(); - - public string AppDataDirectory { set; get; } - - public string ConfigurationDirectory - { - set => this.configurationDirectory = value; - get => this.configurationDirectory ?? GetDefaultConfigurationDirectory(); - } - - public string SettingsFile - { - set => this.settingsFile = value; - get => this.settingsFile ?? GetDefaultSettingsFile(); - } - - public string PluginDirectory - { - set => this.pluginDirectory = value; - get => this.pluginDirectory ?? GetDefaultPluginDirectory(); - } - - public string PresetDirectory - { - set => this.presetDirectory = value; - get => this.presetDirectory ?? GetDefaultPresetDirectory(); - } - - public string TemporaryDirectory - { - set => this.temporaryDirectory = value; - get => this.temporaryDirectory ?? GetDefaultTemporaryDirectory(); - } - - public string CacheDirectory - { - set => this.cacheDirectory = value; - get => this.cacheDirectory ?? GetDefaultCacheDirectory(); - } - - public string BackupDirectory - { - set => this.backupDirectory = value; - get => this.backupDirectory ?? GetDefaultBackupDirectory(); - } - - public string TrashDirectory - { - set => this.trashDirectory = value; - get => this.trashDirectory ?? GetDefaultTrashDirectory(); - } - - public static string ProgramDirectory => AppContext.BaseDirectory; - - private static string GetDirectory(params string[] directories) - { - foreach (var dir in directories.Select(InjectEnvironmentVariables)) - if (Path.IsPathRooted(dir)) - return dir; - - return null; - } - - private static string GetDirectoryIfExists(params string[] directories) - { - foreach (var dir in directories.Select(InjectEnvironmentVariables)) - if (Directory.Exists(dir)) - return dir; - - return InjectEnvironmentVariables(directories.Last()); - } - - private string GetDefaultConfigurationDirectory() => GetExistingPathOrLast( - Path.Join(AppDataDirectory, "Configurations"), - Path.Join(ProgramDirectory, "Configurations"), - Path.Join(Environment.CurrentDirectory, "Configurations") - ); - - private string GetDefaultSettingsFile() => Path.Join(AppDataDirectory, "settings.json"); - private string GetDefaultPluginDirectory() => Path.Join(AppDataDirectory, "Plugins"); - private string GetDefaultPresetDirectory() => Path.Join(AppDataDirectory, "Presets"); - private string GetDefaultTemporaryDirectory() => Path.Join(AppDataDirectory, "Temp"); - private string GetDefaultCacheDirectory() => Path.Join(AppDataDirectory, "Cache"); - private string GetDefaultBackupDirectory() => Path.Join(AppDataDirectory, "Backup"); - private string GetDefaultTrashDirectory() => Path.Join(AppDataDirectory, "Trash"); - } -} diff --git a/OpenTabletDriver.Desktop/Binding/BindingHandler.cs b/OpenTabletDriver.Desktop/Binding/BindingHandler.cs index eb7a85b97..a1fc854dd 100644 --- a/OpenTabletDriver.Desktop/Binding/BindingHandler.cs +++ b/OpenTabletDriver.Desktop/Binding/BindingHandler.cs @@ -1,90 +1,135 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; #nullable enable namespace OpenTabletDriver.Desktop.Binding { - [PluginIgnore] public class BindingHandler : IPipelineElement { - public BindingHandler(IOutputMode outputMode) + private readonly InputDevice _device; + private readonly IServiceProvider _serviceProvider; + + public BindingHandler(IServiceProvider serviceProvider, InputDevice device, BindingSettings settings, IMouseButtonHandler? mouseButtonHandler = null) { - this.outputMode = outputMode; + _serviceProvider = serviceProvider; + _device = device; + + var outputMode = _device.OutputMode!; // Force consume all reports from the last element - var lastElement = this.outputMode.Elements?.LastOrDefault() ?? (IPipelineElement)outputMode; + var lastElement = outputMode.Elements?.LastOrDefault() ?? (IPipelineElement)outputMode; lastElement.Emit += Consume; - } - public ThresholdBindingState? Tip { set; get; } - public ThresholdBindingState? Eraser { set; get; } + Tip = CreateBindingState(settings.TipButton, device, mouseButtonHandler); - public Dictionary PenButtons { set; get; } = new Dictionary(); - public Dictionary AuxButtons { set; get; } = new Dictionary(); - public Dictionary MouseButtons { set; get; } = new Dictionary(); + if (Tip != null) + Tip.ActivationThreshold = settings.TipActivationThreshold; - public BindingState? MouseScrollDown { set; get; } - public BindingState? MouseScrollUp { set; get; } + Eraser = CreateBindingState(settings.EraserButton, device, mouseButtonHandler); + if (Eraser != null) + Eraser.ActivationThreshold = settings.EraserActivationThreshold; + + PenButtons = CreateBindingStates(settings.PenButtons, device, mouseButtonHandler); + AuxButtons = CreateBindingStates(settings.AuxButtons, device, mouseButtonHandler); + MouseButtons = CreateBindingStates(settings.MouseButtons, device, mouseButtonHandler); + + MouseScrollDown = CreateBindingState(settings.MouseScrollDown, device, mouseButtonHandler); + MouseScrollUp = CreateBindingState(settings.MouseScrollUp, device, mouseButtonHandler); + } - private readonly IOutputMode outputMode; + private ThresholdBindingState? Tip { get; } + private ThresholdBindingState? Eraser { get; } + + private Dictionary PenButtons { get; } + private Dictionary AuxButtons { get; } + private Dictionary MouseButtons { get; } + + private BindingState? MouseScrollDown { get; } + private BindingState? MouseScrollUp { get; } public event Action? Emit; public void Consume(IDeviceReport report) { Emit?.Invoke(report); - HandleBinding(outputMode.Tablet, report); + HandleBinding(_device.OutputMode!.Tablet, report); } - public void HandleBinding(TabletReference tablet, IDeviceReport report) + private void HandleBinding(InputDevice device, IDeviceReport report) { - if (tablet == null) - return; - if (report is ITabletReport tabletReport) - HandleTabletReport(tablet, tablet.Properties.Specifications.Pen, tabletReport); + HandleTabletReport(device.Configuration.Specifications.Pen!, tabletReport); if (report is IAuxReport auxReport) - HandleAuxiliaryReport(tablet, auxReport); + HandleAuxiliaryReport(auxReport); if (report is IMouseReport mouseReport) - HandleMouseReport(tablet, mouseReport); + HandleMouseReport(mouseReport); } - private void HandleTabletReport(TabletReference tablet, PenSpecifications pen, ITabletReport report) + private void HandleTabletReport(PenSpecifications pen, ITabletReport report) { - float pressurePercent = (float)report.Pressure / (float)pen.MaxPressure * 100f; - if (report is IEraserReport eraserReport && eraserReport.Eraser) - Eraser?.Invoke(tablet, report, pressurePercent); + var pressurePercent = report.Pressure / (float)pen.MaxPressure * 100f; + if (report is IEraserReport { Eraser: true }) + Eraser?.Invoke(report, pressurePercent); else - Tip?.Invoke(tablet, report, pressurePercent); + Tip?.Invoke(report, pressurePercent); + + HandleBindingCollection(report, PenButtons, report.PenButtons); + } - HandleBindingCollection(tablet, report, PenButtons, report.PenButtons); + private void HandleAuxiliaryReport(IAuxReport report) + { + HandleBindingCollection(report, AuxButtons, report.AuxButtons); } - private void HandleAuxiliaryReport(TabletReference tablet, IAuxReport report) + private void HandleMouseReport(IMouseReport report) { - HandleBindingCollection(tablet, report, AuxButtons, report.AuxButtons); + HandleBindingCollection(report, MouseButtons, report.MouseButtons); + + MouseScrollDown?.Invoke(report, report.Scroll.Y < 0); + MouseScrollUp?.Invoke(report, report.Scroll.Y > 0); } - private void HandleMouseReport(TabletReference tablet, IMouseReport report) + private static void HandleBindingCollection(IDeviceReport report, IDictionary? bindings, IList newStates) { - HandleBindingCollection(tablet, report, MouseButtons, report.MouseButtons); + for (var i = 0; i < newStates.Count; i++) + { + if (bindings != null && bindings.TryGetValue(i, out var binding)) + binding?.Invoke(report, newStates[i]); + } + } - MouseScrollDown?.Invoke(tablet, report, report.Scroll.Y < 0); - MouseScrollUp?.Invoke(tablet, report, report.Scroll.Y > 0); + private T? CreateBindingState(PluginSettings? settings, params object?[] args) where T : BindingState + { + if (settings == null) + return null; + + var newArgs = args.TakeWhile(c => c != null) + .Select(c => c!) + .Append(settings); + + return _serviceProvider.CreateInstance(newArgs.ToArray()); } - private static void HandleBindingCollection(TabletReference tablet, IDeviceReport report, IDictionary bindings, IList newStates) + private Dictionary CreateBindingStates(PluginSettingsCollection collection, params object?[] args) { - for (int i = 0; i < newStates.Count; i++) + var dict = new Dictionary(); + + for (var index = 0; index < collection.Count; index++) { - if (bindings.TryGetValue(i, out var binding)) - binding?.Invoke(tablet, report, newStates[i]); + var state = CreateBindingState(collection[index], args); + + if (!dict.TryAdd(index, state)) + dict[index] = state; } + + return dict; } } } diff --git a/OpenTabletDriver.Desktop/Binding/BindingState.cs b/OpenTabletDriver.Desktop/Binding/BindingState.cs index 92e1284dd..ce6332b56 100644 --- a/OpenTabletDriver.Desktop/Binding/BindingState.cs +++ b/OpenTabletDriver.Desktop/Binding/BindingState.cs @@ -1,27 +1,31 @@ using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding { public class BindingState { - public IBinding Binding { set; get; } - public bool State { protected set; get; } + private readonly IBinding _binding; - protected bool PreviousState { set; get; } + public BindingState(IPluginFactory pluginFactory, InputDevice device, IMouseButtonHandler mouseButtonHandler, PluginSettings settings) + { + _binding = pluginFactory.Construct(settings, device, mouseButtonHandler); + } + + private bool _previousState; - public virtual void Invoke(TabletReference tablet, IDeviceReport report, bool newState) + public void Invoke(IDeviceReport report, bool newState) { - if (Binding is IStateBinding stateBinding) + if (_binding is IStateBinding stateBinding) { - if (newState && !PreviousState) - stateBinding.Press(tablet, report); - else if (!newState && PreviousState) - stateBinding.Release(tablet, report); + if (newState && !_previousState) + stateBinding.Press(report); + else if (!newState && _previousState) + stateBinding.Release(report); } - PreviousState = newState; + _previousState = newState; } } } diff --git a/OpenTabletDriver.Desktop/Binding/KeyBinding.cs b/OpenTabletDriver.Desktop/Binding/KeyBinding.cs index 8d96330e9..609a1f23e 100644 --- a/OpenTabletDriver.Desktop/Binding/KeyBinding.cs +++ b/OpenTabletDriver.Desktop/Binding/KeyBinding.cs @@ -1,12 +1,9 @@ +using System; using System.Collections.Generic; -using System.Linq; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Interop.Input.Keyboard; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Platform.Keyboard; -using OpenTabletDriver.Plugin.Tablet; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding { @@ -15,34 +12,36 @@ public class KeyBinding : IStateBinding { private const string PLUGIN_NAME = "Key Binding"; - [Resolved] - public IVirtualKeyboard Keyboard { set; get; } + private readonly InputDevice _device; + private readonly IVirtualKeyboard _keyboard; - [Property("Key"), PropertyValidated(nameof(ValidKeys))] + public KeyBinding(InputDevice device, IVirtualKeyboard keyboard, ISettingsProvider settingsProvider) + { + _device = device; + _keyboard = keyboard; + + settingsProvider.Inject(this); + } + + [Setting("Key"), MemberValidated(nameof(GetValidKeys))] public string Key { set; get; } - public void Press(TabletReference tablet, IDeviceReport report) + public void Press(IDeviceReport report) { if (!string.IsNullOrWhiteSpace(Key)) - Keyboard.Press(Key); + _keyboard.Press(Key); } - public void Release(TabletReference tablet, IDeviceReport report) + public void Release(IDeviceReport report) { if (!string.IsNullOrWhiteSpace(Key)) - Keyboard.Release(Key); + _keyboard.Release(Key); } - private static IEnumerable validKeys; - public static IEnumerable ValidKeys + public static IEnumerable GetValidKeys(IServiceProvider serviceProvider) { - get => validKeys ??= DesktopInterop.CurrentPlatform switch - { - PluginPlatform.Windows => WindowsVirtualKeyboard.EtoKeysymToVK.Keys, - PluginPlatform.Linux => EvdevVirtualKeyboard.EtoKeysymToEventCode.Keys, - PluginPlatform.MacOS => MacOSVirtualKeyboard.EtoKeysymToVK.Keys, - _ => null - }; + var keysProvider = serviceProvider.GetRequiredService(); + return keysProvider.EtoToNative.Keys; } public override string ToString() => $"{PLUGIN_NAME}: {Key}"; diff --git a/OpenTabletDriver.Desktop/Binding/LinuxArtistMode/LinuxArtistModeButtonBinding.cs b/OpenTabletDriver.Desktop/Binding/LinuxArtistMode/LinuxArtistModeButtonBinding.cs index e77fa0764..3fe6b6cf5 100644 --- a/OpenTabletDriver.Desktop/Binding/LinuxArtistMode/LinuxArtistModeButtonBinding.cs +++ b/OpenTabletDriver.Desktop/Binding/LinuxArtistMode/LinuxArtistModeButtonBinding.cs @@ -1,17 +1,22 @@ using System; -using OpenTabletDriver.Desktop.Interop; +using OpenTabletDriver.Attributes; using OpenTabletDriver.Desktop.Interop.Input.Absolute; using OpenTabletDriver.Native.Linux.Evdev; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding.LinuxArtistMode { - [PluginName("Linux Artist Mode"), SupportedPlatform(PluginPlatform.Linux)] + [PluginName("Linux Artist Mode"), SupportedPlatform(SystemPlatform.Linux)] public class LinuxArtistModeButtonBinding : IStateBinding { - private readonly EvdevVirtualTablet virtualTablet = (EvdevVirtualTablet)DesktopInterop.VirtualTablet; + private readonly InputDevice _inputDevice; + private readonly EvdevVirtualTablet _virtualTablet; + + public LinuxArtistModeButtonBinding(InputDevice inputDevice, EvdevVirtualTablet virtualTablet) + { + _inputDevice = inputDevice; + _virtualTablet = virtualTablet; + } public static string[] ValidButtons { get; } = { "Pen Button 1", @@ -19,15 +24,15 @@ public class LinuxArtistModeButtonBinding : IStateBinding "Pen Button 3" }; - [Property("Button"), PropertyValidated(nameof(ValidButtons))] - public string Button { get; set; } + [Setting("Button"), MemberValidated(nameof(ValidButtons))] + public string Button { set; get; } - public void Press(TabletReference tablet, IDeviceReport report) + public void Press(IDeviceReport report) { SetState(true); } - public void Release(TabletReference tablet, IDeviceReport report) + public void Release(IDeviceReport report) { SetState(false); } @@ -42,7 +47,7 @@ private void SetState(bool state) _ => throw new InvalidOperationException($"Invalid Button '{Button}'") }; - virtualTablet.SetKeyState(eventCode, state); + _virtualTablet.SetKeyState(eventCode, state); } } } diff --git a/OpenTabletDriver.Desktop/Binding/MouseBinding.cs b/OpenTabletDriver.Desktop/Binding/MouseBinding.cs index 07fa33ad5..53a1d34f4 100644 --- a/OpenTabletDriver.Desktop/Binding/MouseBinding.cs +++ b/OpenTabletDriver.Desktop/Binding/MouseBinding.cs @@ -1,43 +1,42 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding { [PluginName(PLUGIN_NAME)] public class MouseBinding : IStateBinding { - private const string PLUGIN_NAME = "Mouse Button Binding"; + private readonly IMouseButtonHandler _pointer; + + public MouseBinding(IMouseButtonHandler pointer, ISettingsProvider settingsProvider) + { + _pointer = pointer; + + settingsProvider.Inject(this); + } - [Resolved] - public IMouseButtonHandler Pointer { set; get; } + private const string PLUGIN_NAME = "Mouse Button Binding"; - [Property("Button"), PropertyValidated(nameof(ValidButtons))] + [Setting("Button"), MemberValidated(nameof(ValidButtons))] public string Button { set; get; } - public void Press(TabletReference tablet, IDeviceReport report) + public void Press(IDeviceReport report) { if (Enum.TryParse(Button, true, out var mouseButton)) - Pointer?.MouseDown(mouseButton); + _pointer?.MouseDown(mouseButton); } - public void Release(TabletReference tablet, IDeviceReport report) + public void Release(IDeviceReport report) { if (Enum.TryParse(Button, true, out var mouseButton)) - Pointer?.MouseUp(mouseButton); + _pointer?.MouseUp(mouseButton); } - private static IEnumerable validButtons; - public static IEnumerable ValidButtons - { - get => validButtons ??= Enum.GetValues().Select(Enum.GetName); - } + public static IEnumerable ValidButtons { get; } = Enum.GetValues(typeof(MouseButton)).Cast().Select(Enum.GetName); public override string ToString() => $"{PLUGIN_NAME}: {Button}"; } diff --git a/OpenTabletDriver.Desktop/Binding/MultiKeyBinding.cs b/OpenTabletDriver.Desktop/Binding/MultiKeyBinding.cs index 32267e88f..6d2ae1f56 100644 --- a/OpenTabletDriver.Desktop/Binding/MultiKeyBinding.cs +++ b/OpenTabletDriver.Desktop/Binding/MultiKeyBinding.cs @@ -1,53 +1,57 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Platform.Keyboard; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding { [PluginName(PLUGIN_NAME)] public class MultiKeyBinding : IStateBinding { - private const string PLUGIN_NAME = "Multi-Key Binding"; - private const char KEYS_SPLITTER = '+'; + private readonly IVirtualKeyboard _keyboard; + + public MultiKeyBinding(IVirtualKeyboard keyboard, ISettingsProvider settingsProvider) + { + _keyboard = keyboard; - private IList keys; - private string keysString; + settingsProvider.Inject(this); + } + + private const string PLUGIN_NAME = "Multi-Key Binding"; - [Resolved] - public IVirtualKeyboard Keyboard { set; get; } + private IList _keys; + private string _keysString; - [Property("Keys")] + [Setting("Keys")] public string Keys { set { - this.keysString = value; - this.keys = ParseKeys(Keys); + _keysString = value; + _keys = ParseKeys(Keys); } - get => this.keysString; + get => _keysString; } - public void Press(TabletReference tablet, IDeviceReport report) + public void Press(IDeviceReport report) { - if (keys.Count > 0) - Keyboard.Press(this.keys); + if (_keys.Count > 0) + _keyboard.Press(_keys); } - public void Release(TabletReference tablet, IDeviceReport report) + public void Release(IDeviceReport report) { - if (keys.Count > 0) - Keyboard.Release(this.keys); + if (_keys.Count > 0) + _keyboard.Release(_keys); } private IList ParseKeys(string str) { - var newKeys = str.Split(KEYS_SPLITTER, StringSplitOptions.TrimEntries); - return newKeys.All(k => Keyboard.SupportedKeys.Contains(k)) ? newKeys : Array.Empty(); + var newKeys = str.Split('+', StringSplitOptions.TrimEntries); + return newKeys.All(k => _keyboard.SupportedKeys.Contains(k)) ? newKeys : + throw new NotSupportedException($"The keybinding combination ({str}) is not supported."); } public override string ToString() => $"{PLUGIN_NAME}: {Keys}"; diff --git a/OpenTabletDriver.Desktop/Binding/ThresholdBindingState.cs b/OpenTabletDriver.Desktop/Binding/ThresholdBindingState.cs index 655a0b1af..746e19b84 100644 --- a/OpenTabletDriver.Desktop/Binding/ThresholdBindingState.cs +++ b/OpenTabletDriver.Desktop/Binding/ThresholdBindingState.cs @@ -1,14 +1,28 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Binding { public class ThresholdBindingState : BindingState { + private readonly InputDevice _device; + + public ThresholdBindingState( + IPluginFactory pluginFactory, + InputDevice device, + IMouseButtonHandler mouseButtonHandler, + PluginSettings settings + ) : base(pluginFactory, device, mouseButtonHandler, settings) + { + _device = device; + } + public float ActivationThreshold { set; get; } - public void Invoke(TabletReference tablet, IDeviceReport report, float value) + public void Invoke(IDeviceReport report, float value) { - bool newState = value > ActivationThreshold; + var newState = value > ActivationThreshold; if (report is ITabletReport tabletReport) { @@ -18,13 +32,13 @@ public void Invoke(TabletReference tablet, IDeviceReport report, float value) } else // remap pressure when beyond threshold { - var maxPressure = tablet.Properties.Specifications.Pen.MaxPressure; + var maxPressure = _device.Configuration.Specifications.Pen!.MaxPressure; var remappedPressure = (value - ActivationThreshold) / (100f - ActivationThreshold); tabletReport.Pressure = (uint)(maxPressure * remappedPressure); } } - base.Invoke(tablet, report, newState); + base.Invoke(report, newState); } } } diff --git a/OpenTabletDriver.Desktop/ClientServiceCollection.cs b/OpenTabletDriver.Desktop/ClientServiceCollection.cs new file mode 100644 index 000000000..bf0c3caa2 --- /dev/null +++ b/OpenTabletDriver.Desktop/ClientServiceCollection.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.RPC; + +namespace OpenTabletDriver.Desktop +{ + using static ServiceDescriptor; + + public class ClientServiceCollection : ServiceCollection + { + private static readonly IEnumerable RequiredServices = new[] + { + Singleton(p => p.CreateInstance>("OpenTabletDriver.Daemon")), + Transient(p => p.GetRequiredService>().Instance!) + }; + + protected ClientServiceCollection() + { + this.AddServices(RequiredServices); + } + + protected ClientServiceCollection(IEnumerable overridingServices) : this() + { + this.AddServices(overridingServices); + } + } +} diff --git a/OpenTabletDriver.Desktop/Components/SleepDetectionThread.cs b/OpenTabletDriver.Desktop/Components/SleepDetectionThread.cs new file mode 100644 index 000000000..4405794f9 --- /dev/null +++ b/OpenTabletDriver.Desktop/Components/SleepDetectionThread.cs @@ -0,0 +1,69 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Components +{ + [PublicAPI] + public class SleepDetectionThread : IDisposable + { + private readonly Stopwatch _stopwatch = new(); + private readonly Action _action; + private Task? _task; + private CancellationTokenSource? _cancellationTokenSource; + private double _prev; + + public SleepDetectionThread(Action action) + { + _action = action; + } + + public void Start() + { + if (_task != null) + { + Stop(); + } + + _cancellationTokenSource = new(); + _task = DetectionLoop(_cancellationTokenSource.Token); + } + + public void Stop() + { + if (_task != null) + { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + _task = null; + } + } + + private async Task DetectionLoop(CancellationToken cancellationToken) + { + _stopwatch.Start(); + while (!cancellationToken.IsCancellationRequested) + { + var elapsed = _stopwatch.Elapsed.TotalSeconds; + if (elapsed - _prev > 2) + _action?.Invoke(); + + _prev = elapsed; + + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + } + _stopwatch.Stop(); + } + + public void Dispose() + { + Stop(); + GC.SuppressFinalize(this); + } + } +} diff --git a/OpenTabletDriver.Desktop/Contracts/IDriverDaemon.cs b/OpenTabletDriver.Desktop/Contracts/IDriverDaemon.cs index b8a838dc0..b2b769339 100644 --- a/OpenTabletDriver.Desktop/Contracts/IDriverDaemon.cs +++ b/OpenTabletDriver.Desktop/Contracts/IDriverDaemon.cs @@ -1,12 +1,18 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop.AppInfo; +using OpenTabletDriver.Desktop.Reflection; using OpenTabletDriver.Desktop.Reflection.Metadata; using OpenTabletDriver.Desktop.RPC; using OpenTabletDriver.Desktop.Updater; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Logging; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Logging; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Tablet; + +#nullable enable namespace OpenTabletDriver.Desktop.Contracts { @@ -14,33 +20,50 @@ public interface IDriverDaemon { event EventHandler Message; event EventHandler DeviceReport; - event EventHandler> TabletsChanged; + event EventHandler>? TabletsChanged; + + Task Initialize(); Task WriteMessage(LogMessage message); Task LoadPlugins(); + Task CheckAssemblyHashes(string remoteHash); Task InstallPlugin(string filePath); - Task UninstallPlugin(string friendlyName); + Task UninstallPlugin(PluginMetadata metadata); Task DownloadPlugin(PluginMetadata metadata); + Task> GetRemotePlugins(string owner = "OpenTabletDriver", string name = "Plugin-Repository", string gitRef = "master"); - Task> GetDevices(); + Task> GetDevices(); + Task> GetDisplays(); - Task> GetTablets(); - Task> DetectTablets(); + Task> GetTablets(); + Task> DetectTablets(); - Task SetSettings(Settings settings); + Task SaveSettings(Settings settings); + Task ApplySettings(Settings settings); Task GetSettings(); - Task ResetSettings(); + Task ResetSettings(); + + Task ApplyPreset(string name); + Task> GetPresets(); + Task SavePreset(string name, Settings settings); - Task GetApplicationInfo(); + Task GetApplicationInfo(); + Task GetDiagnostics(); Task SetTabletDebug(bool isEnabled); - Task RequestDeviceString(int vendorID, int productID, int index); + Task RequestDeviceString(int vendorID, int productID, int index); Task> GetCurrentLog(); + Task GetDefaults(string path); + + Task GetProxiedType(string typeName); + Task> GetMatchingTypes(string typeName); + Task HasUpdate(); - Task GetUpdateInfo(); + Task GetUpdateInfo(); Task InstallUpdate(); + Task> GetInstalledPlugins(); } } diff --git a/OpenTabletDriver.Desktop/Conversion/ConversionFactorAreaConverter.cs b/OpenTabletDriver.Desktop/Conversion/ConversionFactorAreaConverter.cs index 106c5a3e0..4e65ed9f0 100644 --- a/OpenTabletDriver.Desktop/Conversion/ConversionFactorAreaConverter.cs +++ b/OpenTabletDriver.Desktop/Conversion/ConversionFactorAreaConverter.cs @@ -1,7 +1,5 @@ -using System.Numerics; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Conversion { @@ -15,21 +13,29 @@ public class ConversionFactorAreaConverter : IAreaConverter public string Bottom => "Bottom"; public string Right => "Right"; - protected double GetConversionFactor(TabletReference tablet) + private static double GetConversionFactor(InputDevice tablet) { - var digitizer = tablet.Properties.Specifications.Digitizer; + var digitizer = tablet.Configuration.Specifications.Digitizer; return digitizer.MaxX / digitizer.Width; } - public Area Convert(TabletReference tablet, double top, double left, double bottom, double right) + public AngledArea Convert(InputDevice tablet, double top, double left, double bottom, double right) { - double conversionFactor = GetConversionFactor(tablet); - var width = (right - left) / conversionFactor; - var height = (bottom - top) / conversionFactor; - var offsetX = (width / 2) + (left / conversionFactor); - var offsetY = (height / 2) + (top / conversionFactor); + var conversionFactor = GetConversionFactor(tablet); - return new Area((float)width, (float)height, new Vector2((float)offsetX, (float)offsetY), 0f); + var width = (float) ((right - left) / conversionFactor); + var height = (float) ((bottom - top) / conversionFactor); + var offsetX = (float) (width / 2 + left / conversionFactor); + var offsetY = (float) (height / 2 + top / conversionFactor); + + return new AngledArea + { + Width = width, + Height = height, + XPosition = offsetX, + YPosition = offsetY, + Rotation = 0 + }; } } } diff --git a/OpenTabletDriver.Desktop/Conversion/PercentageAreaConverter.cs b/OpenTabletDriver.Desktop/Conversion/PercentageAreaConverter.cs index 10bc0c447..2c5825ce2 100644 --- a/OpenTabletDriver.Desktop/Conversion/PercentageAreaConverter.cs +++ b/OpenTabletDriver.Desktop/Conversion/PercentageAreaConverter.cs @@ -1,7 +1,5 @@ -using System.Numerics; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Conversion { @@ -15,16 +13,23 @@ public class PercentageAreaConverter : IAreaConverter public string Bottom => "Down"; public string Right => "Right"; - public Area Convert(TabletReference tablet, double up, double left, double down, double right) + public AngledArea Convert(InputDevice tablet, double up, double left, double down, double right) { - var digitizer = tablet.Properties.Specifications.Digitizer; + var digitizer = tablet.Configuration.Specifications.Digitizer; - var width = (right - left) * digitizer.Width; - var height = (down - up) * digitizer.Height; - var offsetX = (width / 2) + (left * digitizer.Width); - var offsetY = (height / 2) + (up * digitizer.Height); + var width = (float) ((right - left) * digitizer.Width); + var height = (float) ((down - up) * digitizer.Height); + var offsetX = (float) (width / 2 + left * digitizer.Width); + var offsetY = (float) (height / 2 + up * digitizer.Height); - return new Area((float)width, (float)height, new Vector2((float)offsetX, (float)offsetY), 0f); + return new AngledArea + { + Width = width, + Height = height, + XPosition = offsetX, + YPosition = offsetY, + Rotation = 0 + }; } } } diff --git a/OpenTabletDriver.Desktop/Conversion/XP_PenDriverAreaConverter.cs b/OpenTabletDriver.Desktop/Conversion/XP_PenDriverAreaConverter.cs index bda1f7a55..ade8e04b9 100644 --- a/OpenTabletDriver.Desktop/Conversion/XP_PenDriverAreaConverter.cs +++ b/OpenTabletDriver.Desktop/Conversion/XP_PenDriverAreaConverter.cs @@ -1,7 +1,5 @@ -using System.Numerics; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Conversion { @@ -17,15 +15,22 @@ public class XP_PenDriverAreaConverter : IAreaConverter private const float XP_PEN_AREA_CONSTANT = 3.937f; - public Area Convert(TabletReference tablet, double w, double h, double x, double y) + public AngledArea Convert(InputDevice tablet, double w, double h, double x, double y) { double conversionFactor = XP_PEN_AREA_CONSTANT; - var width = w / conversionFactor; - var height = h / conversionFactor; - var offsetX = (width / 2) + (x / conversionFactor); - var offsetY = (height / 2) + (y / conversionFactor); + var width = (float) (w / conversionFactor); + var height = (float) (h / conversionFactor); + var offsetX = (float) (width / 2 + x / conversionFactor); + var offsetY = (float) (height / 2 + y / conversionFactor); - return new Area((float)width, (float)height, new Vector2((float)offsetX, (float)offsetY), 0f); + return new AngledArea + { + Width = width, + Height = height, + XPosition = offsetX, + YPosition = offsetY, + Rotation = 0 + }; } } } diff --git a/OpenTabletDriver.Desktop/DependencyInjectionExtensions.cs b/OpenTabletDriver.Desktop/DependencyInjectionExtensions.cs new file mode 100644 index 000000000..fbff59951 --- /dev/null +++ b/OpenTabletDriver.Desktop/DependencyInjectionExtensions.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenTabletDriver.Desktop +{ + public static class DependencyInjectionExtensions + { + public static void AddServices(this IServiceCollection serviceCollection, IEnumerable services) + { + var existingServiceTypes = serviceCollection.Select(s => s.ServiceType); + var newServiceTypes = services.Select(s => s.ServiceType); + var conflictingServiceTypes = existingServiceTypes.Intersect(newServiceTypes); + + var existingConflictingServices = serviceCollection.Where( + s => conflictingServiceTypes.Contains(s.ServiceType) + ); + + serviceCollection.RemoveServices(existingConflictingServices.ToImmutableArray()); + + foreach (var service in services) + { + serviceCollection.Add(service); + } + } + + public static void RemoveServices(this IServiceCollection serviceCollection, IEnumerable services) + { + foreach (var service in services) + { + serviceCollection.Remove(service); + } + } + + public static object CreateInstance(this IServiceProvider serviceProvider, Type type, params object[] additionalDependencies) + { + var constructorQuery = from ctor in type.GetConstructors() + let parameters = ctor.GetParameters() + orderby parameters.Length + select ctor; + + var constructor = constructorQuery.First(); + + var argsQuery = from parameter in constructor.GetParameters() + let paramType = parameter.ParameterType + select additionalDependencies.FirstOrDefault(d => d.GetType().IsAssignableTo(paramType)) ?? serviceProvider.GetRequiredService(paramType); + + return constructor.Invoke(argsQuery.ToArray()); + } + + public static T CreateInstance(this IServiceProvider serviceProvider, params object[] additionalDependencies) + { + return (T) serviceProvider.CreateInstance(typeof(T), additionalDependencies); + } + + public static T GetOrCreateInstance(this IServiceProvider serviceProvider, params object[] additionalDependencies) + { + if (serviceProvider.GetService() is T service) + return service; + + return CreateInstance(serviceProvider, additionalDependencies); + } + } +} diff --git a/OpenTabletDriver.Desktop/DesktopDeviceConfigurationProvider.cs b/OpenTabletDriver.Desktop/DesktopDeviceConfigurationProvider.cs index db36a097f..df6096714 100644 --- a/OpenTabletDriver.Desktop/DesktopDeviceConfigurationProvider.cs +++ b/OpenTabletDriver.Desktop/DesktopDeviceConfigurationProvider.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using OpenTabletDriver.Components; using OpenTabletDriver.Configurations; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Desktop.Interop.AppInfo; +using OpenTabletDriver.Tablet; #nullable enable @@ -14,6 +14,12 @@ namespace OpenTabletDriver.Desktop public class DesktopDeviceConfigurationProvider : IDeviceConfigurationProvider { private readonly DeviceConfigurationProvider _inAssemblyConfigurationProvider = new(); + private readonly IAppInfo _appInfo; + + public DesktopDeviceConfigurationProvider(IAppInfo appInfo) + { + _appInfo = appInfo; + } public IEnumerable TabletConfigurations => GetTabletConfigurations(); @@ -21,10 +27,10 @@ private IEnumerable GetTabletConfigurations() { IEnumerable<(ConfigurationSource, TabletConfiguration)> jsonConfigurations = Array.Empty<(ConfigurationSource, TabletConfiguration)>(); - if (Directory.Exists(AppInfo.Current.ConfigurationDirectory)) + if (Directory.Exists(_appInfo.ConfigurationDirectory)) { - Log.Write("Detect", $"Configuration overrides exist in '{AppInfo.Current.ConfigurationDirectory}', overriding built-in configurations.", LogLevel.Debug); - var files = Directory.EnumerateFiles(AppInfo.Current.ConfigurationDirectory, "*.json", SearchOption.AllDirectories); + Log.Write("Detect", $"Configuration overrides exist in '{_appInfo.ConfigurationDirectory}', overriding built-in configurations.", LogLevel.Debug); + var files = Directory.EnumerateFiles(_appInfo.ConfigurationDirectory, "*.json", SearchOption.AllDirectories); jsonConfigurations = files.Select(path => Serialization.Deserialize(File.OpenRead(path))) .Select(jsonConfig => (ConfigurationSource.File, jsonConfig)); diff --git a/OpenTabletDriver.Desktop/DesktopReportParserProvider.cs b/OpenTabletDriver.Desktop/DesktopReportParserProvider.cs index bdb1cbc71..7b5703f41 100644 --- a/OpenTabletDriver.Desktop/DesktopReportParserProvider.cs +++ b/OpenTabletDriver.Desktop/DesktopReportParserProvider.cs @@ -1,14 +1,21 @@ +using OpenTabletDriver.Components; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop { public class DesktopReportParserProvider : IReportParserProvider { + private readonly IPluginFactory _pluginFactory; + + public DesktopReportParserProvider(IPluginFactory pluginFactory) + { + _pluginFactory = pluginFactory; + } + public IReportParser GetReportParser(string reportParserName) { - return AppInfo.PluginManager.ConstructObject>(reportParserName); + return _pluginFactory.Construct>(reportParserName); } } } diff --git a/OpenTabletDriver.Desktop/DesktopServiceCollection.cs b/OpenTabletDriver.Desktop/DesktopServiceCollection.cs new file mode 100644 index 000000000..43629dac8 --- /dev/null +++ b/OpenTabletDriver.Desktop/DesktopServiceCollection.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Octokit; +using OpenTabletDriver.ComponentProviders; +using OpenTabletDriver.Components; +using OpenTabletDriver.Configurations; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Interop; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + using static ServiceDescriptor; + + public class DesktopServiceCollection : ServiceCollection + { + private static readonly IEnumerable RequiredServices = new[] + { + // Core Services + Singleton(), + Singleton(), + Singleton(p => new DeviceHubsProvider(p)), + Singleton(RootHub.WithProvider), + Singleton(), + Singleton(), + // Desktop Services + Singleton(new GitHubClient(ProductHeaderValue.Parse("OpenTabletDriver"))), + Transient(), + Singleton(), + Singleton(), + Singleton(), + Singleton() + }; + + public DesktopServiceCollection() + { + this.AddServices(RequiredServices); + } + + protected DesktopServiceCollection(IEnumerable overridingServices) : this() + { + this.AddServices(overridingServices); + } + + public static DesktopServiceCollection GetPlatformServiceCollection() + { + return SystemInterop.CurrentPlatform switch + { + SystemPlatform.Windows => new DesktopWindowsServiceCollection(), + SystemPlatform.Linux => new DesktopLinuxServiceCollection(), + SystemPlatform.MacOS => new DesktopMacOSServiceCollection(), + _ => throw new PlatformNotSupportedException("This platform is not supported by OpenTabletDriver.") + }; + } + } +} diff --git a/OpenTabletDriver.Desktop/Diagnostics/DiagnosticInfo.cs b/OpenTabletDriver.Desktop/Diagnostics/DiagnosticInfo.cs index 31e7b3052..d4238d4f4 100644 --- a/OpenTabletDriver.Desktop/Diagnostics/DiagnosticInfo.cs +++ b/OpenTabletDriver.Desktop/Diagnostics/DiagnosticInfo.cs @@ -1,59 +1,49 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Logging; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Logging; + +#nullable enable namespace OpenTabletDriver.Desktop.Diagnostics { - public class DiagnosticInfo + public class DiagnosticInfo : IDiagnosticInfo { - public DiagnosticInfo(IEnumerable log, IEnumerable devices) + public DiagnosticInfo( + IEnumerable log, + IEnumerable devices, + EnvironmentDictionary environmentDictionary + ) { ConsoleLog = log; Devices = devices; + EnvironmentVariables = environmentDictionary; } - [JsonProperty("App Version")] - public string AppVersion { private set; get; } = GetAppVersion(); - - [JsonProperty("Build Date")] - public string BuildDate { private set; get; } = typeof(BuildDateAttribute).Assembly.GetCustomAttribute().BuildDate; - - [JsonProperty("Operating System")] - public OperatingSystem OperatingSystem { private set; get; } = Environment.OSVersion; - - [JsonProperty("Environment Variables")] - public IDictionary EnvironmentVariables { private set; get; } = new EnvironmentDictionary(); - - [JsonProperty("HID Devices")] - public IEnumerable Devices { private set; get; } - - [JsonProperty("Console Log")] - public IEnumerable ConsoleLog { private set; get; } + public string AppVersion { get; } = GetAppVersion(); + public string BuildDate { get; } = typeof(BuildDateAttribute).Assembly.GetCustomAttribute()?.BuildDate ?? string.Empty; + public OperatingSystem OperatingSystem { get; } = Environment.OSVersion; + public IDictionary EnvironmentVariables { get; } + public IEnumerable Devices { get; } + public IEnumerable ConsoleLog { get; } private static string GetAppVersion() { - string version = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; + var version = Assembly.GetEntryAssembly()!.GetCustomAttribute()!.InformationalVersion; return $"OpenTabletDriver v{version}"; } - [OnError] - internal void OnError(StreamingContext _, ErrorContext errorContext) - { - errorContext.Handled = true; - Log.Write("Diagnostics", $"Handled diagnostics serialization error", LogLevel.Error); - Log.Exception(errorContext.Error); - } - public override string ToString() { - return JsonConvert.SerializeObject(this, Formatting.Indented); + using (var memoryStream = new MemoryStream()) + using (var sr = new StreamReader(memoryStream)) + { + Serialization.Serialize(memoryStream, this); + return sr.ReadToEnd(); + } } } } diff --git a/OpenTabletDriver.Desktop/Diagnostics/EnvironmentDictionary.cs b/OpenTabletDriver.Desktop/Diagnostics/EnvironmentDictionary.cs index bb7eddbb9..ea3b51458 100644 --- a/OpenTabletDriver.Desktop/Diagnostics/EnvironmentDictionary.cs +++ b/OpenTabletDriver.Desktop/Diagnostics/EnvironmentDictionary.cs @@ -1,32 +1,34 @@ using System; using System.Collections.Generic; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin; namespace OpenTabletDriver.Desktop.Diagnostics { public class EnvironmentDictionary : Dictionary { + private static readonly string[] EnvironmentVariables = + { + "USER", + "TEMP", + "TMP", + "TMPDIR" + }; + public EnvironmentDictionary() { - AddVariable("USER", "TEMP", "TMP", "TMPDIR"); - switch (DesktopInterop.CurrentPlatform) - { - case PluginPlatform.Linux: - AddVariable("DISPLAY", "WAYLAND_DISPLAY", "PWD", "PATH"); - break; - case PluginPlatform.Windows: - AddVariable("USERPROFILE"); - break; - } + AddVariables(EnvironmentVariables); + } + + protected EnvironmentDictionary(string[] additionalVariables) : this() + { + AddVariables(additionalVariables); } - private void AddVariable(params string[] variables) + private void AddVariables(string[] variables) { foreach (var variable in variables) { var value = Environment.GetEnvironmentVariable(variable); - base.Add(variable, value); + Add(variable, value); } } } diff --git a/OpenTabletDriver.Desktop/Diagnostics/IDiagnosticInfo.cs b/OpenTabletDriver.Desktop/Diagnostics/IDiagnosticInfo.cs new file mode 100644 index 000000000..471d881d0 --- /dev/null +++ b/OpenTabletDriver.Desktop/Diagnostics/IDiagnosticInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Logging; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Diagnostics +{ + [PublicAPI] + public interface IDiagnosticInfo + { + string AppVersion { get; } + string BuildDate { get; } + OperatingSystem OperatingSystem { get; } + IDictionary EnvironmentVariables { get; } + IEnumerable Devices { get; } + IEnumerable ConsoleLog { get; } + } +} diff --git a/OpenTabletDriver.Desktop/Diagnostics/LinuxEnvironmentDictionary.cs b/OpenTabletDriver.Desktop/Diagnostics/LinuxEnvironmentDictionary.cs new file mode 100644 index 000000000..00f7db1fd --- /dev/null +++ b/OpenTabletDriver.Desktop/Diagnostics/LinuxEnvironmentDictionary.cs @@ -0,0 +1,17 @@ +namespace OpenTabletDriver.Desktop.Diagnostics +{ + public sealed class LinuxEnvironmentDictionary : EnvironmentDictionary + { + private static readonly string[] EnvironmentVariables = new[] + { + "DISPLAY", + "WAYLAND_DISPLAY", + "PWD", + "PATH" + }; + + public LinuxEnvironmentDictionary() : base(EnvironmentVariables) + { + } + } +} diff --git a/OpenTabletDriver.Desktop/Diagnostics/WindowsEnvironmentDictionary.cs b/OpenTabletDriver.Desktop/Diagnostics/WindowsEnvironmentDictionary.cs new file mode 100644 index 000000000..a6afc65a0 --- /dev/null +++ b/OpenTabletDriver.Desktop/Diagnostics/WindowsEnvironmentDictionary.cs @@ -0,0 +1,17 @@ +namespace OpenTabletDriver.Desktop.Diagnostics +{ + public sealed class WindowsEnvironmentDictionary : EnvironmentDictionary + { + private static readonly string[] EnvironmentVariables = new[] + { + "USER", + "TEMP", + "TMP", + "TMPDIR" + }; + + public WindowsEnvironmentDictionary() : base(EnvironmentVariables) + { + } + } +} diff --git a/OpenTabletDriver.Desktop/HostBuilder.cs b/OpenTabletDriver.Desktop/HostBuilder.cs new file mode 100644 index 000000000..54a5de763 --- /dev/null +++ b/OpenTabletDriver.Desktop/HostBuilder.cs @@ -0,0 +1,51 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public abstract class HostBuilder : IHostBuilder + { + private readonly IServiceCollection _serviceCollection; + + protected HostBuilder(IServiceCollection serviceCollection) + { + _serviceCollection = serviceCollection; + } + + private bool _built; + + public virtual IHostBuilder ConfigureServices(Action configure) + { + if (_built) + throw new InvalidOperationException("Cannot configure services after the host has been built."); + + configure(_serviceCollection); + return this; + } + + protected IServiceProvider BuildServiceProvider() + { + if (_built) + throw new InvalidOperationException("Cannot build service from the same service collection more than once."); + +#if DEBUG + var serviceProvider = _serviceCollection.BuildServiceProvider(new ServiceProviderOptions() + { + ValidateScopes = true, + ValidateOnBuild = true + }); +#else + var serviceProvider = _serviceCollection.BuildServiceProvider(); +#endif + _built = true; + + return serviceProvider; + } + + public abstract THost Build(out IServiceProvider serviceProvider); + + public virtual THost Build() => Build(out _); + } +} diff --git a/OpenTabletDriver.Desktop/IApplicationLifetime.cs b/OpenTabletDriver.Desktop/IApplicationLifetime.cs new file mode 100644 index 000000000..5e5141cd6 --- /dev/null +++ b/OpenTabletDriver.Desktop/IApplicationLifetime.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public interface IApplicationLifetime + { + Task Run(string[] args); + } +} diff --git a/OpenTabletDriver.Desktop/IHostBuilder.cs b/OpenTabletDriver.Desktop/IHostBuilder.cs new file mode 100644 index 000000000..adc57e029 --- /dev/null +++ b/OpenTabletDriver.Desktop/IHostBuilder.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public interface IHostBuilder + { + IHostBuilder ConfigureServices(Action configure); + + /// + /// Builds an instance of . + /// + /// The final service provider associated to the . + /// The built host with its lifetime managed by . + THost Build(out IServiceProvider serviceProvider); + } +} diff --git a/OpenTabletDriver.Desktop/IPresetManager.cs b/OpenTabletDriver.Desktop/IPresetManager.cs new file mode 100644 index 000000000..0a42d61ea --- /dev/null +++ b/OpenTabletDriver.Desktop/IPresetManager.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace OpenTabletDriver.Desktop +{ + public interface IPresetManager + { + IReadOnlyCollection GetPresets(); + Preset FindPreset(string presetName); + void Refresh(); + void Save(string name, Settings settings); + } +} diff --git a/OpenTabletDriver.Desktop/ISettingsManager.cs b/OpenTabletDriver.Desktop/ISettingsManager.cs new file mode 100644 index 000000000..a1f6d342b --- /dev/null +++ b/OpenTabletDriver.Desktop/ISettingsManager.cs @@ -0,0 +1,17 @@ +using System.IO; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public interface ISettingsManager + { + Settings Settings { set; get; } + + bool Load(); + void Save(); + + bool Load(FileInfo file); + void Save(FileInfo file); + } +} diff --git a/OpenTabletDriver.Desktop/Interop/AppInfo/AppInfo.cs b/OpenTabletDriver.Desktop/Interop/AppInfo/AppInfo.cs new file mode 100644 index 000000000..a75e97224 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/AppInfo/AppInfo.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using OpenTabletDriver.Interop; + +namespace OpenTabletDriver.Desktop.Interop.AppInfo +{ + public class AppInfo : IAppInfo + { + private string _binaryDirectory, + _configurationDirectory, + _settingsFile, + _pluginDirectory, + _presetDirectory, + _temporaryDirectory, + _cacheDirectory, + _backupDirectory, + _trashDirectory; + + public string AppDataDirectory { set; get; } + + public string BinaryDirectory + { + set => _binaryDirectory = value; + get => _binaryDirectory ?? GetDefaultBinaryDirectory(); + } + + public string ConfigurationDirectory + { + set => _configurationDirectory = value; + get => _configurationDirectory ?? GetDefaultConfigurationDirectory(); + } + + public string SettingsFile + { + set => _settingsFile = value; + get => _settingsFile ?? GetDefaultSettingsFile(); + } + + public string PluginDirectory + { + set => _pluginDirectory = value; + get => _pluginDirectory ?? GetDefaultPluginDirectory(); + } + + public string PresetDirectory + { + set => _presetDirectory = value; + get => _presetDirectory ?? GetDefaultPresetDirectory(); + } + + public string TemporaryDirectory + { + set => _temporaryDirectory = value; + get => _temporaryDirectory ?? GetDefaultTemporaryDirectory(); + } + + public string CacheDirectory + { + set => _cacheDirectory = value; + get => _cacheDirectory ?? GetDefaultCacheDirectory(); + } + + public string BackupDirectory + { + set => _backupDirectory = value; + get => _backupDirectory ?? GetDefaultBackupDirectory(); + } + + public string TrashDirectory + { + set => _trashDirectory = value; + get => _trashDirectory ?? GetDefaultTrashDirectory(); + } + + public static string ProgramDirectory => AppContext.BaseDirectory; + + public static IAppInfo GetPlatformAppInfo() + { + return SystemInterop.CurrentPlatform switch + { + SystemPlatform.Windows => new WindowsAppInfo(), + SystemPlatform.Linux => new LinuxAppInfo(), + SystemPlatform.MacOS => new MacOSAppInfo(), + _ => throw new PlatformNotSupportedException("This platform is not supported by OpenTabletDriver.") + }; + } + + private string GetDefaultBinaryDirectory() => AppDomain.CurrentDomain.BaseDirectory; + + private string GetDefaultConfigurationDirectory() => FileUtilities.GetExistingPathOrLast( + Path.Join(AppDataDirectory, "Configurations"), + Path.Join(ProgramDirectory, "Configurations"), + Path.Join(System.Environment.CurrentDirectory, "Configurations") + ); + + private string GetDefaultSettingsFile() => Path.Join(AppDataDirectory, "settings.json"); + private string GetDefaultPluginDirectory() => Path.Join(AppDataDirectory, "Plugins"); + private string GetDefaultPresetDirectory() => Path.Join(AppDataDirectory, "Presets"); + private string GetDefaultTemporaryDirectory() => Path.Join(AppDataDirectory, "Temp"); + private string GetDefaultCacheDirectory() => Path.Join(AppDataDirectory, "Cache"); + private string GetDefaultBackupDirectory() => Path.Join(AppDataDirectory, "Backup"); + private string GetDefaultTrashDirectory() => Path.Join(AppDataDirectory, "Trash"); + } +} diff --git a/OpenTabletDriver.Desktop/Interop/AppInfo/IAppInfo.cs b/OpenTabletDriver.Desktop/Interop/AppInfo/IAppInfo.cs new file mode 100644 index 000000000..42fe99a62 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/AppInfo/IAppInfo.cs @@ -0,0 +1,18 @@ +#nullable enable + +namespace OpenTabletDriver.Desktop.Interop.AppInfo +{ + public interface IAppInfo + { + string ConfigurationDirectory { set; get; } + string SettingsFile { set; get; } + string BinaryDirectory { set; get; } + string PluginDirectory { set; get; } + string PresetDirectory { set; get; } + string TemporaryDirectory { set; get; } + string CacheDirectory { set; get; } + string BackupDirectory { set; get; } + string TrashDirectory { set; get; } + string AppDataDirectory { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/AppInfo/LinuxAppInfo.cs b/OpenTabletDriver.Desktop/Interop/AppInfo/LinuxAppInfo.cs new file mode 100644 index 000000000..43eaacf57 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/AppInfo/LinuxAppInfo.cs @@ -0,0 +1,15 @@ +namespace OpenTabletDriver.Desktop.Interop.AppInfo +{ + using static FileUtilities; + + public class LinuxAppInfo : AppInfo + { + public LinuxAppInfo() + { + ConfigurationDirectory = GetExistingPath("$XDG_DATA_HOME/OpenTabletDriver/Configurations", "~/.local/share/OpenTabletDriver/Configurations"); + AppDataDirectory = GetPath("$XDG_CONFIG_HOME/OpenTabletDriver", "~/.config/OpenTabletDriver"); + TemporaryDirectory = GetPath("$XDG_RUNTIME_DIR/OpenTabletDriver", "$TEMP/OpenTabletDriver"); + CacheDirectory = GetPath("$XDG_CACHE_HOME/OpenTabletDriver", "~/.cache/OpenTabletDriver"); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/AppInfo/MacOSAppInfo.cs b/OpenTabletDriver.Desktop/Interop/AppInfo/MacOSAppInfo.cs new file mode 100644 index 000000000..2913dd9d4 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/AppInfo/MacOSAppInfo.cs @@ -0,0 +1,14 @@ +namespace OpenTabletDriver.Desktop.Interop.AppInfo +{ + using static FileUtilities; + + public class MacOSAppInfo : AppInfo + { + public MacOSAppInfo() + { + AppDataDirectory = GetPath("~/Library/Application Support/OpenTabletDriver"); + TemporaryDirectory = GetPath("$TMPDIR/OpenTabletDriver"); + CacheDirectory = GetPath("~/Library/Caches/OpenTabletDriver"); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/AppInfo/WindowsAppInfo.cs b/OpenTabletDriver.Desktop/Interop/AppInfo/WindowsAppInfo.cs new file mode 100644 index 000000000..fb43ffc85 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/AppInfo/WindowsAppInfo.cs @@ -0,0 +1,14 @@ +using System.IO; + +namespace OpenTabletDriver.Desktop.Interop.AppInfo +{ + using static FileUtilities; + + public class WindowsAppInfo : AppInfo + { + public WindowsAppInfo() + { + AppDataDirectory = GetExistingPathOrLast(Path.Join(ProgramDirectory, "userdata"), "$LOCALAPPDATA\\OpenTabletDriver"); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/DesktopInterop.cs b/OpenTabletDriver.Desktop/Interop/DesktopInterop.cs deleted file mode 100644 index 571cff14d..000000000 --- a/OpenTabletDriver.Desktop/Interop/DesktopInterop.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Diagnostics; -using OpenTabletDriver.Desktop.Interop.Display; -using OpenTabletDriver.Desktop.Interop.Input.Absolute; -using OpenTabletDriver.Desktop.Interop.Input.Keyboard; -using OpenTabletDriver.Desktop.Interop.Input.Relative; -using OpenTabletDriver.Desktop.Interop.Timer; -using OpenTabletDriver.Desktop.Updater; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Display; -using OpenTabletDriver.Plugin.Platform.Keyboard; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Timers; - -namespace OpenTabletDriver.Desktop.Interop -{ - public class DesktopInterop : SystemInterop - { - protected DesktopInterop() - { - } - - private static IUpdater updater; - private static IVirtualScreen virtualScreen; - private static IAbsolutePointer absolutePointer; - private static IRelativePointer relativePointer; - private static IPressureHandler virtualTablet; - private static IVirtualKeyboard virtualKeyboard; - - public static void Open(string path) - { - switch (CurrentPlatform) - { - case PluginPlatform.Windows: - var startInfo = new ProcessStartInfo("cmd", $"/c start {path.Replace("&", "^&")}") - { - CreateNoWindow = true - }; - Process.Start(startInfo); - break; - case PluginPlatform.Linux: - Process.Start("xdg-open", $"\"{path}\""); - break; - case PluginPlatform.MacOS: - case PluginPlatform.FreeBSD: - Process.Start("open", $"\"{path}\""); - break; - } - } - - public static void OpenFolder(string path) - { - switch (CurrentPlatform) - { - case PluginPlatform.Windows: - Process.Start("explorer", $"\"{path.Replace("&", "^&")}\""); - break; - default: - Open(path); - break; - } - } - - public static IUpdater Updater => CurrentPlatform switch - { - PluginPlatform.Windows => updater ??= new WindowsUpdater(), - PluginPlatform.MacOS => updater ??= new MacOSUpdater(), - _ => null - }; - - public static ITimer Timer => CurrentPlatform switch - { - PluginPlatform.Windows => new WindowsTimer(), - PluginPlatform.Linux => new LinuxTimer(), - _ => new FallbackTimer() - }; - - public static IAbsolutePointer AbsolutePointer => CurrentPlatform switch - { - PluginPlatform.Windows => new WindowsAbsolutePointer(), - PluginPlatform.Linux => absolutePointer ??= new EvdevAbsolutePointer(), - PluginPlatform.MacOS => new MacOSAbsolutePointer(), - _ => null - }; - - public static IRelativePointer RelativePointer => CurrentPlatform switch - { - PluginPlatform.Windows => new WindowsRelativePointer(), - PluginPlatform.Linux => relativePointer ??= new EvdevRelativePointer(), - PluginPlatform.MacOS => new MacOSRelativePointer(), - _ => null - }; - - public static IPressureHandler VirtualTablet => CurrentPlatform switch - { - PluginPlatform.Linux => virtualTablet ??= new EvdevVirtualTablet(), - _ => null - }; - - public static IVirtualKeyboard VirtualKeyboard => CurrentPlatform switch - { - PluginPlatform.Windows => new WindowsVirtualKeyboard(), - PluginPlatform.Linux => virtualKeyboard ??= new EvdevVirtualKeyboard(), - PluginPlatform.MacOS => new MacOSVirtualKeyboard(), - _ => null - }; - - public static IVirtualScreen VirtualScreen => virtualScreen ??= CurrentPlatform switch - { - PluginPlatform.Windows => new WindowsDisplay(), - PluginPlatform.Linux => ConstructLinuxDisplay(), - PluginPlatform.MacOS => new MacOSDisplay(), - _ => null - }; - - private static IVirtualScreen ConstructLinuxDisplay() - { - if (Environment.GetEnvironmentVariable("WAYLAND_DISPLAY") != null) - return new WaylandDisplay(); - else if (Environment.GetEnvironmentVariable("DISPLAY") != null) - return new XScreen(); - - Log.Write("Display", "Neither Wayland nor X11 were detected, defaulting to X11.", LogLevel.Warning); - return new XScreen(); - } - } -} diff --git a/OpenTabletDriver.Desktop/Interop/DesktopLinuxServiceCollection.cs b/OpenTabletDriver.Desktop/Interop/DesktopLinuxServiceCollection.cs new file mode 100644 index 000000000..8c33e7c38 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/DesktopLinuxServiceCollection.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop.Display; +using OpenTabletDriver.Desktop.Interop.Environment; +using OpenTabletDriver.Desktop.Interop.Input.Absolute; +using OpenTabletDriver.Desktop.Interop.Input.Keyboard; +using OpenTabletDriver.Desktop.Interop.Input.Relative; +using OpenTabletDriver.Desktop.Interop.Timer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.Platform.Pointer; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Interop +{ + using static ServiceDescriptor; + + public sealed class DesktopLinuxServiceCollection : DesktopServiceCollection + { + private static readonly IEnumerable PlatformRequiredServices = new[] + { + Transient(), + Transient(), + Transient(), + Singleton(), + Singleton(), + Singleton(), + Singleton(), + Singleton(), + GetVirtualScreen() + }; + + public DesktopLinuxServiceCollection() : base(PlatformRequiredServices) + { + } + + private static ServiceDescriptor GetVirtualScreen() + { + if (HasEnvironmentVariable("WAYLAND_DISPLAY")) + return Singleton(); + if (HasEnvironmentVariable("DISPLAY")) + return Singleton(); + + Log.Write("Display", "Neither Wayland nor X11 were detected, defaulting to X11.", LogLevel.Warning); + return Singleton(); + } + + private static bool HasEnvironmentVariable(string variable) + { + return !string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(variable)); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/DesktopMacOSServiceCollection.cs b/OpenTabletDriver.Desktop/Interop/DesktopMacOSServiceCollection.cs new file mode 100644 index 000000000..d5ec2f9d1 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/DesktopMacOSServiceCollection.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop.Interop.Display; +using OpenTabletDriver.Desktop.Interop.Environment; +using OpenTabletDriver.Desktop.Interop.Input.Absolute; +using OpenTabletDriver.Desktop.Interop.Input.Keyboard; +using OpenTabletDriver.Desktop.Interop.Input.Relative; +using OpenTabletDriver.Desktop.Interop.Timer; +using OpenTabletDriver.Desktop.Updater; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Environment; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.Platform.Pointer; + +namespace OpenTabletDriver.Desktop.Interop +{ + using static ServiceDescriptor; + + public sealed class DesktopMacOSServiceCollection : DesktopServiceCollection + { + private static readonly IEnumerable PlatformRequiredServices = new[] + { + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Transient() + }; + + public DesktopMacOSServiceCollection() : base(PlatformRequiredServices) + { + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/DesktopWindowsServiceCollection.cs b/OpenTabletDriver.Desktop/Interop/DesktopWindowsServiceCollection.cs new file mode 100644 index 000000000..e5ee6b886 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/DesktopWindowsServiceCollection.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop.Display; +using OpenTabletDriver.Desktop.Interop.Environment; +using OpenTabletDriver.Desktop.Interop.Input.Absolute; +using OpenTabletDriver.Desktop.Interop.Input.Keyboard; +using OpenTabletDriver.Desktop.Interop.Input.Relative; +using OpenTabletDriver.Desktop.Interop.Timer; +using OpenTabletDriver.Desktop.Updater; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.Platform.Pointer; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Interop +{ + using static ServiceDescriptor; + + public sealed class DesktopWindowsServiceCollection : DesktopServiceCollection + { + private static readonly IEnumerable PlatformRequiredServices = new[] + { + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Transient(), + Singleton(), + Transient(), + Transient() + }; + + public DesktopWindowsServiceCollection() : base(PlatformRequiredServices) + { + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Display/Display.cs b/OpenTabletDriver.Desktop/Interop/Display/Display.cs index 5ad8cdc8d..578d48e3d 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/Display.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/Display.cs @@ -1,5 +1,5 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; namespace OpenTabletDriver.Desktop.Interop.Display { diff --git a/OpenTabletDriver.Desktop/Interop/Display/MacOSDisplay.cs b/OpenTabletDriver.Desktop/Interop/Display/MacOSDisplay.cs index 57dd137f8..9dce34351 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/MacOSDisplay.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/MacOSDisplay.cs @@ -4,7 +4,7 @@ using System.Numerics; using OpenTabletDriver.Native.OSX; using OpenTabletDriver.Native.OSX.Generic; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; namespace OpenTabletDriver.Desktop.Interop.Display { diff --git a/OpenTabletDriver.Desktop/Interop/Display/WaylandDisplay.cs b/OpenTabletDriver.Desktop/Interop/Display/WaylandDisplay.cs index de073ffb4..2869f9134 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/WaylandDisplay.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/WaylandDisplay.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; using WaylandNET.Client; using WaylandNET.Client.Protocol; @@ -10,7 +10,7 @@ namespace OpenTabletDriver.Desktop.Interop.Display { public class WaylandDisplay : IVirtualScreen { - private List _outputs; + private readonly List _outputs; public WaylandDisplay() { @@ -24,9 +24,11 @@ public WaylandDisplay() switch (@interface) { case "wl_output": - var output = new WaylandOutput(); - output.Index = _outputs.Count + 1; - output.WlOutput = wlRegistry.Bind(name, @interface, 1); + var output = new WaylandOutput + { + Index = _outputs.Count + 1, + WlOutput = wlRegistry.Bind(name, @interface, 1) + }; output.WlOutput.Geometry += (wlOutput, x, y, physicalWidth, physicalHeight, subpixel, make, model, transform) => { if (output.XdgOutput == null || output.XdgOutput.Version < 2) @@ -80,7 +82,7 @@ public WaylandDisplay() } } - public IEnumerable Displays => new IDisplay[] { this }.Concat(_outputs); + public IEnumerable Displays => _outputs.Append(this); public int Index => 0; diff --git a/OpenTabletDriver.Desktop/Interop/Display/WaylandOutput.cs b/OpenTabletDriver.Desktop/Interop/Display/WaylandOutput.cs index 0afa045de..2d7bf2e8e 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/WaylandOutput.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/WaylandOutput.cs @@ -1,5 +1,5 @@ using System.Numerics; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; using WaylandNET.Client.Protocol; namespace OpenTabletDriver.Desktop.Interop.Display diff --git a/OpenTabletDriver.Desktop/Interop/Display/WindowsDisplay.cs b/OpenTabletDriver.Desktop/Interop/Display/WindowsDisplay.cs index a0d46a2ea..fd169c8fb 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/WindowsDisplay.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/WindowsDisplay.cs @@ -4,8 +4,7 @@ using System.Numerics; using System.Runtime.InteropServices; using OpenTabletDriver.Native.Windows; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; namespace OpenTabletDriver.Desktop.Interop.Display { diff --git a/OpenTabletDriver.Desktop/Interop/Display/XScreen.cs b/OpenTabletDriver.Desktop/Interop/Display/XScreen.cs index 9030c5a98..9cfbdb538 100644 --- a/OpenTabletDriver.Desktop/Interop/Display/XScreen.cs +++ b/OpenTabletDriver.Desktop/Interop/Display/XScreen.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Numerics; using OpenTabletDriver.Native.Linux.Xorg; -using OpenTabletDriver.Plugin.Platform.Display; +using OpenTabletDriver.Platform.Display; namespace OpenTabletDriver.Desktop.Interop.Display { @@ -11,12 +11,12 @@ namespace OpenTabletDriver.Desktop.Interop.Display using static XRandr; using Window = IntPtr; - public class XScreen : IVirtualScreen, IDisposable + public sealed class XScreen : IVirtualScreen, IDisposable { public unsafe XScreen() { - Display = XOpenDisplay(null); - RootWindow = XDefaultRootWindow(Display); + _display = XOpenDisplay(null); + _rootWindow = XDefaultRootWindow(_display); var monitors = GetXRandrDisplays().ToList(); var primary = monitors.FirstOrDefault(d => d.Primary != 0); @@ -37,31 +37,31 @@ public unsafe XScreen() Position = new Vector2(primary.X, primary.Y); } - private Window Display; - private Window RootWindow; + private readonly Window _display; + private readonly Window _rootWindow; public float Width { - get => XDisplayWidth(Display, 0); + get => XDisplayWidth(_display, 0); } public float Height { - get => XDisplayHeight(Display, 0); + get => XDisplayHeight(_display, 0); } - public Vector2 Position { private set; get; } = new Vector2(0, 0); + public Vector2 Position { get; } private unsafe IEnumerable GetXRandrDisplays() { ICollection monitors = new List(); - var xRandrMonitors = XRRGetMonitors(Display, RootWindow, true, out var count); + var xRandrMonitors = XRRGetMonitors(_display, _rootWindow, true, out var count); for (int i = 0; i < count; i++) monitors.Add(xRandrMonitors[i]); return monitors; } - public IEnumerable Displays { private set; get; } + public IEnumerable Displays { get; } public int Index => 0; @@ -72,7 +72,7 @@ public override string ToString() public void Dispose() { - XCloseDisplay(Display); + XCloseDisplay(_display); } } } diff --git a/OpenTabletDriver.Desktop/Interop/Environment/EnvironmentHandler.cs b/OpenTabletDriver.Desktop/Interop/Environment/EnvironmentHandler.cs new file mode 100644 index 000000000..de935e2c3 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Environment/EnvironmentHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics; +using OpenTabletDriver.Platform.Environment; + +namespace OpenTabletDriver.Desktop.Interop.Environment +{ + public abstract class EnvironmentHandler : IEnvironmentHandler + { + public abstract void Open(string path); + public virtual void OpenFolder(string path) => Open(path); + + protected void Exec(string executable, string args) + { + try + { + Process.Start(executable, args); + } + catch (Exception e) + { + Log.Exception(e); + } + } + + protected void Exec(ProcessStartInfo startInfo) + { + try + { + Process.Start(startInfo); + } + catch (Exception e) + { + Log.Exception(e); + } + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Environment/LinuxEnvironmentHandler.cs b/OpenTabletDriver.Desktop/Interop/Environment/LinuxEnvironmentHandler.cs new file mode 100644 index 000000000..06f4ba6d8 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Environment/LinuxEnvironmentHandler.cs @@ -0,0 +1,10 @@ +namespace OpenTabletDriver.Desktop.Interop.Environment +{ + public class LinuxEnvironmentHandler : EnvironmentHandler + { + public override void Open(string path) + { + Exec("xdg-open", $"\"{path}\""); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Environment/MacOSEnvironmentHandler.cs b/OpenTabletDriver.Desktop/Interop/Environment/MacOSEnvironmentHandler.cs new file mode 100644 index 000000000..0480b2d2f --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Environment/MacOSEnvironmentHandler.cs @@ -0,0 +1,10 @@ +namespace OpenTabletDriver.Desktop.Interop.Environment +{ + public class MacOSEnvironmentHandler : EnvironmentHandler + { + public override void Open(string path) + { + Exec("open", $"\"{path}\""); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Environment/WindowsEnvironmentHandler.cs b/OpenTabletDriver.Desktop/Interop/Environment/WindowsEnvironmentHandler.cs new file mode 100644 index 000000000..d8349beb9 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Environment/WindowsEnvironmentHandler.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; + +namespace OpenTabletDriver.Desktop.Interop.Environment +{ + public class WindowsEnvironmentHandler : EnvironmentHandler + { + public override void Open(string path) + { + var startInfo = new ProcessStartInfo + { + FileName = "cmd", + Arguments = $"/c start {path.Replace("&", "^&")}", + CreateNoWindow = true + }; + Exec(startInfo); + } + + public override void OpenFolder(string path) + { + Exec("explorer", $"\"{path.Replace("&", "^&")}\""); + } + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevAbsolutePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevAbsolutePointer.cs index a41fcf85c..0cbe8f08f 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevAbsolutePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevAbsolutePointer.cs @@ -3,14 +3,16 @@ using OpenTabletDriver.Native.Linux; using OpenTabletDriver.Native.Linux.Evdev; using OpenTabletDriver.Native.Linux.Evdev.Structs; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; + +#nullable enable namespace OpenTabletDriver.Desktop.Interop.Input.Absolute { public class EvdevAbsolutePointer : EvdevVirtualMouse, IAbsolutePointer { - public unsafe EvdevAbsolutePointer() + public unsafe EvdevAbsolutePointer(IVirtualScreen virtualScreen) { Device = new EvdevDevice("OpenTabletDriver Virtual Tablet"); @@ -18,14 +20,14 @@ public unsafe EvdevAbsolutePointer() var xAbs = new input_absinfo { - maximum = (int)DesktopInterop.VirtualScreen.Width + maximum = (int)virtualScreen.Width }; input_absinfo* xPtr = &xAbs; Device.EnableCustomCode(EventType.EV_ABS, EventCode.ABS_X, (IntPtr)xPtr); var yAbs = new input_absinfo { - maximum = (int)DesktopInterop.VirtualScreen.Height + maximum = (int)virtualScreen.Height }; input_absinfo* yPtr = &yAbs; Device.EnableCustomCode(EventType.EV_ABS, EventCode.ABS_Y, (IntPtr)yPtr); diff --git a/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevVirtualTablet.cs b/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevVirtualTablet.cs index 9a22db1b3..e20f9a10a 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevVirtualTablet.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Absolute/EvdevVirtualTablet.cs @@ -3,8 +3,8 @@ using OpenTabletDriver.Native.Linux; using OpenTabletDriver.Native.Linux.Evdev; using OpenTabletDriver.Native.Linux.Evdev.Structs; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input.Absolute { @@ -15,7 +15,7 @@ public class EvdevVirtualTablet : EvdevVirtualMouse, IAbsolutePointer, IPressure private bool isEraser; private bool proximity = true; - public unsafe EvdevVirtualTablet() + public unsafe EvdevVirtualTablet(IVirtualScreen virtualScreen) { Device = new EvdevDevice("OpenTabletDriver Virtual Artist Tablet"); @@ -24,7 +24,7 @@ public unsafe EvdevVirtualTablet() var xAbs = new input_absinfo { - maximum = (int)(DesktopInterop.VirtualScreen.Width * RESOLUTION), + maximum = (int)(virtualScreen.Width * RESOLUTION), resolution = 100000 }; input_absinfo* xPtr = &xAbs; @@ -32,7 +32,7 @@ public unsafe EvdevVirtualTablet() var yAbs = new input_absinfo { - maximum = (int)(DesktopInterop.VirtualScreen.Height * RESOLUTION), + maximum = (int)(virtualScreen.Height * RESOLUTION), resolution = 100000 }; input_absinfo* yPtr = &yAbs; diff --git a/OpenTabletDriver.Desktop/Interop/Input/Absolute/MacOSAbsolutePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Absolute/MacOSAbsolutePointer.cs index 9cf280941..f8105bf18 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Absolute/MacOSAbsolutePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Absolute/MacOSAbsolutePointer.cs @@ -2,7 +2,8 @@ using System.Numerics; using OpenTabletDriver.Native.OSX; using OpenTabletDriver.Native.OSX.Generic; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input.Absolute { @@ -10,6 +11,10 @@ namespace OpenTabletDriver.Desktop.Interop.Input.Absolute public class MacOSAbsolutePointer : MacOSVirtualMouse, IAbsolutePointer { + public MacOSAbsolutePointer(IVirtualScreen virtualScreen) : base(virtualScreen) + { + } + public void SetPosition(Vector2 pos) { var newPos = new CGPoint(pos.X, pos.Y) - offset; diff --git a/OpenTabletDriver.Desktop/Interop/Input/Absolute/WindowsAbsolutePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Absolute/WindowsAbsolutePointer.cs index d7cb66e37..9802c6014 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Absolute/WindowsAbsolutePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Absolute/WindowsAbsolutePointer.cs @@ -2,38 +2,53 @@ using System.Numerics; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.Input; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; + +#nullable enable namespace OpenTabletDriver.Desktop.Interop.Input.Absolute { using static Windows; - public class WindowsAbsolutePointer : WindowsVirtualMouse, IAbsolutePointer + public class WindowsAbsolutePointer : WindowsVirtualMouse, IAbsolutePointer, IDisposable { - static WindowsAbsolutePointer() + public WindowsAbsolutePointer(IVirtualScreen virtualScreen) { timer = new System.Threading.Timer( - state => ScreenToVirtualDesktop = new Vector2( - DesktopInterop.VirtualScreen.Width, - DesktopInterop.VirtualScreen.Height - ) / 65535, - null, + UpdateVirtualDesktop, + virtualScreen, TimeSpan.Zero, - TimeSpan.FromSeconds(1) + TimeSpan.FromSeconds(5) ); } - private static System.Threading.Timer timer; - private static Vector2 ScreenToVirtualDesktop = new Vector2(DesktopInterop.VirtualScreen.Width, DesktopInterop.VirtualScreen.Height) / 65535; + private System.Threading.Timer timer; + private Vector2 screenToVirtualDesktop; + + private void UpdateVirtualDesktop(object? state) + { + var virtualScreen = (IVirtualScreen)state!; + + screenToVirtualDesktop = new Vector2( + virtualScreen.Width, + virtualScreen.Height + ) / 65535; + } public void SetPosition(Vector2 pos) { - var virtualDesktopCoords = pos / ScreenToVirtualDesktop; + var virtualDesktopCoords = pos / screenToVirtualDesktop; inputs[0].U.mi.dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.MOVE | MOUSEEVENTF.VIRTUALDESK; inputs[0].U.mi.dx = (int)virtualDesktopCoords.X; inputs[0].U.mi.dy = (int)virtualDesktopCoords.Y; SendInput(1, inputs, INPUT.Size); } + + public void Dispose() + { + timer.Dispose(); + } } } diff --git a/OpenTabletDriver.Desktop/Interop/Input/EvdevVirtualMouse.cs b/OpenTabletDriver.Desktop/Interop/Input/EvdevVirtualMouse.cs index 505b543e3..80556b8f5 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/EvdevVirtualMouse.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/EvdevVirtualMouse.cs @@ -1,7 +1,7 @@ using System; +using OpenTabletDriver.Attributes; using OpenTabletDriver.Native.Linux.Evdev; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop { diff --git a/OpenTabletDriver.Desktop/Interop/Input/InputDictionary.cs b/OpenTabletDriver.Desktop/Interop/Input/InputDictionary.cs index 3ab3eb5f4..fc25cb352 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/InputDictionary.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/InputDictionary.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Collections.Generic; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input { diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/EvdevVirtualKeyboard.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/EvdevVirtualKeyboard.cs index 973fbd075..2d67f0832 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/EvdevVirtualKeyboard.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/EvdevVirtualKeyboard.cs @@ -3,18 +3,24 @@ using System.Linq; using OpenTabletDriver.Native.Linux; using OpenTabletDriver.Native.Linux.Evdev; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Keyboard; +using OpenTabletDriver.Platform.Keyboard; namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard { public class EvdevVirtualKeyboard : IVirtualKeyboard, IDisposable { - public EvdevVirtualKeyboard() + private readonly IKeysProvider _keysProvider; + + public unsafe EvdevVirtualKeyboard(IKeysProvider keysProvider) { + _keysProvider = keysProvider; + Device = new EvdevDevice("OpenTabletDriver Virtual Keyboard"); - Device.EnableTypeCodes(EventType.EV_KEY, EtoKeysymToEventCode.Values.Distinct().ToArray()); + var keyCodes = _keysProvider.EtoToNative.Values.Distinct() + .Select(k => (EventCode)Convert.ToUInt32(k)) + .ToArray(); + Device.EnableTypeCodes(EventType.EV_KEY, keyCodes); var result = Device.Initialize(); switch (result) @@ -28,11 +34,13 @@ public EvdevVirtualKeyboard() } } - private EvdevDevice Device { set; get; } + private EvdevDevice Device { get; } + + public IEnumerable SupportedKeys => _keysProvider.EtoToNative.Keys; private void KeyEvent(string key, bool isPress) { - var keyEventCode = EtoKeysymToEventCode[key]; + var keyEventCode = (EventCode)_keysProvider.EtoToNative[key]; Device.Write(EventType.EV_KEY, keyEventCode, isPress ? 1 : 0); Device.Sync(); @@ -64,137 +72,5 @@ public void Dispose() { Device?.Dispose(); } - - public IEnumerable SupportedKeys => EtoKeysymToEventCode.Keys; - - internal static readonly Dictionary EtoKeysymToEventCode = new Dictionary - { - { "None", 0x0 }, - { "A", EventCode.KEY_A }, - { "B", EventCode.KEY_B }, - { "C", EventCode.KEY_C }, - { "D", EventCode.KEY_D }, - { "E", EventCode.KEY_E }, - { "F", EventCode.KEY_F }, - { "G", EventCode.KEY_G }, - { "H", EventCode.KEY_H }, - { "I", EventCode.KEY_I }, - { "J", EventCode.KEY_J }, - { "K", EventCode.KEY_K }, - { "L", EventCode.KEY_L }, - { "M", EventCode.KEY_M }, - { "N", EventCode.KEY_N }, - { "O", EventCode.KEY_O }, - { "P", EventCode.KEY_P }, - { "Q", EventCode.KEY_Q }, - { "R", EventCode.KEY_R }, - { "S", EventCode.KEY_S }, - { "T", EventCode.KEY_T }, - { "U", EventCode.KEY_U }, - { "V", EventCode.KEY_V }, - { "W", EventCode.KEY_W }, - { "X", EventCode.KEY_X }, - { "Y", EventCode.KEY_Y }, - { "Z", EventCode.KEY_Z }, - { "D0", EventCode.KEY_0 }, - { "D1", EventCode.KEY_1 }, - { "D2", EventCode.KEY_2 }, - { "D3", EventCode.KEY_3 }, - { "D4", EventCode.KEY_4 }, - { "D5", EventCode.KEY_5 }, - { "D6", EventCode.KEY_6 }, - { "D7", EventCode.KEY_7 }, - { "D8", EventCode.KEY_8 }, - { "D9", EventCode.KEY_9 }, - { "F1", EventCode.KEY_F1 }, - { "F2", EventCode.KEY_F2 }, - { "F3", EventCode.KEY_F3 }, - { "F4", EventCode.KEY_F4 }, - { "F5", EventCode.KEY_F5 }, - { "F6", EventCode.KEY_F6 }, - { "F7", EventCode.KEY_F7 }, - { "F8", EventCode.KEY_F8 }, - { "F9", EventCode.KEY_F9 }, - { "F10", EventCode.KEY_F10 }, - { "F11", EventCode.KEY_F11 }, - { "F12", EventCode.KEY_F12 }, - { "F13", EventCode.KEY_F13 }, - { "F14", EventCode.KEY_F14 }, - { "F15", EventCode.KEY_F15 }, - { "F16", EventCode.KEY_F16 }, - { "F17", EventCode.KEY_F17 }, - { "F18", EventCode.KEY_F18 }, - { "F19", EventCode.KEY_F19 }, - { "F20", EventCode.KEY_F20 }, - { "F21", EventCode.KEY_F21 }, - { "F22", EventCode.KEY_F22 }, - { "F23", EventCode.KEY_F23 }, - { "F24", EventCode.KEY_F24 }, - { "Minus", EventCode.KEY_MINUS }, - { "Grave", EventCode.KEY_GRAVE }, - { "Insert", EventCode.KEY_INSERT }, - { "Home", EventCode.KEY_HOME }, - { "PageUp", EventCode.KEY_PAGEUP }, - { "PageDown", EventCode.KEY_PAGEDOWN }, - { "Delete", EventCode.KEY_DELETE }, - { "End", EventCode.KEY_END }, - { "Divide", EventCode.KEY_SLASH }, - { "Decimal", EventCode.KEY_DOT }, - { "Backspace", EventCode.KEY_BACKSPACE }, - { "Up", EventCode.KEY_UP }, - { "Down", EventCode.KEY_DOWN }, - { "Left", EventCode.KEY_LEFT }, - { "Right", EventCode.KEY_RIGHT }, - { "Tab", EventCode.KEY_TAB }, - { "Space", EventCode.KEY_SPACE }, - { "CapsLock", EventCode.KEY_CAPSLOCK }, - { "ScrollLock", EventCode.KEY_SCROLLLOCK }, - { "PrintScreen", EventCode.KEY_PRINT }, - { "NumberLock", EventCode.KEY_NUMLOCK }, - { "Enter", EventCode.KEY_ENTER }, - { "Escape", EventCode.KEY_ESC }, - { "Multiply", EventCode.KEY_NUMERIC_STAR }, - { "Add", EventCode.KEY_EQUAL }, - { "Subtract", EventCode.KEY_KPMINUS }, - { "Help", EventCode.KEY_HELP }, - { "Pause", EventCode.KEY_PAUSE }, - { "Clear", EventCode.KEY_CLEAR }, - { "KeypadEqual", EventCode.KEY_KPEQUAL }, - { "Menu", EventCode.KEY_MENU }, - { "Backslash", EventCode.KEY_BACKSLASH }, - { "Plus", EventCode.KEY_KPPLUS }, - { "Equal", EventCode.KEY_EQUAL }, - { "Semicolon", EventCode.KEY_SEMICOLON }, - { "Quote", EventCode.KEY_APOSTROPHE }, - { "Comma", EventCode.KEY_COMMA }, - { "Period", EventCode.KEY_DOT }, - { "ForwardSlash", EventCode.KEY_SLASH }, - { "Slash", EventCode.KEY_SLASH }, - { "RightBracket", EventCode.KEY_RIGHTBRACE }, - { "LeftBracket", EventCode.KEY_LEFTBRACE }, - { "ContextMenu", EventCode.KEY_CONTEXT_MENU }, - { "Keypad0", EventCode.KEY_KP0 }, - { "Keypad1", EventCode.KEY_KP1 }, - { "Keypad2", EventCode.KEY_KP2 }, - { "Keypad3", EventCode.KEY_KP3 }, - { "Keypad4", EventCode.KEY_KP4 }, - { "Keypad5", EventCode.KEY_KP5 }, - { "Keypad6", EventCode.KEY_KP6 }, - { "Keypad7", EventCode.KEY_KP7 }, - { "Keypad8", EventCode.KEY_KP8 }, - { "Keypad9", EventCode.KEY_KP9 }, - { "LeftShift", EventCode.KEY_LEFTSHIFT }, - { "RightShift", EventCode.KEY_RIGHTSHIFT }, - { "LeftControl", EventCode.KEY_LEFTCTRL }, - { "RightControl", EventCode.KEY_RIGHTCTRL }, - { "LeftAlt", EventCode.KEY_LEFTALT }, - { "RightAlt", EventCode.KEY_RIGHTALT }, - { "LeftApplication", EventCode.KEY_LEFTMETA }, - { "RightApplication", EventCode.KEY_RIGHTMETA }, - { "Shift", EventCode.KEY_LEFTSHIFT }, - { "Alt", EventCode.KEY_LEFTALT }, - { "Control", EventCode.KEY_LEFTCTRL }, - { "Application", EventCode.KEY_LEFTMETA } - }; } } diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/LinuxKeysProvider.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/LinuxKeysProvider.cs new file mode 100644 index 000000000..178a7246b --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/LinuxKeysProvider.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using OpenTabletDriver.Native.Linux.Evdev; +using OpenTabletDriver.Platform.Keyboard; + +namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard +{ + public class LinuxKeysProvider : IKeysProvider + { + public IDictionary EtoToNative { get; } = new Dictionary + { + {"None", 0x0}, + {"A", EventCode.KEY_A}, + {"B", EventCode.KEY_B}, + {"C", EventCode.KEY_C}, + {"D", EventCode.KEY_D}, + {"E", EventCode.KEY_E}, + {"F", EventCode.KEY_F}, + {"G", EventCode.KEY_G}, + {"H", EventCode.KEY_H}, + {"I", EventCode.KEY_I}, + {"J", EventCode.KEY_J}, + {"K", EventCode.KEY_K}, + {"L", EventCode.KEY_L}, + {"M", EventCode.KEY_M}, + {"N", EventCode.KEY_N}, + {"O", EventCode.KEY_O}, + {"P", EventCode.KEY_P}, + {"Q", EventCode.KEY_Q}, + {"R", EventCode.KEY_R}, + {"S", EventCode.KEY_S}, + {"T", EventCode.KEY_T}, + {"U", EventCode.KEY_U}, + {"V", EventCode.KEY_V}, + {"W", EventCode.KEY_W}, + {"X", EventCode.KEY_X}, + {"Y", EventCode.KEY_Y}, + {"Z", EventCode.KEY_Z}, + {"D0", EventCode.KEY_0}, + {"D1", EventCode.KEY_1}, + {"D2", EventCode.KEY_2}, + {"D3", EventCode.KEY_3}, + {"D4", EventCode.KEY_4}, + {"D5", EventCode.KEY_5}, + {"D6", EventCode.KEY_6}, + {"D7", EventCode.KEY_7}, + {"D8", EventCode.KEY_8}, + {"D9", EventCode.KEY_9}, + {"F1", EventCode.KEY_F1}, + {"F2", EventCode.KEY_F2}, + {"F3", EventCode.KEY_F3}, + {"F4", EventCode.KEY_F4}, + {"F5", EventCode.KEY_F5}, + {"F6", EventCode.KEY_F6}, + {"F7", EventCode.KEY_F7}, + {"F8", EventCode.KEY_F8}, + {"F9", EventCode.KEY_F9}, + {"F10", EventCode.KEY_F10}, + {"F11", EventCode.KEY_F11}, + {"F12", EventCode.KEY_F12}, + {"Minus", EventCode.KEY_MINUS}, + {"Grave", EventCode.KEY_GRAVE}, + {"Insert", EventCode.KEY_INSERT}, + {"Home", EventCode.KEY_HOME}, + {"PageUp", EventCode.KEY_PAGEUP}, + {"PageDown", EventCode.KEY_PAGEDOWN}, + {"Delete", EventCode.KEY_DELETE}, + {"End", EventCode.KEY_END}, + {"Divide", EventCode.KEY_SLASH}, + {"Decimal", EventCode.KEY_DOT}, + {"Backspace", EventCode.KEY_BACKSPACE}, + {"Up", EventCode.KEY_UP}, + {"Down", EventCode.KEY_DOWN}, + {"Left", EventCode.KEY_LEFT}, + {"Right", EventCode.KEY_RIGHT}, + {"Tab", EventCode.KEY_TAB}, + {"Space", EventCode.KEY_SPACE}, + {"CapsLock", EventCode.KEY_CAPSLOCK}, + {"ScrollLock", EventCode.KEY_SCROLLLOCK}, + {"PrintScreen", EventCode.KEY_PRINT}, + {"NumberLock", EventCode.KEY_NUMLOCK}, + {"Enter", EventCode.KEY_ENTER}, + {"Escape", EventCode.KEY_ESC}, + {"Multiply", EventCode.KEY_NUMERIC_STAR}, + {"Add", EventCode.KEY_EQUAL}, + {"Subtract", EventCode.KEY_KPMINUS}, + {"Help", EventCode.KEY_HELP}, + {"Pause", EventCode.KEY_PAUSE}, + {"Clear", EventCode.KEY_CLEAR}, + {"KeypadEqual", EventCode.KEY_KPEQUAL}, + {"Menu", EventCode.KEY_MENU}, + {"Backslash", EventCode.KEY_BACKSLASH}, + {"Plus", EventCode.KEY_KPPLUS}, + {"Equal", EventCode.KEY_EQUAL}, + {"Semicolon", EventCode.KEY_SEMICOLON}, + {"Quote", EventCode.KEY_APOSTROPHE}, + {"Comma", EventCode.KEY_COMMA}, + {"Period", EventCode.KEY_DOT}, + {"ForwardSlash", EventCode.KEY_SLASH}, + {"Slash", EventCode.KEY_SLASH}, + {"RightBracket", EventCode.KEY_RIGHTBRACE}, + {"LeftBracket", EventCode.KEY_LEFTBRACE}, + {"ContextMenu", EventCode.KEY_CONTEXT_MENU}, + {"Keypad0", EventCode.KEY_KP0}, + {"Keypad1", EventCode.KEY_KP1}, + {"Keypad2", EventCode.KEY_KP2}, + {"Keypad3", EventCode.KEY_KP3}, + {"Keypad4", EventCode.KEY_KP4}, + {"Keypad5", EventCode.KEY_KP5}, + {"Keypad6", EventCode.KEY_KP6}, + {"Keypad7", EventCode.KEY_KP7}, + {"Keypad8", EventCode.KEY_KP8}, + {"Keypad9", EventCode.KEY_KP9}, + {"LeftShift", EventCode.KEY_LEFTSHIFT}, + {"RightShift", EventCode.KEY_RIGHTSHIFT}, + {"LeftControl", EventCode.KEY_LEFTCTRL}, + {"RightControl", EventCode.KEY_RIGHTCTRL}, + {"LeftAlt", EventCode.KEY_LEFTALT}, + {"RightAlt", EventCode.KEY_RIGHTALT}, + {"LeftApplication", EventCode.KEY_LEFTMETA}, + {"RightApplication", EventCode.KEY_RIGHTMETA}, + {"Shift", EventCode.KEY_LEFTSHIFT}, + {"Alt", EventCode.KEY_LEFTALT}, + {"Control", EventCode.KEY_LEFTCTRL}, + {"Application", EventCode.KEY_LEFTMETA} + }; + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSKeysProvider.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSKeysProvider.cs new file mode 100644 index 000000000..020b7da6f --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSKeysProvider.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using OpenTabletDriver.Native.OSX.Input; +using OpenTabletDriver.Platform.Keyboard; + +namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard +{ + public class MacOSKeysProvider : IKeysProvider + { + public IDictionary EtoToNative { get; } = new Dictionary + { + { "None", 0x00 }, + { "A", CGKeyCode.kVK_ANSI_A }, + { "B", CGKeyCode.kVK_ANSI_B }, + { "C", CGKeyCode.kVK_ANSI_C }, + { "D", CGKeyCode.kVK_ANSI_D }, + { "E", CGKeyCode.kVK_ANSI_E }, + { "F", CGKeyCode.kVK_ANSI_F }, + { "G", CGKeyCode.kVK_ANSI_G }, + { "H", CGKeyCode.kVK_ANSI_H }, + { "I", CGKeyCode.kVK_ANSI_I }, + { "J", CGKeyCode.kVK_ANSI_J }, + { "K", CGKeyCode.kVK_ANSI_K }, + { "L", CGKeyCode.kVK_ANSI_L }, + { "M", CGKeyCode.kVK_ANSI_M }, + { "N", CGKeyCode.kVK_ANSI_N }, + { "O", CGKeyCode.kVK_ANSI_O }, + { "P", CGKeyCode.kVK_ANSI_P }, + { "Q", CGKeyCode.kVK_ANSI_Q }, + { "R", CGKeyCode.kVK_ANSI_R }, + { "S", CGKeyCode.kVK_ANSI_S }, + { "T", CGKeyCode.kVK_ANSI_T }, + { "U", CGKeyCode.kVK_ANSI_U }, + { "V", CGKeyCode.kVK_ANSI_V }, + { "W", CGKeyCode.kVK_ANSI_W }, + { "X", CGKeyCode.kVK_ANSI_X }, + { "Y", CGKeyCode.kVK_ANSI_Y }, + { "Z", CGKeyCode.kVK_ANSI_Z }, + { "F1", CGKeyCode.kVK_F1 }, + { "F2", CGKeyCode.kVK_F2 }, + { "F3", CGKeyCode.kVK_F3 }, + { "F4", CGKeyCode.kVK_F4 }, + { "F5", CGKeyCode.kVK_F5 }, + { "F6", CGKeyCode.kVK_F6 }, + { "F7", CGKeyCode.kVK_F7 }, + { "F8", CGKeyCode.kVK_F8 }, + { "F9", CGKeyCode.kVK_F9 }, + { "F10", CGKeyCode.kVK_F10 }, + { "F11", CGKeyCode.kVK_F11 }, + { "F12", CGKeyCode.kVK_F12 }, + { "D0", CGKeyCode.kVK_ANSI_0 }, + { "D1", CGKeyCode.kVK_ANSI_1 }, + { "D2", CGKeyCode.kVK_ANSI_2 }, + { "D3", CGKeyCode.kVK_ANSI_3 }, + { "D4", CGKeyCode.kVK_ANSI_4 }, + { "D5", CGKeyCode.kVK_ANSI_5 }, + { "D6", CGKeyCode.kVK_ANSI_6 }, + { "D7", CGKeyCode.kVK_ANSI_7 }, + { "D8", CGKeyCode.kVK_ANSI_8 }, + { "D9", CGKeyCode.kVK_ANSI_9 }, + { "Minus", CGKeyCode.kVK_ANSI_Minus }, + { "Grave", CGKeyCode.kVK_ANSI_Grave }, + //{ "Insert", CGKeyCode.kVK_ANSI_INSERT }, + { "Home", CGKeyCode.kVK_Home }, + { "PageUp", CGKeyCode.kVK_PageUp }, + { "PageDown", CGKeyCode.kVK_PageDown }, + { "Delete", CGKeyCode.kVK_ForwardDelete }, + { "End", CGKeyCode.kVK_End }, + { "Divide", CGKeyCode.kVK_ANSI_KeypadDivide }, + { "Decimal", CGKeyCode.kVK_ANSI_KeypadDecimal }, + { "Backspace", CGKeyCode.kVK_Delete }, + { "Up", CGKeyCode.kVK_UpArrow }, + { "Down", CGKeyCode.kVK_DownArrow }, + { "Left", CGKeyCode.kVK_LeftArrow }, + { "Right", CGKeyCode.kVK_RightArrow }, + { "Tab", CGKeyCode.kVK_Tab }, + { "Space", CGKeyCode.kVK_Space }, + { "CapsLock", CGKeyCode.kVK_CapsLock }, + //{ "ScrollLock", CGKeyCode.kVK_F14 }, + //{ "PrintScreen", CGKeyCode.kVK_F13 }, + { "NumberLock", CGKeyCode.kVK_ANSI_KeypadClear }, + { "Enter", CGKeyCode.kVK_Return }, + { "Escape", CGKeyCode.kVK_Escape }, + { "Multiply", CGKeyCode.kVK_ANSI_KeypadMultiply }, + { "Add", CGKeyCode.kVK_ANSI_KeypadPlus }, + { "Help", CGKeyCode.kVK_Help }, + //{ "Pause", CGKeyCode.kVK_F15 }, + { "Clear", CGKeyCode.kVK_ANSI_KeypadClear }, + { "KeypadEqual", CGKeyCode.kVK_ANSI_KeypadEquals }, + //{ "Menu", CGKeyCode.kVK_ANSI_MENU }, + { "Backslash", CGKeyCode.kVK_ANSI_Backslash }, + { "Plus", CGKeyCode.kVK_ANSI_KeypadPlus }, + { "Equal", CGKeyCode.kVK_ANSI_KeypadEquals }, + { "Semicolon", CGKeyCode.kVK_ANSI_Semicolon }, + { "Quote", CGKeyCode.kVK_ANSI_Quote }, + { "Comma", CGKeyCode.kVK_ANSI_Comma }, + { "Period", CGKeyCode.kVK_ANSI_Period }, + { "ForwardSlash", CGKeyCode.kVK_ANSI_Slash }, + { "Slash", CGKeyCode.kVK_ANSI_Backslash }, + { "RightBracket", CGKeyCode.kVK_ANSI_RightBracket }, + { "LeftBracket", CGKeyCode.kVK_ANSI_LeftBracket }, + //{ "ContextMenu", CGKeyCode.kVK_ANSI_MENU }, + { "Keypad0", CGKeyCode.kVK_ANSI_Keypad0 }, + { "Keypad1", CGKeyCode.kVK_ANSI_Keypad1 }, + { "Keypad2", CGKeyCode.kVK_ANSI_Keypad2 }, + { "Keypad3", CGKeyCode.kVK_ANSI_Keypad3 }, + { "Keypad4", CGKeyCode.kVK_ANSI_Keypad4 }, + { "Keypad5", CGKeyCode.kVK_ANSI_Keypad5 }, + { "Keypad6", CGKeyCode.kVK_ANSI_Keypad6 }, + { "Keypad7", CGKeyCode.kVK_ANSI_Keypad7 }, + { "Keypad8", CGKeyCode.kVK_ANSI_Keypad8 }, + { "Keypad9", CGKeyCode.kVK_ANSI_Keypad9 }, + { "LeftShift", CGKeyCode.kVK_Shift }, + { "RightShift", CGKeyCode.kVK_RightShift }, + { "LeftControl", CGKeyCode.kVK_Control }, + { "RightControl", CGKeyCode.kVK_RightControl }, + { "LeftAlt", CGKeyCode.kVK_Command }, + { "RightAlt", CGKeyCode.kVK_RightCommand }, + { "LeftApplication", CGKeyCode.kVK_Option }, + { "RightApplication", CGKeyCode.kVK_RightOption }, + { "Shift", CGKeyCode.kVK_Shift }, + { "Alt", CGKeyCode.kVK_Command }, + { "Control", CGKeyCode.kVK_Control }, + { "Application", CGKeyCode.kVK_Option }, + }; + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSVirtualKeyboard.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSVirtualKeyboard.cs index 44e74028a..7b51d3aa9 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSVirtualKeyboard.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/MacOSVirtualKeyboard.cs @@ -3,7 +3,7 @@ using OpenTabletDriver.Native.OSX; using OpenTabletDriver.Native.OSX.Generic; using OpenTabletDriver.Native.OSX.Input; -using OpenTabletDriver.Plugin.Platform.Keyboard; +using OpenTabletDriver.Platform.Keyboard; namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard { @@ -11,11 +11,18 @@ namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard public class MacOSVirtualKeyboard : IVirtualKeyboard { + private readonly IKeysProvider _keysProvider; + + public MacOSVirtualKeyboard(IKeysProvider keysProvider) + { + _keysProvider = keysProvider; + } + private void KeyEvent(string key, bool isPress) { - if (EtoKeysymToVK.TryGetValue(key, out var code)) + if (_keysProvider.EtoToNative.TryGetValue(key, out var code)) { - var keyEvent = CGEventCreateKeyboardEvent(IntPtr.Zero, code, isPress); + var keyEvent = CGEventCreateKeyboardEvent(IntPtr.Zero, (CGKeyCode)code, isPress); CGEventPost(CGEventTapLocation.kCGHIDEventTap, keyEvent); CFRelease(keyEvent); } @@ -43,123 +50,6 @@ public void Release(IEnumerable keys) KeyEvent(key, false); } - public IEnumerable SupportedKeys => EtoKeysymToVK.Keys; - - internal static readonly Dictionary EtoKeysymToVK = new Dictionary - { - { "None", 0x00 }, - { "A", CGKeyCode.kVK_ANSI_A }, - { "B", CGKeyCode.kVK_ANSI_B }, - { "C", CGKeyCode.kVK_ANSI_C }, - { "D", CGKeyCode.kVK_ANSI_D }, - { "E", CGKeyCode.kVK_ANSI_E }, - { "F", CGKeyCode.kVK_ANSI_F }, - { "G", CGKeyCode.kVK_ANSI_G }, - { "H", CGKeyCode.kVK_ANSI_H }, - { "I", CGKeyCode.kVK_ANSI_I }, - { "J", CGKeyCode.kVK_ANSI_J }, - { "K", CGKeyCode.kVK_ANSI_K }, - { "L", CGKeyCode.kVK_ANSI_L }, - { "M", CGKeyCode.kVK_ANSI_M }, - { "N", CGKeyCode.kVK_ANSI_N }, - { "O", CGKeyCode.kVK_ANSI_O }, - { "P", CGKeyCode.kVK_ANSI_P }, - { "Q", CGKeyCode.kVK_ANSI_Q }, - { "R", CGKeyCode.kVK_ANSI_R }, - { "S", CGKeyCode.kVK_ANSI_S }, - { "T", CGKeyCode.kVK_ANSI_T }, - { "U", CGKeyCode.kVK_ANSI_U }, - { "V", CGKeyCode.kVK_ANSI_V }, - { "W", CGKeyCode.kVK_ANSI_W }, - { "X", CGKeyCode.kVK_ANSI_X }, - { "Y", CGKeyCode.kVK_ANSI_Y }, - { "Z", CGKeyCode.kVK_ANSI_Z }, - { "F1", CGKeyCode.kVK_F1 }, - { "F2", CGKeyCode.kVK_F2 }, - { "F3", CGKeyCode.kVK_F3 }, - { "F4", CGKeyCode.kVK_F4 }, - { "F5", CGKeyCode.kVK_F5 }, - { "F6", CGKeyCode.kVK_F6 }, - { "F7", CGKeyCode.kVK_F7 }, - { "F8", CGKeyCode.kVK_F8 }, - { "F9", CGKeyCode.kVK_F9 }, - { "F10", CGKeyCode.kVK_F10 }, - { "F11", CGKeyCode.kVK_F11 }, - { "F12", CGKeyCode.kVK_F12 }, - { "D0", CGKeyCode.kVK_ANSI_0 }, - { "D1", CGKeyCode.kVK_ANSI_1 }, - { "D2", CGKeyCode.kVK_ANSI_2 }, - { "D3", CGKeyCode.kVK_ANSI_3 }, - { "D4", CGKeyCode.kVK_ANSI_4 }, - { "D5", CGKeyCode.kVK_ANSI_5 }, - { "D6", CGKeyCode.kVK_ANSI_6 }, - { "D7", CGKeyCode.kVK_ANSI_7 }, - { "D8", CGKeyCode.kVK_ANSI_8 }, - { "D9", CGKeyCode.kVK_ANSI_9 }, - { "Minus", CGKeyCode.kVK_ANSI_Minus }, - { "Grave", CGKeyCode.kVK_ANSI_Grave }, - //{ "Insert", CGKeyCode.kVK_ANSI_INSERT }, - { "Home", CGKeyCode.kVK_Home }, - { "PageUp", CGKeyCode.kVK_PageUp }, - { "PageDown", CGKeyCode.kVK_PageDown }, - { "Delete", CGKeyCode.kVK_ForwardDelete }, - { "End", CGKeyCode.kVK_End }, - { "Divide", CGKeyCode.kVK_ANSI_KeypadDivide }, - { "Decimal", CGKeyCode.kVK_ANSI_KeypadDecimal }, - { "Backspace", CGKeyCode.kVK_Delete }, - { "Up", CGKeyCode.kVK_UpArrow }, - { "Down", CGKeyCode.kVK_DownArrow }, - { "Left", CGKeyCode.kVK_LeftArrow }, - { "Right", CGKeyCode.kVK_RightArrow }, - { "Tab", CGKeyCode.kVK_Tab }, - { "Space", CGKeyCode.kVK_Space }, - { "CapsLock", CGKeyCode.kVK_CapsLock }, - //{ "ScrollLock", CGKeyCode.kVK_F14 }, - //{ "PrintScreen", CGKeyCode.kVK_F13 }, - { "NumberLock", CGKeyCode.kVK_ANSI_KeypadClear }, - { "Enter", CGKeyCode.kVK_Return }, - { "Escape", CGKeyCode.kVK_Escape }, - { "Multiply", CGKeyCode.kVK_ANSI_KeypadMultiply }, - { "Add", CGKeyCode.kVK_ANSI_KeypadPlus }, - { "Help", CGKeyCode.kVK_Help }, - //{ "Pause", CGKeyCode.kVK_F15 }, - { "Clear", CGKeyCode.kVK_ANSI_KeypadClear }, - { "KeypadEqual", CGKeyCode.kVK_ANSI_KeypadEquals }, - //{ "Menu", CGKeyCode.kVK_ANSI_MENU }, - { "Backslash", CGKeyCode.kVK_ANSI_Backslash }, - { "Plus", CGKeyCode.kVK_ANSI_KeypadPlus }, - { "Equal", CGKeyCode.kVK_ANSI_KeypadEquals }, - { "Semicolon", CGKeyCode.kVK_ANSI_Semicolon }, - { "Quote", CGKeyCode.kVK_ANSI_Quote }, - { "Comma", CGKeyCode.kVK_ANSI_Comma }, - { "Period", CGKeyCode.kVK_ANSI_Period }, - { "ForwardSlash", CGKeyCode.kVK_ANSI_Slash }, - { "Slash", CGKeyCode.kVK_ANSI_Backslash }, - { "RightBracket", CGKeyCode.kVK_ANSI_RightBracket }, - { "LeftBracket", CGKeyCode.kVK_ANSI_LeftBracket }, - //{ "ContextMenu", CGKeyCode.kVK_ANSI_MENU }, - { "Keypad0", CGKeyCode.kVK_ANSI_Keypad0 }, - { "Keypad1", CGKeyCode.kVK_ANSI_Keypad1 }, - { "Keypad2", CGKeyCode.kVK_ANSI_Keypad2 }, - { "Keypad3", CGKeyCode.kVK_ANSI_Keypad3 }, - { "Keypad4", CGKeyCode.kVK_ANSI_Keypad4 }, - { "Keypad5", CGKeyCode.kVK_ANSI_Keypad5 }, - { "Keypad6", CGKeyCode.kVK_ANSI_Keypad6 }, - { "Keypad7", CGKeyCode.kVK_ANSI_Keypad7 }, - { "Keypad8", CGKeyCode.kVK_ANSI_Keypad8 }, - { "Keypad9", CGKeyCode.kVK_ANSI_Keypad9 }, - { "LeftShift", CGKeyCode.kVK_Shift }, - { "RightShift", CGKeyCode.kVK_RightShift }, - { "LeftControl", CGKeyCode.kVK_Control }, - { "RightControl", CGKeyCode.kVK_RightControl }, - { "LeftAlt", CGKeyCode.kVK_Command }, - { "RightAlt", CGKeyCode.kVK_RightCommand }, - { "LeftApplication", CGKeyCode.kVK_Option }, - { "RightApplication", CGKeyCode.kVK_RightOption }, - { "Shift", CGKeyCode.kVK_Shift }, - { "Alt", CGKeyCode.kVK_Command }, - { "Control", CGKeyCode.kVK_Control }, - { "Application", CGKeyCode.kVK_Option }, - }; + public IEnumerable SupportedKeys => _keysProvider.EtoToNative.Keys; } } diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsKeysProvider.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsKeysProvider.cs new file mode 100644 index 000000000..2e30bae06 --- /dev/null +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsKeysProvider.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using OpenTabletDriver.Native.Windows.Input; +using OpenTabletDriver.Platform.Keyboard; + +namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard +{ + public class WindowsKeysProvider : IKeysProvider + { + public IDictionary EtoToNative { get; } = new Dictionary + { + { "None", 0x00 }, + { "A", VirtualKey.VK_A }, + { "B", VirtualKey.VK_B }, + { "C", VirtualKey.VK_C }, + { "D", VirtualKey.VK_D }, + { "E", VirtualKey.VK_E }, + { "F", VirtualKey.VK_F }, + { "G", VirtualKey.VK_G }, + { "H", VirtualKey.VK_H }, + { "I", VirtualKey.VK_I }, + { "J", VirtualKey.VK_J }, + { "K", VirtualKey.VK_K }, + { "L", VirtualKey.VK_L }, + { "M", VirtualKey.VK_M }, + { "N", VirtualKey.VK_N }, + { "O", VirtualKey.VK_O }, + { "P", VirtualKey.VK_P }, + { "Q", VirtualKey.VK_Q }, + { "R", VirtualKey.VK_R }, + { "S", VirtualKey.VK_S }, + { "T", VirtualKey.VK_T }, + { "U", VirtualKey.VK_U }, + { "V", VirtualKey.VK_V }, + { "W", VirtualKey.VK_W }, + { "X", VirtualKey.VK_X }, + { "Y", VirtualKey.VK_Y }, + { "Z", VirtualKey.VK_Z }, + { "F1", VirtualKey.VK_F1 }, + { "F2", VirtualKey.VK_F2 }, + { "F3", VirtualKey.VK_F3 }, + { "F4", VirtualKey.VK_F4 }, + { "F5", VirtualKey.VK_F5 }, + { "F6", VirtualKey.VK_F6 }, + { "F7", VirtualKey.VK_F7 }, + { "F8", VirtualKey.VK_F8 }, + { "F9", VirtualKey.VK_F9 }, + { "F10", VirtualKey.VK_F10 }, + { "F11", VirtualKey.VK_F11 }, + { "F12", VirtualKey.VK_F12 }, + { "D0", VirtualKey.VK_0 }, + { "D1", VirtualKey.VK_1 }, + { "D2", VirtualKey.VK_2 }, + { "D3", VirtualKey.VK_3 }, + { "D4", VirtualKey.VK_4 }, + { "D5", VirtualKey.VK_5 }, + { "D6", VirtualKey.VK_6 }, + { "D7", VirtualKey.VK_7 }, + { "D8", VirtualKey.VK_8 }, + { "D9", VirtualKey.VK_9 }, + { "Minus", VirtualKey.VK_SUBTRACT }, + { "Grave", VirtualKey.VK_OEM_3 }, + { "Insert", VirtualKey.VK_INSERT }, + { "Home", VirtualKey.VK_HOME }, + { "PageUp", VirtualKey.VK_PRIOR }, + { "PageDown", VirtualKey.VK_NEXT }, + { "Delete", VirtualKey.VK_DELETE }, + { "End", VirtualKey.VK_END }, + { "Divide", VirtualKey.VK_DIVIDE }, + { "Decimal", VirtualKey.VK_DECIMAL }, + { "Backspace", VirtualKey.VK_BACK }, + { "Up", VirtualKey.VK_UP }, + { "Down", VirtualKey.VK_DOWN }, + { "Left", VirtualKey.VK_LEFT }, + { "Right", VirtualKey.VK_RIGHT }, + { "Tab", VirtualKey.VK_TAB }, + { "Space", VirtualKey.VK_SPACE }, + { "CapsLock", VirtualKey.VK_CAPITAL }, + { "ScrollLock", VirtualKey.VK_SCROLL }, + { "PrintScreen", VirtualKey.VK_PRINT }, + { "NumberLock", VirtualKey.VK_NUMLOCK }, + { "Enter", VirtualKey.VK_RETURN }, + { "Escape", VirtualKey.VK_ESCAPE }, + { "Multiply", VirtualKey.VK_MULTIPLY }, + { "Add", VirtualKey.VK_ADD }, + { "Subtract", VirtualKey.VK_SUBTRACT }, + { "Help", VirtualKey.VK_HELP }, + { "Pause", VirtualKey.VK_PAUSE }, + { "Clear", VirtualKey.VK_CLEAR }, + { "KeypadEqual", VirtualKey.VK_OEM_NEC_EQUAL }, + { "Menu", VirtualKey.VK_MENU }, + { "Backslash", VirtualKey.VK_OEM_4 }, + { "Plus", VirtualKey.VK_OEM_PLUS }, + { "Equal", VirtualKey.VK_OEM_PLUS }, + { "Semicolon", VirtualKey.VK_OEM_6 }, + { "Quote", VirtualKey.VK_OEM_6 }, + { "Comma", VirtualKey.VK_OEM_COMMA }, + { "Period", VirtualKey.VK_OEM_PERIOD }, + { "ForwardSlash", VirtualKey.VK_OEM_2 }, + { "Slash", VirtualKey.VK_OEM_2 }, + { "RightBracket", VirtualKey.VK_OEM_6 }, + { "LeftBracket", VirtualKey.VK_OEM_5 }, + { "ContextMenu", VirtualKey.VK_MENU }, + { "Keypad0", VirtualKey.VK_NUMPAD0 }, + { "Keypad1", VirtualKey.VK_NUMPAD1 }, + { "Keypad2", VirtualKey.VK_NUMPAD2 }, + { "Keypad3", VirtualKey.VK_NUMPAD3 }, + { "Keypad4", VirtualKey.VK_NUMPAD4 }, + { "Keypad5", VirtualKey.VK_NUMPAD5 }, + { "Keypad6", VirtualKey.VK_NUMPAD6 }, + { "Keypad7", VirtualKey.VK_NUMPAD7 }, + { "Keypad8", VirtualKey.VK_NUMPAD8 }, + { "Keypad9", VirtualKey.VK_NUMPAD9 }, + { "LeftShift", VirtualKey.VK_LSHIFT }, + { "RightShift", VirtualKey.VK_RSHIFT }, + { "LeftControl", VirtualKey.VK_LCONTROL }, + { "RightControl", VirtualKey.VK_RCONTROL }, + { "LeftAlt", VirtualKey.VK_LMENU }, + { "RightAlt", VirtualKey.VK_RMENU }, + { "LeftApplication", VirtualKey.VK_LWIN }, + { "RightApplication", VirtualKey.VK_RWIN }, + { "Shift", VirtualKey.VK_SHIFT }, + { "Alt", VirtualKey.VK_MENU }, + { "Control", VirtualKey.VK_CONTROL }, + { "Application", VirtualKey.VK_LWIN }, + }; + } +} diff --git a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsVirtualKeyboard.cs b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsVirtualKeyboard.cs index 18e96e323..c250d6703 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsVirtualKeyboard.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Keyboard/WindowsVirtualKeyboard.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.Input; -using OpenTabletDriver.Plugin.Platform.Keyboard; +using OpenTabletDriver.Platform.Keyboard; namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard { @@ -10,9 +10,16 @@ namespace OpenTabletDriver.Desktop.Interop.Input.Keyboard public class WindowsVirtualKeyboard : IVirtualKeyboard { + private readonly IKeysProvider _keysProvider; + + public WindowsVirtualKeyboard(IKeysProvider keysProvider) + { + _keysProvider = keysProvider; + } + private void KeyEvent(string key, bool isPress) { - var vk = EtoKeysymToVK[key]; + var vk = (VirtualKey)_keysProvider.EtoToNative[key]; var input = new INPUT { type = INPUT_TYPE.KEYBD_INPUT, @@ -55,143 +62,6 @@ public void Release(IEnumerable keys) KeyEvent(key, false); } - public IEnumerable SupportedKeys => EtoKeysymToVK.Keys; - - internal static readonly Dictionary EtoKeysymToVK = new Dictionary - { - { "None", 0x00 }, - { "A", VirtualKey.VK_A }, - { "B", VirtualKey.VK_B }, - { "C", VirtualKey.VK_C }, - { "D", VirtualKey.VK_D }, - { "E", VirtualKey.VK_E }, - { "F", VirtualKey.VK_F }, - { "G", VirtualKey.VK_G }, - { "H", VirtualKey.VK_H }, - { "I", VirtualKey.VK_I }, - { "J", VirtualKey.VK_J }, - { "K", VirtualKey.VK_K }, - { "L", VirtualKey.VK_L }, - { "M", VirtualKey.VK_M }, - { "N", VirtualKey.VK_N }, - { "O", VirtualKey.VK_O }, - { "P", VirtualKey.VK_P }, - { "Q", VirtualKey.VK_Q }, - { "R", VirtualKey.VK_R }, - { "S", VirtualKey.VK_S }, - { "T", VirtualKey.VK_T }, - { "U", VirtualKey.VK_U }, - { "V", VirtualKey.VK_V }, - { "W", VirtualKey.VK_W }, - { "X", VirtualKey.VK_X }, - { "Y", VirtualKey.VK_Y }, - { "Z", VirtualKey.VK_Z }, - { "F1", VirtualKey.VK_F1 }, - { "F2", VirtualKey.VK_F2 }, - { "F3", VirtualKey.VK_F3 }, - { "F4", VirtualKey.VK_F4 }, - { "F5", VirtualKey.VK_F5 }, - { "F6", VirtualKey.VK_F6 }, - { "F7", VirtualKey.VK_F7 }, - { "F8", VirtualKey.VK_F8 }, - { "F9", VirtualKey.VK_F9 }, - { "F10", VirtualKey.VK_F10 }, - { "F11", VirtualKey.VK_F11 }, - { "F12", VirtualKey.VK_F12 }, - { "F13", VirtualKey.VK_F13 }, - { "F14", VirtualKey.VK_F14 }, - { "F15", VirtualKey.VK_F15 }, - { "F16", VirtualKey.VK_F16 }, - { "F17", VirtualKey.VK_F17 }, - { "F18", VirtualKey.VK_F18 }, - { "F19", VirtualKey.VK_F19 }, - { "F20", VirtualKey.VK_F20 }, - { "F21", VirtualKey.VK_F21 }, - { "F22", VirtualKey.VK_F22 }, - { "F23", VirtualKey.VK_F23 }, - { "F24", VirtualKey.VK_F24 }, - { "D0", VirtualKey.VK_0 }, - { "D1", VirtualKey.VK_1 }, - { "D2", VirtualKey.VK_2 }, - { "D3", VirtualKey.VK_3 }, - { "D4", VirtualKey.VK_4 }, - { "D5", VirtualKey.VK_5 }, - { "D6", VirtualKey.VK_6 }, - { "D7", VirtualKey.VK_7 }, - { "D8", VirtualKey.VK_8 }, - { "D9", VirtualKey.VK_9 }, - { "Minus", VirtualKey.VK_SUBTRACT }, - { "Grave", VirtualKey.VK_OEM_3 }, - { "Insert", VirtualKey.VK_INSERT }, - { "Home", VirtualKey.VK_HOME }, - { "PageUp", VirtualKey.VK_PRIOR }, - { "PageDown", VirtualKey.VK_NEXT }, - { "Delete", VirtualKey.VK_DELETE }, - { "End", VirtualKey.VK_END }, - { "Divide", VirtualKey.VK_DIVIDE }, - { "Decimal", VirtualKey.VK_DECIMAL }, - { "Backspace", VirtualKey.VK_BACK }, - { "Up", VirtualKey.VK_UP }, - { "Down", VirtualKey.VK_DOWN }, - { "Left", VirtualKey.VK_LEFT }, - { "Right", VirtualKey.VK_RIGHT }, - { "Tab", VirtualKey.VK_TAB }, - { "Space", VirtualKey.VK_SPACE }, - { "CapsLock", VirtualKey.VK_CAPITAL }, - { "ScrollLock", VirtualKey.VK_SCROLL }, - { "PrintScreen", VirtualKey.VK_PRINT }, - { "NumberLock", VirtualKey.VK_NUMLOCK }, - { "Enter", VirtualKey.VK_RETURN }, - { "Escape", VirtualKey.VK_ESCAPE }, - { "Multiply", VirtualKey.VK_MULTIPLY }, - { "Add", VirtualKey.VK_ADD }, - { "Subtract", VirtualKey.VK_SUBTRACT }, - { "Help", VirtualKey.VK_HELP }, - { "Pause", VirtualKey.VK_PAUSE }, - { "Clear", VirtualKey.VK_CLEAR }, - { "KeypadEqual", VirtualKey.VK_OEM_NEC_EQUAL }, - { "Menu", VirtualKey.VK_MENU }, - { "Backslash", VirtualKey.VK_OEM_4 }, - { "Plus", VirtualKey.VK_OEM_PLUS }, - { "Equal", VirtualKey.VK_OEM_PLUS }, - { "Semicolon", VirtualKey.VK_OEM_6 }, - { "Quote", VirtualKey.VK_OEM_6 }, - { "Comma", VirtualKey.VK_OEM_COMMA }, - { "Period", VirtualKey.VK_OEM_PERIOD }, - { "ForwardSlash", VirtualKey.VK_OEM_2 }, - { "Slash", VirtualKey.VK_OEM_2 }, - { "RightBracket", VirtualKey.VK_OEM_6 }, - { "LeftBracket", VirtualKey.VK_OEM_5 }, - { "ContextMenu", VirtualKey.VK_MENU }, - { "Keypad0", VirtualKey.VK_NUMPAD0 }, - { "Keypad1", VirtualKey.VK_NUMPAD1 }, - { "Keypad2", VirtualKey.VK_NUMPAD2 }, - { "Keypad3", VirtualKey.VK_NUMPAD3 }, - { "Keypad4", VirtualKey.VK_NUMPAD4 }, - { "Keypad5", VirtualKey.VK_NUMPAD5 }, - { "Keypad6", VirtualKey.VK_NUMPAD6 }, - { "Keypad7", VirtualKey.VK_NUMPAD7 }, - { "Keypad8", VirtualKey.VK_NUMPAD8 }, - { "Keypad9", VirtualKey.VK_NUMPAD9 }, - { "LeftShift", VirtualKey.VK_LSHIFT }, - { "RightShift", VirtualKey.VK_RSHIFT }, - { "LeftControl", VirtualKey.VK_LCONTROL }, - { "RightControl", VirtualKey.VK_RCONTROL }, - { "LeftAlt", VirtualKey.VK_LMENU }, - { "RightAlt", VirtualKey.VK_RMENU }, - { "LeftApplication", VirtualKey.VK_LWIN }, - { "RightApplication", VirtualKey.VK_RWIN }, - { "Shift", VirtualKey.VK_SHIFT }, - { "Alt", VirtualKey.VK_MENU }, - { "Control", VirtualKey.VK_CONTROL }, - { "Application", VirtualKey.VK_LWIN }, - { "Mute", VirtualKey.VK_VOLUME_MUTE }, - { "VolumeDown", VirtualKey.VK_VOLUME_DOWN }, - { "VolumeUp", VirtualKey.VK_VOLUME_UP }, - { "Play/Pause", VirtualKey.VK_MEDIA_PLAY_PAUSE }, - { "Stop", VirtualKey.VK_MEDIA_STOP }, - { "PreviousTrack", VirtualKey.VK_MEDIA_PREV_TRACK }, - { "NextTrack", VirtualKey.VK_MEDIA_NEXT_TRACK }, - }; + public IEnumerable SupportedKeys => _keysProvider.EtoToNative.Keys; } } diff --git a/OpenTabletDriver.Desktop/Interop/Input/MacOSVirtualMouse.cs b/OpenTabletDriver.Desktop/Interop/Input/MacOSVirtualMouse.cs index b577a2f71..06e3dc0a6 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/MacOSVirtualMouse.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/MacOSVirtualMouse.cs @@ -4,7 +4,8 @@ using OpenTabletDriver.Native.OSX; using OpenTabletDriver.Native.OSX.Generic; using OpenTabletDriver.Native.OSX.Input; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input { @@ -12,9 +13,9 @@ namespace OpenTabletDriver.Desktop.Interop.Input public abstract class MacOSVirtualMouse : IMouseButtonHandler { - protected MacOSVirtualMouse() + protected MacOSVirtualMouse(IVirtualScreen virtualScreen) { - var primary = DesktopInterop.VirtualScreen.Displays.FirstOrDefault(); + var primary = virtualScreen.Displays.FirstOrDefault(); offset = new CGPoint(primary.Position.X, primary.Position.Y); } diff --git a/OpenTabletDriver.Desktop/Interop/Input/Relative/EvdevRelativePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Relative/EvdevRelativePointer.cs index f65030b35..dbaaf6252 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Relative/EvdevRelativePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Relative/EvdevRelativePointer.cs @@ -1,8 +1,7 @@ using System.Numerics; using OpenTabletDriver.Native.Linux; using OpenTabletDriver.Native.Linux.Evdev; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input.Relative { diff --git a/OpenTabletDriver.Desktop/Interop/Input/Relative/MacOSRelativePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Relative/MacOSRelativePointer.cs index fdd1a3cc3..ee5a2a30d 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Relative/MacOSRelativePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Relative/MacOSRelativePointer.cs @@ -2,7 +2,8 @@ using System.Numerics; using OpenTabletDriver.Native.OSX; using OpenTabletDriver.Native.OSX.Generic; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input.Relative { @@ -10,6 +11,10 @@ namespace OpenTabletDriver.Desktop.Interop.Input.Relative public class MacOSRelativePointer : Input.MacOSVirtualMouse, IRelativePointer { + public MacOSRelativePointer(IVirtualScreen virtualScreen) : base(virtualScreen) + { + } + public void SetPosition(Vector2 delta) { var lastPos = base.GetPosition(); diff --git a/OpenTabletDriver.Desktop/Interop/Input/Relative/WindowsRelativePointer.cs b/OpenTabletDriver.Desktop/Interop/Input/Relative/WindowsRelativePointer.cs index 0ee32c9d4..68888bf64 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/Relative/WindowsRelativePointer.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/Relative/WindowsRelativePointer.cs @@ -1,7 +1,7 @@ using System.Numerics; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.Input; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input.Relative { diff --git a/OpenTabletDriver.Desktop/Interop/Input/WindowsVirtualMouse.cs b/OpenTabletDriver.Desktop/Interop/Input/WindowsVirtualMouse.cs index 379315999..9ed7f73ea 100644 --- a/OpenTabletDriver.Desktop/Interop/Input/WindowsVirtualMouse.cs +++ b/OpenTabletDriver.Desktop/Interop/Input/WindowsVirtualMouse.cs @@ -1,8 +1,8 @@ using System; +using OpenTabletDriver.Attributes; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.Input; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Interop.Input { diff --git a/OpenTabletDriver.Desktop/Interop/Timer/FallbackTimer.cs b/OpenTabletDriver.Desktop/Interop/Timer/FallbackTimer.cs index b77ce452a..ac8065702 100644 --- a/OpenTabletDriver.Desktop/Interop/Timer/FallbackTimer.cs +++ b/OpenTabletDriver.Desktop/Interop/Timer/FallbackTimer.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.Threading; -using OpenTabletDriver.Plugin.Timers; namespace OpenTabletDriver.Desktop.Interop.Timer { diff --git a/OpenTabletDriver.Desktop/Interop/Timer/LinuxTimer.cs b/OpenTabletDriver.Desktop/Interop/Timer/LinuxTimer.cs index 23cdd36e5..3e806ce3d 100644 --- a/OpenTabletDriver.Desktop/Interop/Timer/LinuxTimer.cs +++ b/OpenTabletDriver.Desktop/Interop/Timer/LinuxTimer.cs @@ -3,8 +3,6 @@ using OpenTabletDriver.Native.Linux; using OpenTabletDriver.Native.Linux.Timers; using OpenTabletDriver.Native.Linux.Timers.Structs; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Timers; namespace OpenTabletDriver.Desktop.Interop.Timer { diff --git a/OpenTabletDriver.Desktop/Interop/Timer/WindowsTimer.cs b/OpenTabletDriver.Desktop/Interop/Timer/WindowsTimer.cs index 45febee7b..b8d4cce7e 100644 --- a/OpenTabletDriver.Desktop/Interop/Timer/WindowsTimer.cs +++ b/OpenTabletDriver.Desktop/Interop/Timer/WindowsTimer.cs @@ -2,8 +2,6 @@ using System.Runtime.InteropServices; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.Timers; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Timers; namespace OpenTabletDriver.Desktop.Interop.Timer { diff --git a/OpenTabletDriver.Desktop/Json/AdvancedJsonSerializer.cs b/OpenTabletDriver.Desktop/Json/AdvancedJsonSerializer.cs new file mode 100644 index 000000000..f4ee85db9 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/AdvancedJsonSerializer.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace OpenTabletDriver.Desktop.Json +{ + public sealed class AdvancedJsonSerializer : JsonSerializer + { + public AdvancedJsonSerializer() + { + foreach (var converter in Utilities.Converters) + { + Converters.Add(converter); + } + } + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableAppInfo.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableAppInfo.cs new file mode 100644 index 000000000..e524eee1a --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableAppInfo.cs @@ -0,0 +1,20 @@ +using OpenTabletDriver.Desktop.Interop.AppInfo; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableAppInfo : Serializable, IAppInfo + { + public string ConfigurationDirectory { get; set; } = string.Empty; + public string SettingsFile { get; set; } = string.Empty; + public string BinaryDirectory { get; set; } = string.Empty; + public string PluginDirectory { get; set; } = string.Empty; + public string PresetDirectory { get; set; } = string.Empty; + public string TemporaryDirectory { get; set; } = string.Empty; + public string CacheDirectory { get; set; } = string.Empty; + public string BackupDirectory { get; set; } = string.Empty; + public string TrashDirectory { get; set; } = string.Empty; + public string AppDataDirectory { get; set; } = string.Empty; + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpoint.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpoint.cs new file mode 100644 index 000000000..6112010b8 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpoint.cs @@ -0,0 +1,24 @@ +using OpenTabletDriver.Devices; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableDeviceEndpoint : Serializable, IDeviceEndpoint + { + public int ProductID { set; get; } + public int VendorID { set; get; } + public int InputReportLength { set; get; } + public int OutputReportLength { set; get; } + public int FeatureReportLength { set; get; } + public string Manufacturer { set; get; } = string.Empty; + public string ProductName { set; get; } = string.Empty; + public string FriendlyName { set; get; } = string.Empty; + public string SerialNumber { set; get; } = string.Empty; + public string DevicePath { set; get; } = string.Empty; + public bool CanOpen { set; get; } + + public IDeviceEndpointStream Open() => throw NotSupported(); + public string GetDeviceString(byte index) => throw NotSupported(); + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpointStream.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpointStream.cs new file mode 100644 index 000000000..f1a25c358 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceEndpointStream.cs @@ -0,0 +1,14 @@ +using OpenTabletDriver.Devices; + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableDeviceEndpointStream : Serializable, IDeviceEndpointStream + { + public void Dispose() => throw NotSupported(); + public byte[] Read() => throw NotSupported(); + public void Write(byte[] buffer) => throw NotSupported(); + + public void GetFeature(byte[] buffer) => throw NotSupported(); + public void SetFeature(byte[] buffer) => throw NotSupported(); + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceReportParser.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceReportParser.cs new file mode 100644 index 000000000..01270f4c7 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDeviceReportParser.cs @@ -0,0 +1,9 @@ +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableDeviceReportParser : Serializable, IReportParser + { + public IDeviceReport Parse(byte[] report) => throw NotSupported(); + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDiagnosticInfo.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDiagnosticInfo.cs new file mode 100644 index 000000000..d85254ae1 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDiagnosticInfo.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Logging; + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableDiagnosticInfo : Serializable, IDiagnosticInfo + { + public string AppVersion { set; get; } + public string BuildDate { set; get; } + public OperatingSystem OperatingSystem { set; get; } + public IDictionary EnvironmentVariables { set; get; } + public IEnumerable Devices { set; get; } + public IEnumerable ConsoleLog { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDisplay.cs b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDisplay.cs new file mode 100644 index 000000000..9756db9be --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Implementations/SerializableDisplay.cs @@ -0,0 +1,13 @@ +using System.Numerics; +using OpenTabletDriver.Platform.Display; + +namespace OpenTabletDriver.Desktop.Json.Converters.Implementations +{ + internal sealed class SerializableDisplay : Serializable, IDisplay + { + public int Index { set; get; } + public float Width { set; get; } + public float Height { set; get; } + public Vector2 Position { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/InterfaceConverter.cs b/OpenTabletDriver.Desktop/Json/Converters/InterfaceConverter.cs new file mode 100644 index 000000000..1fe05173d --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/InterfaceConverter.cs @@ -0,0 +1,25 @@ +using System; +using Newtonsoft.Json; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Json.Converters +{ + public class InterfaceConverter : JsonConverter where TInterface : class where TClass : TInterface, new() + { + private readonly JsonSerializer _internalSerializer = new JsonSerializer(); + + public override void WriteJson(JsonWriter writer, TInterface? value, JsonSerializer serializer) + { + _internalSerializer.Serialize(writer, value); + } + + public override TInterface ReadJson(JsonReader reader, Type objectType, TInterface? existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + var obj = existingValue ?? new TClass(); + serializer.Populate(reader, obj); + return obj; + } + } +} diff --git a/OpenTabletDriver.Desktop/Json/Converters/Serializable.cs b/OpenTabletDriver.Desktop/Json/Converters/Serializable.cs new file mode 100644 index 000000000..ec57740e1 --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Converters/Serializable.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; +using Newtonsoft.Json; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Json.Converters +{ + [JsonObject] + internal abstract class Serializable + { + protected static Exception NotSupported([CallerMemberName] string? memberName = null) + { + return new NotSupportedException($"\"{memberName}\" cannot be used in a serialized proxy."); + } + + public override string ToString() => JsonConvert.SerializeObject(this, Formatting.Indented); + } +} diff --git a/OpenTabletDriver.Desktop/Json/Utilities.cs b/OpenTabletDriver.Desktop/Json/Utilities.cs new file mode 100644 index 000000000..a54c51c1b --- /dev/null +++ b/OpenTabletDriver.Desktop/Json/Utilities.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using OpenTabletDriver.Desktop.Diagnostics; +using OpenTabletDriver.Desktop.Interop.AppInfo; +using OpenTabletDriver.Desktop.Json.Converters; +using OpenTabletDriver.Desktop.Json.Converters.Implementations; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Desktop.Json +{ + internal static class Utilities + { + internal static readonly IEnumerable Converters = new JsonConverter[] + { + new InterfaceConverter, SerializableDeviceReportParser>(), + new InterfaceConverter(), + new InterfaceConverter(), + new InterfaceConverter(), + new InterfaceConverter(), + new InterfaceConverter() + }; + } +} diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V5/Settings.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V5/Settings.cs index f78687df5..330907d80 100644 --- a/OpenTabletDriver.Desktop/Migration/LegacySettings/V5/Settings.cs +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V5/Settings.cs @@ -1,14 +1,14 @@ using System; -using System.Numerics; +using JetBrains.Annotations; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin; namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V5 { + [UsedImplicitly(ImplicitUseTargetFlags.Members)] internal class Settings { - public PluginSettingStore OutputMode { get; set; } - public PluginSettingStoreCollection Filters { get; set; } + public PluginSettings OutputMode { get; set; } + public PluginSettingsCollection Filters { get; set; } public bool AutoHook { get; set; } public bool LockUsableAreaDisplay { get; set; } @@ -35,29 +35,31 @@ internal class Settings public TimeSpan ResetTime { get; set; } public float TipActivationPressure { get; set; } - public PluginSettingStore TipButton { get; set; } + public PluginSettings TipButton { get; set; } public float EraserActivationPressure { get; set; } - public PluginSettingStore EraserButton { get; set; } + public PluginSettings EraserButton { get; set; } - public PluginSettingStoreCollection PenButtons { get; set; } - public PluginSettingStoreCollection AuxButtons { get; set; } + public PluginSettingsCollection PenButtons { get; set; } + public PluginSettingsCollection AuxButtons { get; set; } - public PluginSettingStoreCollection Tools { get; set; } - public PluginSettingStoreCollection Interpolators { get; set; } + public PluginSettingsCollection Tools { get; set; } + public PluginSettingsCollection Interpolators { get; set; } public Area GetDisplayArea() => new Area { Width = DisplayWidth, Height = DisplayHeight, - Position = new Vector2(DisplayXOffset, DisplayYOffset) + XPosition = DisplayXOffset, + YPosition = DisplayYOffset }; - public Area GetTabletArea() => new Area + public AngledArea GetTabletArea() => new AngledArea { Width = TabletWidth, Height = TabletHeight, - Position = new Vector2(TabletXOffset, TabletYOffset), + XPosition = TabletXOffset, + YPosition = TabletYOffset, Rotation = TabletRotation }; diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AbsoluteModeSettings.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AbsoluteModeSettings.cs new file mode 100644 index 000000000..b3174f37f --- /dev/null +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AbsoluteModeSettings.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V6 +{ + public class AbsoluteModeSettings : NotifyPropertyChanged + { + [JsonProperty("Display")] + public AreaSettings Display { set; get; } + + [JsonProperty("Tablet")] + public AreaSettings Tablet { set; get; } + + [JsonProperty("EnableClipping")] + public bool EnableClipping { set; get; } + + [JsonProperty("EnableAreaLimiting")] + public bool EnableAreaLimiting { set; get; } + + [JsonProperty("LockAspectRatio")] + public bool LockAspectRatio { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AreaSettings.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AreaSettings.cs new file mode 100644 index 000000000..3dc355aab --- /dev/null +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/AreaSettings.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V6 +{ + public class AreaSettings + { + [JsonProperty("Width")] + public float Width { set; get; } + + [JsonProperty("Height")] + public float Height { set; get; } + + [JsonProperty("X")] + public float X { set; get; } + + [JsonProperty("Y")] + public float Y { set; get; } + + [JsonProperty("Rotation")] + public float Rotation { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Profile.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Profile.cs new file mode 100644 index 000000000..e661924fe --- /dev/null +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Profile.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V6 +{ + public class Profile : NotifyPropertyChanged + { + [JsonProperty("Tablet")] + public string Tablet { set; get; } + + [JsonProperty("OutputMode")] + public PluginSettings OutputMode { set; get; } + + [JsonProperty("Filters")] + public PluginSettingsCollection Filters { set; get; } + + [JsonProperty("AbsoluteModeSettings")] + public AbsoluteModeSettings AbsoluteModeSettings { set; get; } + + [JsonProperty("RelativeModeSettings")] + public RelativeModeSettings RelativeModeSettings { set; get; } + + [JsonProperty("Bindings")] + public BindingSettings BindingSettings { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/RelativeModeSettings.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/RelativeModeSettings.cs new file mode 100644 index 000000000..4cabf6f8a --- /dev/null +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/RelativeModeSettings.cs @@ -0,0 +1,20 @@ +using System; +using Newtonsoft.Json; + +namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V6 +{ + public class RelativeModeSettings : NotifyPropertyChanged + { + [JsonProperty("XSensitivity")] + public float XSensitivity { set; get; } + + [JsonProperty("YSensitivity")] + public float YSensitivity { set; get; } + + [JsonProperty("RelativeRotation")] + public float RelativeRotation { set; get; } + + [JsonProperty("RelativeResetDelay")] + public TimeSpan ResetTime { set; get; } + } +} diff --git a/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Settings.cs b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Settings.cs new file mode 100644 index 000000000..99c2b99a5 --- /dev/null +++ b/OpenTabletDriver.Desktop/Migration/LegacySettings/V6/Settings.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; +using System.Linq; +using Newtonsoft.Json; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.Desktop.Migration.LegacySettings.V6 +{ + public class Settings : NotifyPropertyChanged + { + [JsonProperty("Profiles")] + public Collection Profiles { set; get; } + + [JsonProperty("LockUsableAreaDisplay")] + public bool LockUsableAreaDisplay { set; get; } + + [JsonProperty("LockUsableAreaTablet")] + public bool LockUsableAreaTablet { set; get; } + + [JsonProperty("Tools")] + public Collection Tools { set; get; } + + public bool IsValid() + { + return Profiles != null + && Tools != null + && Profiles.All(p => p.AbsoluteModeSettings != null && p.RelativeModeSettings != null); + } + } +} diff --git a/OpenTabletDriver.Desktop/Migration/SettingsMigrator.cs b/OpenTabletDriver.Desktop/Migration/SettingsMigrator.cs index 47c7c6e8b..836fe72fe 100644 --- a/OpenTabletDriver.Desktop/Migration/SettingsMigrator.cs +++ b/OpenTabletDriver.Desktop/Migration/SettingsMigrator.cs @@ -2,21 +2,30 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Numerics; using System.Text.RegularExpressions; -using OpenTabletDriver.Desktop.Binding; -using OpenTabletDriver.Desktop.Output; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop.Interop.AppInfo; using OpenTabletDriver.Desktop.Profiles; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Output; + +#nullable enable namespace OpenTabletDriver.Desktop.Migration { - using V5 = LegacySettings.V5; + using V6 = LegacySettings.V6; - public static class SettingsMigrator + public class SettingsMigrator { - public static void Migrate(AppInfo appInfo) + private readonly IServiceProvider _serviceProvider; + + public SettingsMigrator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void Migrate(IAppInfo appInfo) { var file = new FileInfo(appInfo.SettingsFile); @@ -25,7 +34,7 @@ public static void Migrate(AppInfo appInfo) Log.Write("Settings", "Settings have been migrated."); // Back up existing settings file for safety - var backupDir = AppInfo.Current.BackupDirectory; + var backupDir = appInfo.BackupDirectory; if (!Directory.Exists(backupDir)) Directory.CreateDirectory(backupDir); @@ -37,79 +46,124 @@ public static void Migrate(AppInfo appInfo) } } - private static Settings Migrate(FileInfo file) + private Settings? Migrate(FileInfo file) { - var v5 = Serialization.Deserialize(file); - return v5.IsValid() ? Convert(v5) : null; + var v6 = Serialization.Deserialize(file); + return v6.IsValid() ? Convert(v6) : null; } - private static Settings Convert(V5::Settings old) + private Settings? Convert(V6::Settings oldSettings) { - var settings = Settings.GetDefaults(); + var settings = new Settings + { + Tools = new PluginSettingsCollection(oldSettings.Tools) + }; - if (settings.Profiles.FirstOrDefault() is Profile profile) + foreach (var oldProfile in oldSettings.Profiles) { - profile.OutputMode = SafeMigrate(old.OutputMode, new PluginSettingStore(typeof(AbsoluteMode))); + var newProfile = new Profile + { + Tablet = oldProfile.Tablet, + Filters = oldProfile.Filters, + BindingSettings = oldProfile.BindingSettings, + OutputMode = MigrateOutputModeSettings(oldSettings, oldProfile) + }; + settings.Profiles.Add(newProfile); + } + + if (settings.Profiles.Any() && settings.Profiles.All(p => !string.IsNullOrWhiteSpace(p.Tablet))) + return settings; - profile.AbsoluteModeSettings.Display.Area = old.GetDisplayArea(); - profile.AbsoluteModeSettings.Tablet.Area = old.GetTabletArea(); - profile.AbsoluteModeSettings.EnableAreaLimiting = old.EnableAreaLimiting; - profile.AbsoluteModeSettings.EnableClipping = old.EnableClipping; - profile.AbsoluteModeSettings.LockAspectRatio = old.LockAspectRatio; + return null; + } + + private PluginSettings? MigrateOutputModeSettings(V6::Settings oldSettings, V6::Profile oldProfile) + { + var pluginFactory = _serviceProvider.GetRequiredService(); - profile.RelativeModeSettings.XSensitivity = old.XSensitivity; - profile.RelativeModeSettings.YSensitivity = old.YSensitivity; - profile.RelativeModeSettings.RelativeRotation = old.RelativeRotation; - profile.RelativeModeSettings.ResetTime = old.ResetTime; + var oldSetting = oldProfile.OutputMode; + var type = pluginFactory.GetPluginType(oldSetting.Path); + if (type == null) + return null; - profile.Filters = SafeMigrateCollection(new PluginSettingStoreCollection(old.Filters.Concat(old.Interpolators))); + bool isAbsolute = type.IsAssignableTo(typeof(AbsoluteOutputMode)); + bool isRelative = type.IsAssignableTo(typeof(RelativeOutputMode)); - profile.BindingSettings.TipActivationThreshold = old.TipActivationPressure; - profile.BindingSettings.TipButton = SafeMigrate(old.TipButton, new PluginSettingStore(new MouseBinding { Button = nameof(MouseButton.Left) })); - profile.BindingSettings.PenButtons = SafeMigrateCollection(old.PenButtons); - profile.BindingSettings.AuxButtons = SafeMigrateCollection(old.AuxButtons); + if (isAbsolute) + { + var absSettings = oldProfile.AbsoluteModeSettings; + return new PluginSettings(type, new + { + Input = new AngledArea + { + Width = absSettings.Tablet.Width, + Height = absSettings.Tablet.Height, + XPosition = absSettings.Tablet.X, + YPosition = absSettings.Tablet.Y, + Rotation = absSettings.Tablet.Rotation + }, + Output = new Area + { + Width = absSettings.Display.Width, + Height = absSettings.Display.Height, + XPosition = absSettings.Display.X, + YPosition = absSettings.Display.Y + }, + LockAspectRatio = absSettings.LockAspectRatio, + AreaClipping = absSettings.EnableClipping, + AreaLimiting = absSettings.EnableAreaLimiting, + AreaBounds = oldSettings.LockUsableAreaDisplay || oldSettings.LockUsableAreaTablet + }); } - settings.LockUsableAreaDisplay = old.LockUsableAreaDisplay; - settings.LockUsableAreaTablet = old.LockUsableAreaTablet; + if (isRelative) + { + var relSettings = oldProfile.RelativeModeSettings; + return new PluginSettings(type, new + { + Sensitivity = new Vector2(relSettings.XSensitivity, relSettings.YSensitivity), + Rotation = relSettings.RelativeRotation, + ResetDelay = relSettings.ResetTime + }); + } - return settings; + return null; } - private static readonly Dictionary namespaceMigrationDict = new Dictionary + private static readonly Dictionary NamespaceMigrationDict = new Dictionary { { new Regex(@"TabletDriverLib\.(.+?)$"), $"OpenTabletDriver.{{0}}" }, { new Regex(@"OpenTabletDriver\.Binding\.(.+?)$"), $"OpenTabletDriver.Desktop.Binding.{{0}}" }, { new Regex(@"OpenTabletDriver\.Output\.(.+?)$"), $"OpenTabletDriver.Desktop.Output.{{0}}" } }; - private static readonly Dictionary propertyMigrationDict = new Dictionary + private static readonly Dictionary PropertyMigrationDict = new Dictionary { { new Regex(@"OpenTabletDriver\.Desktop\.Binding\.KeyBinding$"), ("^Property$", "Key") }, { new Regex(@"OpenTabletDriver\.Desktop\.Binding\.MouseBinding$"), ("^Property$", "Button") } }; - public static PluginSettingStore SafeMigrate(PluginSettingStore store, PluginSettingStore defaultStore = null) + private PluginSettings? SafeMigrate(PluginSettings? store, PluginSettings? defaultStore = null) { store = SafeMigrateNamespace(store, defaultStore); store = MigrateProperty(store); return store; } - public static void MigrateNamespace(PluginSettingStore store) + private static void MigrateNamespace(PluginSettings? store) { if (store != null) { - store.Path = MigrateNamespace(store.Path); + store.Path = MigrateNamespace(store.Path) ?? string.Empty; } } - public static string MigrateNamespace(string input) + private static string? MigrateNamespace(string? input) { if (string.IsNullOrWhiteSpace(input)) return input; - foreach (var pair in namespaceMigrationDict) + foreach (var pair in NamespaceMigrationDict) { var regex = pair.Key; var replacement = pair.Value; @@ -122,18 +176,18 @@ public static string MigrateNamespace(string input) return input; } - public static PluginSettingStore MigrateProperty(PluginSettingStore store) + private static PluginSettings? MigrateProperty(PluginSettings? store) { if (store != null) { - foreach (var pair in propertyMigrationDict) + foreach (var pair in PropertyMigrationDict) { var type = pair.Key; - (var property, var replacementProperty) = pair.Value; + var (property, replacementProperty) = pair.Value; - if (type.IsMatch(store.Path)) + if (type.IsMatch(store.Path ?? string.Empty)) { - foreach (var setting in store.Settings) + foreach (var setting in store.Settings!) { if (Regex.IsMatch(setting.Property, property)) { @@ -147,21 +201,31 @@ public static PluginSettingStore MigrateProperty(PluginSettingStore store) return store; } - private static PluginSettingStore SafeMigrateNamespace(PluginSettingStore store, PluginSettingStore defaultStore) + private PluginSettings? SafeMigrateNamespace(PluginSettings? store, PluginSettings? defaultStore) { MigrateNamespace(store); - if (store != null && PluginSettingStore.FromPath(store.Path) == null && defaultStore != null) + if (store != null && StoreFromTypePath(store.Path) == null && defaultStore != null) { - Log.Write("Settings", $"Invalid plugin path '{store.Path ?? "null"}' has been changed to '{defaultStore.Path}'", LogLevel.Warning); + Log.Write("Settings", $"Invalid plugin path '{store.Path}' has been changed to '{defaultStore.Path}'", LogLevel.Warning); store = defaultStore; } return store; } - private static PluginSettingStoreCollection SafeMigrateCollection(PluginSettingStoreCollection collection) + private PluginSettings? StoreFromTypePath(string? path) + { + var pluginFactory = _serviceProvider.GetRequiredService(); + var type = pluginFactory.GetPluginType(path ?? string.Empty); + if (type == null) + return null; + + return new PluginSettings(type); + } + + private PluginSettingsCollection SafeMigrateCollection(PluginSettingsCollection? collection) { if (collection == null) - collection = new PluginSettingStoreCollection(); + collection = new PluginSettingsCollection(); for (int i = 0; i < collection.Count; i++) collection[i] = SafeMigrate(collection[i]); diff --git a/OpenTabletDriver.Desktop/Output/AbsoluteMode.cs b/OpenTabletDriver.Desktop/Output/AbsoluteMode.cs index 1d6fe4a02..dbd0aa4ff 100644 --- a/OpenTabletDriver.Desktop/Output/AbsoluteMode.cs +++ b/OpenTabletDriver.Desktop/Output/AbsoluteMode.cs @@ -1,14 +1,22 @@ -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Output { [PluginName("Absolute Mode")] - public class AbsoluteMode : AbsoluteOutputMode, IPointerProvider + public class AbsoluteMode : AbsoluteOutputMode, IMouseButtonSource { - [Resolved] - public override IAbsolutePointer Pointer { set; get; } + public AbsoluteMode( + InputDevice tablet, + IAbsolutePointer absolutePointer, + ISettingsProvider settingsProvider + ) : base(tablet, absolutePointer) + { + settingsProvider.Inject(this); + } + + + public IMouseButtonHandler MouseButtonHandler => Pointer; } } diff --git a/OpenTabletDriver.Desktop/Output/LinuxArtistMode.cs b/OpenTabletDriver.Desktop/Output/LinuxArtistMode.cs index c875c6081..9624172c9 100644 --- a/OpenTabletDriver.Desktop/Output/LinuxArtistMode.cs +++ b/OpenTabletDriver.Desktop/Output/LinuxArtistMode.cs @@ -1,22 +1,21 @@ -using System; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Output { - [PluginName("Artist Mode"), SupportedPlatform(PluginPlatform.Linux)] - public class LinuxArtistMode : AbsoluteOutputMode, IPointerProvider + [PluginName("Artist Mode"), SupportedPlatform(SystemPlatform.Linux)] + public class LinuxArtistMode : AbsoluteOutputMode, IMouseButtonSource { - [Resolved] - public IPressureHandler VirtualTablet { get; set; } - - public override IAbsolutePointer Pointer + public LinuxArtistMode( + InputDevice tablet, + IPressureHandler pressureHandler, + ISettingsProvider settingsProvider + ) : base(tablet, pressureHandler) { - set => throw new NotSupportedException(); - get => (IAbsolutePointer)VirtualTablet; + settingsProvider.Inject(this); } + + public IMouseButtonHandler MouseButtonHandler => Pointer; } } diff --git a/OpenTabletDriver.Desktop/Output/RelativeMode.cs b/OpenTabletDriver.Desktop/Output/RelativeMode.cs index 1dc65a36f..df7ce26e3 100644 --- a/OpenTabletDriver.Desktop/Output/RelativeMode.cs +++ b/OpenTabletDriver.Desktop/Output/RelativeMode.cs @@ -1,14 +1,21 @@ -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Pointer; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Output; +using OpenTabletDriver.Platform.Pointer; namespace OpenTabletDriver.Desktop.Output { [PluginName("Relative Mode")] - public class RelativeMode : RelativeOutputMode, IPointerProvider + public class RelativeMode : RelativeOutputMode, IMouseButtonSource { - [Resolved] - public override IRelativePointer Pointer { set; get; } + public RelativeMode( + InputDevice tablet, + IRelativePointer relativePointer, + ISettingsProvider settingsProvider + ) : base(tablet, relativePointer) + { + settingsProvider.Inject(this); + } + + public IMouseButtonHandler MouseButtonHandler => Pointer; } } diff --git a/OpenTabletDriver.Desktop/PluginSettingsProvider.cs b/OpenTabletDriver.Desktop/PluginSettingsProvider.cs new file mode 100644 index 000000000..52b86bf80 --- /dev/null +++ b/OpenTabletDriver.Desktop/PluginSettingsProvider.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Desktop.Reflection; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public class PluginSettingsProvider : ISettingsProvider + { + private readonly PluginSettings _store; + + public PluginSettingsProvider(PluginSettings store) + { + _store = store; + } + + public void Inject(object? obj) + { + if (obj == null) + return; + + var type = obj.GetType(); + var group = type.Name; + + foreach (var property in type.GetProperties()) + { + var attr = property.GetCustomAttribute(); + if (attr != null) + { + var setting = _store[property.Name]; + var value = setting.GetValue(property.PropertyType); + property.SetValue(obj, value); + + if (value != null) + Log.Debug(group, $"{property.Name}: {value}"); + } + } + } + } +} diff --git a/OpenTabletDriver.Desktop/Preset.cs b/OpenTabletDriver.Desktop/Preset.cs index 36591e02f..bd88d4bd6 100644 --- a/OpenTabletDriver.Desktop/Preset.cs +++ b/OpenTabletDriver.Desktop/Preset.cs @@ -1,5 +1,3 @@ -using Newtonsoft.Json; - namespace OpenTabletDriver.Desktop { public class Preset @@ -11,11 +9,6 @@ public Preset(string name, Settings settings) } public string Name { get; } - private Settings Settings; - - public Settings GetSettings() - { - return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(Settings)); - } + public Settings Settings { get; } } } diff --git a/OpenTabletDriver.Desktop/PresetManager.cs b/OpenTabletDriver.Desktop/PresetManager.cs index 73b590e91..f22755005 100644 --- a/OpenTabletDriver.Desktop/PresetManager.cs +++ b/OpenTabletDriver.Desktop/PresetManager.cs @@ -1,17 +1,20 @@ using System.Collections.Generic; using System.IO; -using OpenTabletDriver.Plugin; +using System.Linq; +using OpenTabletDriver.Desktop.Interop.AppInfo; namespace OpenTabletDriver.Desktop { - public class PresetManager + public class PresetManager : IPresetManager { - public PresetManager() + public PresetManager(IAppInfo appInfo) { - PresetDirectory = new DirectoryInfo(AppInfo.Current.PresetDirectory); + _presetDirectory = new DirectoryInfo(appInfo.PresetDirectory); } - public DirectoryInfo PresetDirectory { get; } + private readonly DirectoryInfo _presetDirectory; + + private const string FILE_EXTENSION = ".json"; private List Presets { get; } = new List(); @@ -24,17 +27,17 @@ public Preset FindPreset(string presetName) private void Load() { - foreach (var preset in PresetDirectory.EnumerateFiles("*.json")) + foreach (var file in _presetDirectory.EnumerateFiles("*.json")) { - var settings = Settings.Deserialize(preset); + var settings = Settings.Deserialize(file); if (settings != null) { - Presets.Add(new Preset(preset.Name.Replace(preset.Extension, string.Empty), settings)); - Log.Write("Settings", $"Loaded preset '{preset.Name}'", LogLevel.Info); + Presets.Add(new Preset(file.Name.Replace(file.Extension, string.Empty), settings)); + Log.Write("Settings", $"Loaded preset '{file.Name}'", LogLevel.Info); } else { - Log.Write("Settings", $"Invalid settings file '{preset.Name}' attempted to load into presets", LogLevel.Warning); + Log.Write("Settings", $"Invalid settings file '{file.Name}' attempted to load into presets", LogLevel.Warning); } } } @@ -45,5 +48,20 @@ public void Refresh() Load(); Log.Write("Settings", $"Presets have been refreshed. Loaded {Presets.Count} presets.", LogLevel.Info); } + + public void Save(string name, Settings settings) + { + if (!_presetDirectory.Exists) + _presetDirectory.Create(); + + var files = _presetDirectory.EnumerateFiles("*" + FILE_EXTENSION); + var oldFile = files.FirstOrDefault(f => f.Name.Replace(FILE_EXTENSION, string.Empty) == name); + + oldFile?.Delete(); + var path = Path.Join(_presetDirectory.FullName, name + FILE_EXTENSION); + Serialization.Serialize(File.Create(path), settings); + + Refresh(); + } } } diff --git a/OpenTabletDriver.Desktop/Profiles/AbsoluteModeSettings.cs b/OpenTabletDriver.Desktop/Profiles/AbsoluteModeSettings.cs deleted file mode 100644 index acff19e20..000000000 --- a/OpenTabletDriver.Desktop/Profiles/AbsoluteModeSettings.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Newtonsoft.Json; -using OpenTabletDriver.Plugin.Platform.Display; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Desktop.Profiles -{ - public class AbsoluteModeSettings : ViewModel - { - private AreaSettings display, tablet; - private bool _lockar, _clipping, _areaLimiting; - - [JsonProperty("Display")] - public AreaSettings Display - { - set => this.RaiseAndSetIfChanged(ref this.display, value); - get => this.display; - } - - [JsonProperty("Tablet")] - public AreaSettings Tablet - { - set => this.RaiseAndSetIfChanged(ref this.tablet, value); - get => this.tablet; - } - - [JsonProperty("EnableClipping")] - public bool EnableClipping - { - set => RaiseAndSetIfChanged(ref _clipping, value); - get => _clipping; - } - - [JsonProperty("EnableAreaLimiting")] - public bool EnableAreaLimiting - { - set => RaiseAndSetIfChanged(ref _areaLimiting, value); - get => _areaLimiting; - } - - [JsonProperty("LockAspectRatio")] - public bool LockAspectRatio - { - set => RaiseAndSetIfChanged(ref _lockar, value); - get => _lockar; - } - - public static AbsoluteModeSettings GetDefaults(DigitizerSpecifications digitizer) - { - var display = AppInfo.PluginManager.GetService(); - - return new AbsoluteModeSettings - { - Display = AreaSettings.GetDefaults(display), - Tablet = AreaSettings.GetDefaults(digitizer), - EnableClipping = true - }; - } - } -} diff --git a/OpenTabletDriver.Desktop/Profiles/AreaSettings.cs b/OpenTabletDriver.Desktop/Profiles/AreaSettings.cs deleted file mode 100644 index 2a3b50b92..000000000 --- a/OpenTabletDriver.Desktop/Profiles/AreaSettings.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Numerics; -using Newtonsoft.Json; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Platform.Display; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Desktop.Profiles -{ - public class AreaSettings : ViewModel - { - private float width, height, x, y, rotation; - - [JsonProperty("Width")] - public float Width - { - set => this.RaiseAndSetIfChanged(ref this.width, value); - get => this.width; - } - - [JsonProperty("Height")] - public float Height - { - set => this.RaiseAndSetIfChanged(ref this.height, value); - get => this.height; - } - - [JsonProperty("X")] - public float X - { - set => this.RaiseAndSetIfChanged(ref this.x, value); - get => this.x; - } - - [JsonProperty("Y")] - public float Y - { - set => this.RaiseAndSetIfChanged(ref this.y, value); - get => this.y; - } - - [JsonProperty("Rotation")] - public float Rotation - { - set => this.RaiseAndSetIfChanged(ref this.rotation, value); - get => this.rotation; - } - - [JsonIgnore] - public Area Area - { - set - { - Width = value.Width; - Height = value.Height; - X = value.Position.X; - Y = value.Position.Y; - Rotation = value.Rotation; - } - get => new Area - { - Width = this.Width, - Height = this.Height, - Position = new Vector2(this.X, this.Y), - Rotation = this.Rotation - }; - } - - public static AreaSettings GetDefaults(DigitizerSpecifications digitizer) - { - return new AreaSettings - { - Width = digitizer.Width, - Height = digitizer.Height, - X = digitizer.Width / 2, - Y = digitizer.Height / 2 - }; - } - - public static AreaSettings GetDefaults(IDisplay display) - { - return new AreaSettings - { - Width = display.Width, - Height = display.Height, - X = display.Width / 2, - Y = display.Height / 2 - }; - } - } -} diff --git a/OpenTabletDriver.Desktop/Profiles/BindingSettings.cs b/OpenTabletDriver.Desktop/Profiles/BindingSettings.cs index edb4a35c2..bef391679 100644 --- a/OpenTabletDriver.Desktop/Profiles/BindingSettings.cs +++ b/OpenTabletDriver.Desktop/Profiles/BindingSettings.cs @@ -1,103 +1,105 @@ +using System.ComponentModel; using Newtonsoft.Json; using OpenTabletDriver.Desktop.Binding; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Desktop.Profiles { - public class BindingSettings : ViewModel + public class BindingSettings : NotifyPropertyChanged { - private float tP, eP; - private PluginSettingStore tipButton, eraserButton, mouseScrollUp, mouseScrollDown; - private PluginSettingStoreCollection penButtons = new PluginSettingStoreCollection(), - auxButtons = new PluginSettingStoreCollection(), - mouseButtons = new PluginSettingStoreCollection(); + private float _tP, _eP; + private PluginSettings _tipButton, _eraserButton, _mouseScrollUp, _mouseScrollDown; + private PluginSettingsCollection _penButtons = new PluginSettingsCollection(), + _auxButtons = new PluginSettingsCollection(), + _mouseButtons = new PluginSettingsCollection(); - [JsonProperty("TipActivationThreshold")] + [DisplayName("Tip Activation Threshold"), JsonProperty("TipActivationThreshold")] public float TipActivationThreshold { - set => this.RaiseAndSetIfChanged(ref this.tP, value); - get => this.tP; + set => RaiseAndSetIfChanged(ref _tP, value); + get => _tP; } - [JsonProperty("TipButton")] - public PluginSettingStore TipButton + [DisplayName("Tip Button"), JsonProperty("TipButton")] + public PluginSettings TipButton { - set => this.RaiseAndSetIfChanged(ref this.tipButton, value); - get => this.tipButton; + set => RaiseAndSetIfChanged(ref _tipButton, value); + get => _tipButton; } - [JsonProperty("EraserActivationThreshold")] + [DisplayName("Eraser Activation Threshold"), JsonProperty("EraserActivationThreshold")] public float EraserActivationThreshold { - set => this.RaiseAndSetIfChanged(ref this.eP, value); - get => this.eP; + set => RaiseAndSetIfChanged(ref _eP, value); + get => _eP; } - [JsonProperty("EraserButton")] - public PluginSettingStore EraserButton + [DisplayName("Eraser Button"), JsonProperty("EraserButton")] + public PluginSettings EraserButton { - set => this.RaiseAndSetIfChanged(ref this.eraserButton, value); - get => this.eraserButton; + set => RaiseAndSetIfChanged(ref _eraserButton, value); + get => _eraserButton; } - [JsonProperty("PenButtons")] - public PluginSettingStoreCollection PenButtons + [DisplayName("Pen Button"), JsonProperty("PenButtons")] + public PluginSettingsCollection PenButtons { - set => this.RaiseAndSetIfChanged(ref this.penButtons, value); - get => this.penButtons; + set => RaiseAndSetIfChanged(ref _penButtons, value); + get => _penButtons; } - [JsonProperty("AuxButtons")] - public PluginSettingStoreCollection AuxButtons + [DisplayName("Auxiliary Button"), JsonProperty("AuxButtons")] + public PluginSettingsCollection AuxButtons { - set => this.RaiseAndSetIfChanged(ref this.auxButtons, value); - get => this.auxButtons; + set => RaiseAndSetIfChanged(ref _auxButtons, value); + get => _auxButtons; } - [JsonProperty("MouseButtons")] - public PluginSettingStoreCollection MouseButtons + [DisplayName("Mouse Button"), JsonProperty("MouseButtons")] + public PluginSettingsCollection MouseButtons { - set => this.RaiseAndSetIfChanged(ref this.mouseButtons, value); - get => this.mouseButtons; + set => RaiseAndSetIfChanged(ref _mouseButtons, value); + get => _mouseButtons; } - [JsonProperty("MouseScrollUp")] - public PluginSettingStore MouseScrollUp + [DisplayName("Mouse Scroll Up"), JsonProperty("MouseScrollUp")] + public PluginSettings MouseScrollUp { - set => this.RaiseAndSetIfChanged(ref this.mouseScrollUp, value); - get => this.mouseScrollUp; + set => RaiseAndSetIfChanged(ref _mouseScrollUp, value); + get => _mouseScrollUp; } - [JsonProperty("MouseScrollDown")] - public PluginSettingStore MouseScrollDown + [DisplayName("Mouse Scroll Down"), JsonProperty("MouseScrollDown")] + public PluginSettings MouseScrollDown { - set => this.RaiseAndSetIfChanged(ref this.mouseScrollDown, value); - get => this.mouseScrollDown; + set => RaiseAndSetIfChanged(ref _mouseScrollDown, value); + get => _mouseScrollDown; } public static BindingSettings GetDefaults(TabletSpecifications tabletSpecifications) { var bindingSettings = new BindingSettings { - TipButton = new PluginSettingStore( - new MouseBinding + TipButton = new PluginSettings( + typeof(MouseBinding), + new { Button = nameof(MouseButton.Left) } ), - PenButtons = new PluginSettingStoreCollection(), - AuxButtons = new PluginSettingStoreCollection(), - MouseButtons = new PluginSettingStoreCollection() + PenButtons = new PluginSettingsCollection(), + AuxButtons = new PluginSettingsCollection(), + MouseButtons = new PluginSettingsCollection() }; bindingSettings.MatchSpecifications(tabletSpecifications); return bindingSettings; } - public void MatchSpecifications(TabletSpecifications tabletSpecifications) + private void MatchSpecifications(TabletSpecifications tabletSpecifications) { - int penButtonCount = (int?)tabletSpecifications.Pen?.Buttons?.ButtonCount ?? 0; + int penButtonCount = (int?)tabletSpecifications.Pen?.ButtonCount ?? 0; int auxButtonCount = (int?)tabletSpecifications.AuxiliaryButtons?.ButtonCount ?? 0; int mouseButtonCount = (int?)tabletSpecifications.MouseButtons?.ButtonCount ?? 0; diff --git a/OpenTabletDriver.Desktop/Profiles/Profile.cs b/OpenTabletDriver.Desktop/Profiles/Profile.cs index f0287d7de..16a46bc98 100644 --- a/OpenTabletDriver.Desktop/Profiles/Profile.cs +++ b/OpenTabletDriver.Desktop/Profiles/Profile.cs @@ -1,71 +1,63 @@ +using System; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using OpenTabletDriver.Desktop.Output; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Platform.Display; namespace OpenTabletDriver.Desktop.Profiles { - public class Profile : ViewModel + public class Profile : NotifyPropertyChanged { - private string tablet; - private PluginSettingStore outputMode; - private AbsoluteModeSettings absoluteMode = new AbsoluteModeSettings(); - private RelativeModeSettings relativeMode = new RelativeModeSettings(); - private BindingSettings bindings = new BindingSettings(); - private PluginSettingStoreCollection filters = new PluginSettingStoreCollection(); + private string _tablet; + private PluginSettings _outputMode; + private BindingSettings _bindings = new BindingSettings(); + private PluginSettingsCollection _filters = new PluginSettingsCollection(); [JsonProperty("Tablet")] public string Tablet { - set => this.RaiseAndSetIfChanged(ref tablet, value); - get => tablet; + set => RaiseAndSetIfChanged(ref _tablet, value); + get => _tablet; } [JsonProperty("OutputMode")] - public PluginSettingStore OutputMode + public PluginSettings OutputMode { - set => RaiseAndSetIfChanged(ref outputMode, value); - get => outputMode; + set => RaiseAndSetIfChanged(ref _outputMode, value); + get => _outputMode; } [JsonProperty("Filters")] - public PluginSettingStoreCollection Filters + public PluginSettingsCollection Filters { - set => RaiseAndSetIfChanged(ref filters, value); - get => filters; - } - - [JsonProperty("AbsoluteModeSettings")] - public AbsoluteModeSettings AbsoluteModeSettings - { - set => this.RaiseAndSetIfChanged(ref absoluteMode, value); - get => absoluteMode; - } - - [JsonProperty("RelativeModeSettings")] - public RelativeModeSettings RelativeModeSettings - { - set => this.RaiseAndSetIfChanged(ref relativeMode, value); - get => relativeMode; + set => RaiseAndSetIfChanged(ref _filters, value); + get => _filters; } [JsonProperty("Bindings")] public BindingSettings BindingSettings { - set => this.RaiseAndSetIfChanged(ref bindings, value); - get => bindings; + set => RaiseAndSetIfChanged(ref _bindings, value); + get => _bindings; } - public static Profile GetDefaults(TabletReference tablet) + public static Profile GetDefaults(IServiceProvider serviceProvider, InputDevice tablet) { + var screen = serviceProvider.GetRequiredService(); + var digitizer = tablet.Configuration.Specifications.Digitizer; + return new Profile { - Tablet = tablet.Properties.Name, - OutputMode = new PluginSettingStore(typeof(AbsoluteMode)), - AbsoluteModeSettings = AbsoluteModeSettings.GetDefaults(tablet.Properties.Specifications.Digitizer), - RelativeModeSettings = RelativeModeSettings.GetDefaults(), - BindingSettings = BindingSettings.GetDefaults(tablet.Properties.Specifications) + Tablet = tablet.Configuration.Name, + OutputMode = typeof(AbsoluteMode).GetDefaultSettings(serviceProvider, digitizer, screen), + BindingSettings = BindingSettings.GetDefaults(tablet.Configuration.Specifications) }; } + + public override string ToString() + { + return base.ToString() + ": " + Tablet; + } } } diff --git a/OpenTabletDriver.Desktop/Profiles/ProfileCollection.cs b/OpenTabletDriver.Desktop/Profiles/ProfileCollection.cs index 5e884bcea..01a07ec31 100644 --- a/OpenTabletDriver.Desktop/Profiles/ProfileCollection.cs +++ b/OpenTabletDriver.Desktop/Profiles/ProfileCollection.cs @@ -2,14 +2,16 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using OpenTabletDriver.Plugin.Tablet; +using System.Text.Json.Serialization; + +#nullable enable namespace OpenTabletDriver.Desktop.Profiles { public class ProfileCollection : ObservableCollection { + [JsonConstructor] public ProfileCollection() - : base() { } @@ -18,35 +20,37 @@ public ProfileCollection(IEnumerable profiles) { } - public ProfileCollection(IEnumerable tablets) - : this(tablets.Select(s => Profile.GetDefaults(s))) - { - } - - public Profile this[TabletReference tablet] + public Profile? this[InputDevice tablet] { set => SetProfile(tablet, value); get => GetProfile(tablet); } - public void SetProfile(TabletReference tablet, Profile profile) + private void SetProfile(InputDevice tablet, Profile? profile) { - if (this.FirstOrDefault(t => t.Tablet == tablet.Properties.Name) is Profile oldProfile) + if (profile == null) + return; + + if (this.FirstOrDefault(t => t.Tablet == tablet.Configuration.Name) is Profile oldProfile) { - this.Remove(oldProfile); + Remove(oldProfile); } - this.Add(profile); + + Add(profile); } - public Profile GetProfile(TabletReference tablet) + private Profile? GetProfile(InputDevice tablet) { - return this.FirstOrDefault(t => t.Tablet == tablet.Properties.Name) is Profile profile ? profile : Generate(tablet); + return this.FirstOrDefault(t => t.Tablet == tablet.Configuration.Name); } - public Profile Generate(TabletReference tablet) + public Profile GetOrSetDefaults(IServiceProvider serviceProvider, InputDevice inputDevice) { - var profile = Profile.GetDefaults(tablet); - SetProfile(tablet, profile); + if (GetProfile(inputDevice) is Profile existingProfile) + return existingProfile; + + var profile = Profile.GetDefaults(serviceProvider, inputDevice); + SetProfile(inputDevice, profile); return profile; } } diff --git a/OpenTabletDriver.Desktop/Profiles/RelativeModeSettings.cs b/OpenTabletDriver.Desktop/Profiles/RelativeModeSettings.cs deleted file mode 100644 index 52bd9ef1f..000000000 --- a/OpenTabletDriver.Desktop/Profiles/RelativeModeSettings.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Numerics; -using Newtonsoft.Json; - -namespace OpenTabletDriver.Desktop.Profiles -{ - public class RelativeModeSettings : ViewModel - { - private float xS, yS, relRot; - private TimeSpan rT; - - [JsonProperty("XSensitivity")] - public float XSensitivity - { - set => RaiseAndSetIfChanged(ref this.xS, value); - get => this.xS; - } - - [JsonProperty("YSensitivity")] - public float YSensitivity - { - set => RaiseAndSetIfChanged(ref this.yS, value); - get => this.yS; - } - - [JsonProperty("RelativeRotation")] - public float RelativeRotation - { - set => RaiseAndSetIfChanged(ref this.relRot, value); - get => this.relRot; - } - - [JsonProperty("RelativeResetDelay")] - public TimeSpan ResetTime - { - set => RaiseAndSetIfChanged(ref this.rT, value); - get => this.rT; - } - - [JsonIgnore] - public Vector2 Sensitivity - { - set - { - XSensitivity = value.X; - YSensitivity = value.Y; - } - get => new Vector2(XSensitivity, YSensitivity); - } - - public static RelativeModeSettings GetDefaults() - { - return new RelativeModeSettings - { - XSensitivity = 10, - YSensitivity = 10, - RelativeRotation = 0, - ResetTime = TimeSpan.FromMilliseconds(100) - }; - } - } -} diff --git a/OpenTabletDriver.Desktop/RPC/DebugReportData.cs b/OpenTabletDriver.Desktop/RPC/DebugReportData.cs index f2583bb13..70bfc4a42 100644 --- a/OpenTabletDriver.Desktop/RPC/DebugReportData.cs +++ b/OpenTabletDriver.Desktop/RPC/DebugReportData.cs @@ -1,32 +1,36 @@ -using System.Linq; -using System.Text.Json.Serialization; -using Newtonsoft.Json.Linq; -using OpenTabletDriver.Plugin.Tablet; +using System.Numerics; +using Newtonsoft.Json; +using OpenTabletDriver.Tablet; + +#nullable enable namespace OpenTabletDriver.Desktop.RPC { + [JsonObject] public class DebugReportData { [JsonConstructor] - public DebugReportData() + private DebugReportData() { } - public DebugReportData(TabletReference tablet, IDeviceReport report) + public DebugReportData(string deviceName, IDeviceReport report) { - this.Tablet = tablet; - this.Data = JToken.FromObject(report); - this.Path = report.GetType().FullName; - } + var type = report.GetType(); - public TabletReference Tablet { set; get; } - public string Path { set; get; } - public JToken Data { set; get; } + DeviceName = deviceName; + ReportType = type.FullName!; + Raw = ReportFormatter.GetStringRaw(report.Raw); + Formatted = ReportFormatter.GetStringFormat(report); - public object ToObject() - { - var type = AppInfo.PluginManager.PluginTypes.First(t => t.FullName == Path); - return Data.ToObject(type); + RawPosition = (report as ITabletReport)?.Position; } + + public string DeviceName { set; get; } = string.Empty; + public string ReportType { set; get; } = string.Empty; + public string Raw { set; get; } = string.Empty; + public string Formatted { set; get; } = string.Empty; + + public Vector2? RawPosition { set; get; } } } diff --git a/OpenTabletDriver.Desktop/RPC/Messages/MessageFormatter.cs b/OpenTabletDriver.Desktop/RPC/Messages/MessageFormatter.cs new file mode 100644 index 000000000..67e9c9ed1 --- /dev/null +++ b/OpenTabletDriver.Desktop/RPC/Messages/MessageFormatter.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using StreamJsonRpc; + +#nullable enable + +namespace OpenTabletDriver.Desktop.RPC.Messages +{ + public class MessageFormatter : JsonMessageFormatter + { + public MessageFormatter(IEnumerable additionalConverters) + { + foreach (var converter in additionalConverters) + { + JsonSerializer.Converters.Add(converter); + } + } + } +} diff --git a/OpenTabletDriver.Desktop/RPC/Messages/MessageHandler.cs b/OpenTabletDriver.Desktop/RPC/Messages/MessageHandler.cs new file mode 100644 index 000000000..5725e5c67 --- /dev/null +++ b/OpenTabletDriver.Desktop/RPC/Messages/MessageHandler.cs @@ -0,0 +1,15 @@ +using System.IO; +using StreamJsonRpc; + +#nullable enable + +namespace OpenTabletDriver.Desktop.RPC.Messages +{ + public class MessageHandler : NewLineDelimitedMessageHandler + { + public MessageHandler(Stream stream) + : base(stream, stream, new MessageFormatter(Json.Utilities.Converters)) + { + } + } +} diff --git a/OpenTabletDriver.UX/Tools/ReportFormatter.cs b/OpenTabletDriver.Desktop/RPC/ReportFormatter.cs similarity index 79% rename from OpenTabletDriver.UX/Tools/ReportFormatter.cs rename to OpenTabletDriver.Desktop/RPC/ReportFormatter.cs index 4daf8ae82..63da1034a 100644 --- a/OpenTabletDriver.UX/Tools/ReportFormatter.cs +++ b/OpenTabletDriver.Desktop/RPC/ReportFormatter.cs @@ -1,16 +1,33 @@ using System; using System.Collections.Generic; using System.Text; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Tablet.Touch; +using OpenTabletDriver.Tablet; +using OpenTabletDriver.Tablet.Touch; -namespace OpenTabletDriver.UX.Tools +namespace OpenTabletDriver.Desktop.RPC { public static class ReportFormatter { - public static string GetStringRaw(IDeviceReport report) + public static string GetStringRaw(byte[] raw) { - return BitConverter.ToString(report.Raw).Replace('-', ' '); + var sb = new StringBuilder(); + + var column = 0; + foreach (var b in raw) + { + column++; + if (column > 8) + { + column = 0; + sb.AppendLine(); + } + + sb.Append(b.ToString("X2")); + sb.Append(' '); + } + + return sb.ToString(); + return BitConverter.ToString(raw).Replace('-', ' '); } public static string GetStringFormat(IDeviceReport report) @@ -35,15 +52,15 @@ public static string GetStringFormat(IDeviceReport report) sb.AppendLines(GetStringFormat(mouseReport)); if (report is IToolReport toolReport) sb.AppendLines(GetStringFormat(toolReport)); - if (report is OutOfRangeReport oorReport) - sb.AppendLines(GetStringFormat(oorReport)); + if (report is OutOfRangeReport) + sb.AppendLine("Out of range"); return sb.ToString(); } private static IEnumerable GetStringFormat(IAbsolutePositionReport absolutePositionReport) { - yield return $"Position:[{absolutePositionReport.Position.X},{absolutePositionReport.Position.Y}]"; + yield return $"Position:{absolutePositionReport.Position}"; } private static IEnumerable GetStringFormat(ITabletReport tabletReport) @@ -70,7 +87,7 @@ private static IEnumerable GetStringFormat(IProximityReport proximityRep private static IEnumerable GetStringFormat(ITiltReport tiltReport) { - yield return $"Tilt:[{tiltReport.Tilt.X},{tiltReport.Tilt.Y}]"; + yield return $"Tilt:{tiltReport.Tilt}"; } private static IEnumerable GetStringFormat(ITouchReport touchReport) @@ -84,7 +101,7 @@ private static IEnumerable GetStringFormat(ITouchReport touchReport) private static IEnumerable GetStringFormat(IMouseReport mouseReport) { yield return $"MouseButtons:[{string.Join(" ", mouseReport.MouseButtons)}]"; - yield return $"Scroll:[{mouseReport.Scroll.X},{mouseReport.Scroll.Y}]"; + yield return $"Scroll:{mouseReport.Scroll}"; } private static IEnumerable GetStringFormat(IToolReport toolReport) @@ -94,11 +111,6 @@ private static IEnumerable GetStringFormat(IToolReport toolReport) yield return $"Serial:{toolReport.Serial}"; } - private static IEnumerable GetStringFormat(OutOfRangeReport oorReport) - { - yield return $"Pen is out of Range"; - } - private static void AppendLines(this StringBuilder sb, IEnumerable lines) { foreach (var line in lines) diff --git a/OpenTabletDriver.Desktop/RPC/RpcClient.cs b/OpenTabletDriver.Desktop/RPC/RpcClient.cs index 5788f7835..9aecf648d 100644 --- a/OpenTabletDriver.Desktop/RPC/RpcClient.cs +++ b/OpenTabletDriver.Desktop/RPC/RpcClient.cs @@ -1,63 +1,70 @@ using System; -using System.Collections.Generic; using System.IO.Pipes; using System.Threading.Tasks; using StreamJsonRpc; +#nullable enable + namespace OpenTabletDriver.Desktop.RPC { public class RpcClient where T : class { - private readonly string pipeName; - private NamedPipeClientStream stream; - private JsonRpc rpc; - private IList> reconnectHooks = new List>(); - - public T Instance { private set; get; } - public bool IsConnected { get => rpc != null && !rpc.IsDisposed; } - - public event EventHandler Connected; - public event EventHandler Disconnected; + private readonly string _pipeName; public RpcClient(string pipeName) { - this.pipeName = pipeName; + _pipeName = pipeName; } + private NamedPipeClientStream? _stream; + private JsonRpc? _rpc; + private readonly TimeSpan _connectTimeout = TimeSpan.FromSeconds(5); + + public T? Instance { private set; get; } + + public event EventHandler? Connected; + public event EventHandler? Disconnected; + public async Task Connect() { - this.stream = GetStream(); - await this.stream.ConnectAsync(); + _stream = GetStream(); + var connect = _stream.ConnectAsync(); + var timeout = Task.Delay(_connectTimeout); + var result = await Task.WhenAny(connect, timeout); + if (result == timeout) + throw new TimeoutException($"Connecting to daemon failed after {_connectTimeout.Seconds} seconds."); - rpc = new JsonRpc(this.stream); - rpc.Disconnected += (_, _) => - { - this.stream.Dispose(); - OnDisconnected(); - rpc.Dispose(); - }; + _rpc = Utilities.Client(_stream); + _rpc.Disconnected += (_, _) => OnDisconnected(); - this.Instance = this.rpc.Attach(); - rpc.StartListening(); + Instance = _rpc.Attach(); + _rpc.StartListening(); OnConnected(); } + public async Task Reconnect() + { + await _stream!.DisposeAsync(); + _rpc?.Dispose(); + await Task.Delay(500); + } + protected virtual void OnConnected() { - this.Connected?.Invoke(this, EventArgs.Empty); + Connected?.Invoke(this, EventArgs.Empty); } - protected virtual void OnDisconnected() + private void OnDisconnected() { - this.Disconnected?.Invoke(this, EventArgs.Empty); + Disconnected?.Invoke(this, EventArgs.Empty); } private NamedPipeClientStream GetStream() { return new NamedPipeClientStream( ".", - this.pipeName, + _pipeName, PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough | PipeOptions.CurrentUserOnly ); diff --git a/OpenTabletDriver.Desktop/RPC/RpcHost.cs b/OpenTabletDriver.Desktop/RPC/RpcHost.cs index a5ac116a1..158a06c52 100644 --- a/OpenTabletDriver.Desktop/RPC/RpcHost.cs +++ b/OpenTabletDriver.Desktop/RPC/RpcHost.cs @@ -2,21 +2,20 @@ using System.IO; using System.IO.Pipes; using System.Threading.Tasks; -using OpenTabletDriver.Plugin; using StreamJsonRpc; namespace OpenTabletDriver.Desktop.RPC { public class RpcHost where T : class { - private JsonRpc rpc; - private readonly string pipeName; + private JsonRpc _rpc; + private readonly string _pipeName; public event EventHandler ConnectionStateChanged; public RpcHost(string pipeName) { - this.pipeName = pipeName; + _pipeName = pipeName; } public async Task Run(T host) @@ -30,8 +29,9 @@ public async Task Run(T host) try { ConnectionStateChanged?.Invoke(this, true); - this.rpc = JsonRpc.Attach(stream, host); - await this.rpc.Completion; + _rpc = Utilities.Host(stream, host); + _rpc.StartListening(); + await _rpc.Completion; } catch (ObjectDisposedException) { @@ -44,7 +44,7 @@ public async Task Run(T host) Log.Exception(ex); } ConnectionStateChanged?.Invoke(this, false); - this.rpc.Dispose(); + _rpc.Dispose(); await stream.DisposeAsync(); }); } @@ -53,7 +53,7 @@ public async Task Run(T host) private NamedPipeServerStream CreateStream() { return new NamedPipeServerStream( - this.pipeName, + _pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, diff --git a/OpenTabletDriver.Desktop/RPC/TypeProxy.cs b/OpenTabletDriver.Desktop/RPC/TypeProxy.cs new file mode 100644 index 000000000..5d787929d --- /dev/null +++ b/OpenTabletDriver.Desktop/RPC/TypeProxy.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using Newtonsoft.Json; +using OpenTabletDriver.Desktop.Reflection; + +#nullable enable + +namespace OpenTabletDriver.Desktop.RPC +{ + [JsonObject] + public class TypeProxy + { + [JsonConstructor] + public TypeProxy() + { + } + + public TypeProxy(IPluginFactory pluginFactory, Type type) + { + Path = type.Namespace + "." + type.Name; + FriendlyName = pluginFactory.GetFriendlyName(type.GetPath()); + } + + public string Path { set; get; } = string.Empty; + public string? FriendlyName { set; get; } + + public Type FindType(IPluginManager pluginManager) + { + return pluginManager.ExportedTypes.First(t => t.FullName == Path); + } + } +} diff --git a/OpenTabletDriver.Desktop/RPC/Utilities.cs b/OpenTabletDriver.Desktop/RPC/Utilities.cs new file mode 100644 index 000000000..cd9c90111 --- /dev/null +++ b/OpenTabletDriver.Desktop/RPC/Utilities.cs @@ -0,0 +1,21 @@ +using System.IO; +using OpenTabletDriver.Desktop.RPC.Messages; +using StreamJsonRpc; + +#nullable enable + +namespace OpenTabletDriver.Desktop.RPC +{ + internal static class Utilities + { + public static JsonRpc Client(Stream stream) + { + return new JsonRpc(new MessageHandler(stream)); + } + + public static JsonRpc Host(Stream stream, T obj) + { + return new JsonRpc(new MessageHandler(stream), obj); + } + } +} diff --git a/OpenTabletDriver.Desktop/Reflection/DesktopPluginContext.cs b/OpenTabletDriver.Desktop/Reflection/DesktopPluginContext.cs index e9b5c6ef0..2a4b4ea8d 100644 --- a/OpenTabletDriver.Desktop/Reflection/DesktopPluginContext.cs +++ b/OpenTabletDriver.Desktop/Reflection/DesktopPluginContext.cs @@ -2,9 +2,8 @@ using System.IO; using System.Linq; using System.Reflection; -using OpenTabletDriver.Desktop.Interop; using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.Plugin; +using OpenTabletDriver.Interop; namespace OpenTabletDriver.Desktop.Reflection { @@ -19,7 +18,7 @@ public DesktopPluginContext(DirectoryInfo directory) { // Ignore a plugin library build artifact // Loading it seems to stop loading any further DLLs from the directory - if (string.Equals(plugin.Name, "OpenTabletDriver.Plugin.dll", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(plugin.Name, "OpenTabletDriver.dll", StringComparison.OrdinalIgnoreCase)) continue; LoadAssemblyFromFile(plugin); @@ -37,16 +36,14 @@ public PluginMetadata GetMetadata() { return Serialization.Deserialize(file); } - else + + return new PluginMetadata { - return new PluginMetadata - { - Name = FriendlyName, - }; - } + Name = FriendlyName + }; } - protected Assembly LoadAssemblyFromFile(FileInfo file) + private Assembly LoadAssemblyFromFile(FileInfo file) { try { @@ -63,27 +60,30 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { if (Directory == null) { - Log.Write("Plugin", $"Independent plugin does not support loading native library '{unmanagedDllName}'", LogLevel.Warning); + Log.Write("Plugin", $"Independent plugin does not support loading native library '{unmanagedDllName}'", + LogLevel.Warning); throw new NotSupportedException(); } var runtimeFolder = new DirectoryInfo(Path.Join(Directory.FullName, "runtimes")); if (runtimeFolder.Exists) { - var libraryFile = runtimeFolder.EnumerateFiles(ToDllName(unmanagedDllName), SearchOption.AllDirectories).FirstOrDefault(); + var libraryFile = runtimeFolder.EnumerateFiles(ToDllName(unmanagedDllName), SearchOption.AllDirectories) + .FirstOrDefault(); if (libraryFile != null) return LoadUnmanagedDllFromPath(libraryFile.FullName); } + return IntPtr.Zero; } private static string ToDllName(string dllName) { - return DesktopInterop.CurrentPlatform switch + return SystemInterop.CurrentPlatform switch { - PluginPlatform.Windows => $"{dllName}.dll", - PluginPlatform.Linux => $"lib{dllName}.so", - PluginPlatform.MacOS => $"lib{dllName}.dylib", + SystemPlatform.Windows => $"{dllName}.dll", + SystemPlatform.Linux => $"lib{dllName}.so", + SystemPlatform.MacOS => $"lib{dllName}.dylib", _ => null }; } diff --git a/OpenTabletDriver.Desktop/Reflection/DesktopPluginManager.cs b/OpenTabletDriver.Desktop/Reflection/DesktopPluginManager.cs deleted file mode 100644 index 6e34ae355..000000000 --- a/OpenTabletDriver.Desktop/Reflection/DesktopPluginManager.cs +++ /dev/null @@ -1,270 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.Desktop.Reflection -{ - public class DesktopPluginManager : PluginManager - { - public DesktopPluginManager() - : this(AppInfo.Current.PluginDirectory, AppInfo.Current.TrashDirectory, AppInfo.Current.TemporaryDirectory) - { - } - - protected DesktopPluginManager(string pluginDirectory, string trashDirectory, string tempDirectory) - : this(new DirectoryInfo(pluginDirectory), new DirectoryInfo(trashDirectory), new DirectoryInfo(tempDirectory)) - { - } - - public DesktopPluginManager(DirectoryInfo pluginDirectory, DirectoryInfo trashDirectory, DirectoryInfo tempDirectory) - { - PluginDirectory = pluginDirectory; - TrashDirectory = trashDirectory; - TemporaryDirectory = tempDirectory; - - if (!PluginDirectory.Exists) - PluginDirectory.Create(); - } - - public DirectoryInfo PluginDirectory { get; } - protected DirectoryInfo TrashDirectory { get; } - protected DirectoryInfo TemporaryDirectory { get; } - - protected List Plugins { get; } = new List(); - - public IReadOnlyCollection GetLoadedPlugins() => Plugins; - - public event EventHandler AssembliesChanged; - - public void Clean() - { - try - { - if (PluginDirectory.Exists) - { - foreach (var file in PluginDirectory.GetFiles()) - { - Log.Write("Plugin", $"Unexpected file found: '{file.FullName}'", LogLevel.Warning); - } - } - - if (TrashDirectory.Exists) - Directory.Delete(TrashDirectory.FullName, true); - if (TemporaryDirectory.Exists) - Directory.Delete(TemporaryDirectory.FullName, true); - } - catch (Exception ex) - { - Log.Exception(ex); - } - } - - public void Load() - { - foreach (var dir in PluginDirectory.GetDirectories()) - LoadPlugin(dir); - - AppInfo.PluginManager.ResetServices(); - AssembliesChanged?.Invoke(this, EventArgs.Empty); - } - - protected void LoadPlugin(DirectoryInfo directory) - { - // "Plugins" are directories that contain managed and unmanaged dll - // These dlls are loaded into a PluginContext per directory - directory.Refresh(); - if (Plugins.All(p => p.Directory.Name != directory.Name)) - { - if (directory.Exists) - { - Log.Write("Plugin", $"Loading plugin '{directory.Name}'", LogLevel.Debug); - var context = new DesktopPluginContext(directory); - - // Populate PluginTypes so desktop implementations can access them - ImportTypes(context); - Plugins.Add(context); - } - else - { - Log.Write("Plugin", $"Tried to load a nonexistent plugin '{directory.Name}'", LogLevel.Warning); - } - } - else - { - Log.Write("Plugin", $"Attempted to load the plugin {directory.Name} when it is already loaded.", LogLevel.Debug); - } - } - - protected void ImportTypes(PluginContext context) - { - var types = from asm in context.Assemblies - where IsLoadable(asm) - from type in asm.GetExportedTypes() - where IsPluginType(type) - select type; - - types.AsParallel().ForAll(type => - { - if (!IsPlatformSupported(type)) - { - Log.Write("Plugin", $"Plugin '{type.FullName}' is not supported on {DesktopInterop.CurrentPlatform}", LogLevel.Info); - return; - } - if (IsPluginIgnored(type)) - return; - - try - { - var pluginTypeInfo = type.GetTypeInfo(); - if (!pluginTypes.Contains(pluginTypeInfo)) - pluginTypes.Add(pluginTypeInfo); - } - catch - { - Log.Write("Plugin", $"Plugin '{type.FullName}' incompatible", LogLevel.Warning); - } - }); - } - - public bool InstallPlugin(string filePath) - { - var file = new FileInfo(filePath); - if (!file.Exists) - return false; - - var name = file.Name.Replace(file.Extension, string.Empty); - var tempDir = new DirectoryInfo(Path.Join(TemporaryDirectory.FullName, name)); - if (!tempDir.Exists) - tempDir.Create(); - - var pluginPath = Path.Join(AppInfo.Current.PluginDirectory, name); - var pluginDir = new DirectoryInfo(pluginPath); - switch (file.Extension) - { - case ".zip": - { - ZipFile.ExtractToDirectory(file.FullName, tempDir.FullName, true); - break; - } - case ".dll": - { - file.CopyTo(Path.Join(tempDir.FullName, file.Name)); - break; - } - default: - throw new InvalidOperationException($"Unsupported archive type: {file.Extension}"); - } - var context = Plugins.FirstOrDefault(ctx => ctx.Directory.FullName == pluginDir.FullName); - var result = pluginDir.Exists ? UpdatePlugin(context, tempDir) : InstallPlugin(pluginDir, tempDir); - - if (!TemporaryDirectory.GetFileSystemInfos().Any()) - Directory.Delete(TemporaryDirectory.FullName, true); - - if (result) - LoadPlugin(pluginDir); - return result; - } - - public async Task DownloadPlugin(PluginMetadata metadata) - { - string sourcePath = Path.Join(TemporaryDirectory.FullName, metadata.Name); - string targetPath = Path.Join(PluginDirectory.FullName, metadata.Name); - string metadataPath = Path.Join(targetPath, "metadata.json"); - - var sourceDir = new DirectoryInfo(sourcePath); - var targetDir = new DirectoryInfo(targetPath); - - await metadata.DownloadAsync(sourcePath); - - var context = Plugins.FirstOrDefault(ctx => ctx.Directory.FullName == targetDir.FullName); - var result = targetDir.Exists ? UpdatePlugin(context, sourceDir) : InstallPlugin(targetDir, sourceDir); - - using (var fs = File.Create(metadataPath)) - Serialization.Serialize(fs, metadata); - - if (!TemporaryDirectory.GetFileSystemInfos().Any()) - Directory.Delete(TemporaryDirectory.FullName, true); - return result; - } - - public bool InstallPlugin(DirectoryInfo target, DirectoryInfo source) - { - Log.Write("Plugin", $"Installing plugin '{target.Name}'"); - source.CopyTo(target); - LoadPlugin(target); - return true; - } - - public bool UninstallPlugin(DesktopPluginContext plugin) - { - if (plugin == null) - return false; - - var random = new Random(); - if (!Directory.Exists(TrashDirectory.FullName)) - TrashDirectory.Create(); - - Log.Write("Plugin", $"Uninstalling plugin '{plugin.FriendlyName}'"); - - var trashPath = Path.Join(TrashDirectory.FullName, $"{plugin.FriendlyName}_{random.Next()}"); - Directory.Move(plugin.Directory.FullName, trashPath); - - return UnloadPlugin(plugin); - } - - public bool UpdatePlugin(DesktopPluginContext plugin, DirectoryInfo source) - { - var targetDir = new DirectoryInfo(plugin.Directory.FullName); - if (UninstallPlugin(plugin)) - return InstallPlugin(targetDir, source); - return false; - } - - public bool UnloadPlugin(DesktopPluginContext context) - { - Log.Write("Plugin", $"Unloading plugin '{context.FriendlyName}'", LogLevel.Debug); - Plugins.Remove(context); - AssembliesChanged?.Invoke(this, EventArgs.Empty); - return context.Assemblies.All(p => RemoveAllTypesForAssembly(p)); - } - - public bool RemoveAllTypesForAssembly(Assembly asm) - { - try - { - var types = pluginTypes.Where(t => t.Assembly == asm) - .Select(t => t.GetTypeInfo()); - - pluginTypes = new ConcurrentBag(pluginTypes.Except(types)); - return true; - } - catch (Exception ex) - { - Log.Exception(ex); - return false; - } - } - - public override void ResetServices() - { - base.ResetServices(); - - // These services will always be provided on the desktop - AddService(() => this); - AddService(() => DesktopInterop.Timer); - AddService(() => DesktopInterop.AbsolutePointer); - AddService(() => DesktopInterop.RelativePointer); - AddService(() => DesktopInterop.VirtualTablet); - AddService(() => DesktopInterop.VirtualScreen); - AddService(() => DesktopInterop.VirtualKeyboard); - } - } -} diff --git a/OpenTabletDriver.Desktop/Reflection/Extensions.cs b/OpenTabletDriver.Desktop/Reflection/Extensions.cs index 3222e2db5..b33691015 100644 --- a/OpenTabletDriver.Desktop/Reflection/Extensions.cs +++ b/OpenTabletDriver.Desktop/Reflection/Extensions.cs @@ -1,35 +1,146 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Logging; + +#nullable enable namespace OpenTabletDriver.Desktop.Reflection { - internal static class Extensions + public static class Extensions { - public static void CopyTo(this DirectoryInfo source, DirectoryInfo destination) + public static string GetFullyQualifiedName(this Type type) { - if (!source.Exists) + if (type.GenericTypeArguments.Any()) { - throw new DirectoryNotFoundException( - "Source directory does not exist or could not be found: " - + source.FullName); + var name = type.Name; + var index = name.IndexOf('`'); + var query = type.GenericTypeArguments.Select(t => t.GetFullyQualifiedName()); + return type.Namespace + "." + name[..index] + "<" + string.Join(", ", query) + ">"; } - // If the destination directory doesn't exist, create it. - destination.Create(); + return type.GetPath(); + } + + public static string GetPath(this Type type) + { + return type.Namespace + "." + type.Name; + } + + public static string? GetFriendlyName(this Type type) + { + return type.GetCustomAttribute()?.Name; + } - // Get the files in the directory and copy them to the new location. - foreach (var file in source.GetFiles()) + public static bool IsPlatformSupported(this TypeInfo type) + { + var attr = type.GetCustomAttribute(); + return attr?.IsCurrentPlatform ?? true; + } + + public static bool IsLoadable(this Assembly asm) + { + try { - string tempPath = Path.Combine(destination.FullName, file.Name); - file.CopyTo(tempPath, false); + _ = asm.DefinedTypes; + return true; } + catch (Exception ex) + { + var asmName = asm.GetName(); + var hResultHex = ex.HResult.ToString("X"); + var message = new LogMessage + { + Group = "Plugin", + Level = LogLevel.Warning, + Message = $"Plugin '{asmName.Name}, Version={asmName.Version}' can't be loaded and is likely out of date. (HResult: 0x{hResultHex})", + StackTrace = ex.Message + Environment.NewLine + ex.StackTrace + }; + Log.Write(message); + return false; + } + } + + public static PluginSettings GetDefaultSettings( + this Type type, + IServiceProvider serviceProvider, + params object[] additionalDeps + ) + { + var settings = EnumerateDefaultSettings(type, serviceProvider, additionalDeps); + return new PluginSettings(type, settings); + } + + public static Exception GetInnermostException(this Exception e) + { + var ex = e; + while (ex.InnerException != null) + ex = ex.InnerException; + return ex; + } - foreach (DirectoryInfo subdir in source.GetDirectories()) + private static IEnumerable EnumerateDefaultSettings(this Type type, IServiceProvider serviceProvider, + object[] additionalDeps) + { + var settingProperties = from property in type.GetProperties() + where property.GetCustomAttribute() != null + select property; + + foreach (var property in settingProperties) { - CopyTo( - new DirectoryInfo(subdir.FullName), - new DirectoryInfo(Path.Combine(destination.FullName, subdir.Name)) - ); + var defaultValue = GetDefaultValue(type, property, serviceProvider, additionalDeps); + if (defaultValue != null) + { + yield return new PluginSetting(property, defaultValue); + } } } + + private static object? GetDefaultValue( + this Type type, + PropertyInfo property, + IServiceProvider serviceProvider, + object[] additionalServices + ) + { + var value = property.GetCustomAttribute()?.Value; + return value ?? property.GetCustomAttribute() + ?.GetDefaultValue(serviceProvider, type, additionalServices); + } + + private static object? GetDefaultValue( + this MemberSourcedDefaultsAttribute attribute, + IServiceProvider serviceProvider, + Type sourceType, + object[] additionalServices + ) + { + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy; + + var targetMemberName = attribute.TargetMemberName; + + if (sourceType.GetMethod(targetMemberName, bindingFlags) is MethodInfo defaultMethod) + return defaultMethod.InvokeWithProvider(serviceProvider, additionalServices); + + if (sourceType.GetProperty(targetMemberName, bindingFlags) is PropertyInfo defaultProperty) + return defaultProperty.GetValue(null); + + if (sourceType.GetField(targetMemberName, bindingFlags) is FieldInfo defaultField) + return defaultField.GetValue(null); + + return null; + } + + private static object? InvokeWithProvider(this MethodInfo method, IServiceProvider provider, object[] additionalServices) + { + var args = from parameter in method.GetParameters() + let type = parameter.ParameterType + select provider.GetService(type) ?? additionalServices.First(s => s.GetType().IsAssignableTo(type)); + + return method.Invoke(null, args.ToArray()); + } } } diff --git a/OpenTabletDriver.Desktop/Reflection/IPluginFactory.cs b/OpenTabletDriver.Desktop/Reflection/IPluginFactory.cs new file mode 100644 index 000000000..04dde8d49 --- /dev/null +++ b/OpenTabletDriver.Desktop/Reflection/IPluginFactory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Reflection +{ + public interface IPluginFactory + { + T? Construct(PluginSettings settings, params object[] args) where T : class; + T? Construct(string fullPath, params object[] args) where T : class; + Type? GetPluginType(string path); + IEnumerable GetMatchingTypes(Type baseType); + string? GetFriendlyName(string path); + } +} diff --git a/OpenTabletDriver.Desktop/Reflection/IPluginManager.cs b/OpenTabletDriver.Desktop/Reflection/IPluginManager.cs new file mode 100644 index 000000000..7fd7cabd5 --- /dev/null +++ b/OpenTabletDriver.Desktop/Reflection/IPluginManager.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using OpenTabletDriver.Desktop.Reflection.Metadata; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Reflection +{ + public interface IPluginManager + { + event EventHandler? AssembliesChanged; + IEnumerable Assemblies { get; } + IReadOnlyList Plugins { get; } + IEnumerable ExportedTypes { get; } + IEnumerable LibraryTypes { get; } + IEnumerable PluginTypes { get; } + + void Clean(); + void Load(); + bool InstallPlugin(string filePath); + Task DownloadPlugin(PluginMetadata metadata); + bool InstallPlugin(DirectoryInfo target, DirectoryInfo source); + bool UninstallPlugin(DesktopPluginContext plugin); + bool UpdatePlugin(DesktopPluginContext plugin, DirectoryInfo source); + bool UnloadPlugin(DesktopPluginContext context); + string GetStateHash(); + } +} diff --git a/OpenTabletDriver.Desktop/Reflection/IServiceManager.cs b/OpenTabletDriver.Desktop/Reflection/IServiceManager.cs deleted file mode 100644 index cdd628368..000000000 --- a/OpenTabletDriver.Desktop/Reflection/IServiceManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace OpenTabletDriver.Desktop.Reflection -{ - public interface IServiceManager : IServiceProvider - { - bool AddService(Func value); - void ResetServices(); - } -} diff --git a/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadata.cs b/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadata.cs index e3b47336f..b74e2a1b3 100644 --- a/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadata.cs +++ b/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadata.cs @@ -1,70 +1,81 @@ using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; +using JetBrains.Annotations; using OpenTabletDriver.Desktop.Compression; +#nullable enable + namespace OpenTabletDriver.Desktop.Reflection.Metadata { + [UsedImplicitly(ImplicitUseTargetFlags.Members)] public class PluginMetadata { /// /// The name of the plugin. /// - public string Name { set; get; } + public string Name { set; get; } = null!; /// /// The owner of the plugin's source code repository. /// - public string Owner { set; get; } + public string? Owner { set; get; } /// /// The plugin's long description. /// - public string Description { set; get; } + public string? Description { set; get; } /// /// The plugins' version. /// Newer supported versions will be preferred by default. /// - public Version PluginVersion { set; get; } + [DisplayName("Plugin Version")] + public Version? PluginVersion { set; get; } /// /// The plugin's minimum supported OpenTabletDriver version, /// - public Version SupportedDriverVersion { set; get; } + [DisplayName("Supported Driver Version")] + public Version? SupportedDriverVersion { set; get; } /// /// The plugin's source code repository URL. /// - public string RepositoryUrl { set; get; } + [DisplayName("Source Code Repository"), Url] + public string? RepositoryUrl { set; get; } /// /// The plugin's binary download URL. /// - public string DownloadUrl { set; get; } + [DisplayName("Download"), Url] + public string? DownloadUrl { set; get; } /// /// The compression format used in the binary download from . /// - public string CompressionFormat { set; get; } + public string? CompressionFormat { set; get; } /// /// The SHA256 hash of the file at , used for verifying file integrity. /// - public string SHA256 { set; get; } + public string? SHA256 { set; get; } /// /// The plugin's wiki URL. /// - public string WikiUrl { set; get; } + [DisplayName("Wiki"), Url] + public string? WikiUrl { set; get; } /// /// The SPDX license identifier expression. /// - public string LicenseIdentifier { set; get; } + [DisplayName("License")] + public string? LicenseIdentifier { set; get; } public static string GetSHA256(Stream stream) { @@ -81,7 +92,7 @@ public bool VerifySHA256(Stream stream) return GetSHA256(stream) == SHA256; } - public async Task GetDownloadStream() + public async Task GetDownloadStream() { using (var client = PluginMetadataCollection.GetClient()) { @@ -101,7 +112,7 @@ public async Task DownloadAsync(string outputDirectory) // Verify SHA256 hash if (SHA256 == null || VerifySHA256(stream)) { - stream.Decompress(outputDirectory, this.CompressionFormat); + stream.Decompress(outputDirectory, CompressionFormat); } else { @@ -113,7 +124,7 @@ public async Task DownloadAsync(string outputDirectory) public bool IsSupportedBy(Version appVersion) { // Always return false when major and minor is not equal (x.y.0.0). - if (SupportedDriverVersion.Major != appVersion.Major) + if (SupportedDriverVersion!.Major != appVersion.Major) return false; if (SupportedDriverVersion.Minor != appVersion.Minor) return false; @@ -127,14 +138,11 @@ public bool IsSupportedBy(Version appVersion) return true; } - public static bool Match(PluginMetadata primary, PluginMetadata secondary) + public bool Match(PluginMetadata secondary) { - if (primary == null || secondary == null) - return false; - - return primary.Name == secondary.Name && - primary.Owner == secondary.Owner && - primary.RepositoryUrl == secondary.RepositoryUrl; + return Name == secondary.Name && + Owner == secondary.Owner && + RepositoryUrl == secondary.RepositoryUrl; } } } diff --git a/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadataCollection.cs b/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadataCollection.cs index 5617a6681..f1d39d60f 100644 --- a/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadataCollection.cs +++ b/OpenTabletDriver.Desktop/Reflection/Metadata/PluginMetadataCollection.cs @@ -9,6 +9,7 @@ using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using Newtonsoft.Json; +using OpenTabletDriver.Desktop.Interop.AppInfo; namespace OpenTabletDriver.Desktop.Reflection.Metadata { @@ -16,7 +17,6 @@ public class PluginMetadataCollection : Collection { [JsonConstructor] protected PluginMetadataCollection() - : base() { } @@ -42,20 +42,20 @@ public static async Task DownloadAsync() return await DownloadAsync(REPOSITORY_OWNER, REPOSITORY_NAME); } - public static async Task DownloadAsync(string owner, string name, string gitRef = null) + public static async Task DownloadAsync(IAppInfo appInfo, string owner, string name, string gitRef) { string archiveUrl = $"https://api.github.com/repos/{owner}/{name}/tarball/{gitRef}"; - return await DownloadAsync(archiveUrl); + return await DownloadAsync(archiveUrl, appInfo.CacheDirectory); } - public static async Task DownloadAsync(string archiveUrl) + private static async Task DownloadAsync(string archiveUrl, string cacheDir) { using (var client = GetClient()) using (var httpStream = await client.GetStreamAsync(archiveUrl)) - return FromStream(httpStream); + return FromStream(httpStream, cacheDir); } - public static PluginMetadataCollection FromStream(Stream stream) + private static PluginMetadataCollection FromStream(Stream stream, string cacheDir) { var memStream = new MemoryStream(); stream.CopyTo(memStream); @@ -65,20 +65,20 @@ public static PluginMetadataCollection FromStream(Stream stream) using (var archive = TarArchive.CreateInputTarArchive(gzipStream, null)) { string hash = CalculateSHA256(memStream); - string cacheDir = Path.Join(AppInfo.Current.CacheDirectory, $"{hash}-OpenTabletDriver-PluginMetadata"); + string metadataCacheDir = Path.Join(cacheDir, $"{hash}-OpenTabletDriver-PluginMetadata"); - if (Directory.Exists(cacheDir)) - Directory.Delete(cacheDir, true); - archive.ExtractContents(cacheDir); + if (Directory.Exists(metadataCacheDir)) + Directory.Delete(metadataCacheDir, true); + archive.ExtractContents(metadataCacheDir); - var collection = EnumeratePluginMetadata(cacheDir); + var collection = EnumeratePluginMetadata(metadataCacheDir); var metadataCollection = new PluginMetadataCollection(collection); return metadataCollection; } } - protected static string CalculateSHA256(Stream stream) + private static string CalculateSHA256(Stream stream) { using (var sha256 = SHA256.Create()) { @@ -94,7 +94,7 @@ protected static string CalculateSHA256(Stream stream) } } - protected static IEnumerable EnumeratePluginMetadata(string directoryPath) + private static IEnumerable EnumeratePluginMetadata(string directoryPath) { foreach (var file in Directory.EnumerateFiles(directoryPath, "*.json", SearchOption.AllDirectories)) using (var fs = File.OpenRead(file)) diff --git a/OpenTabletDriver.Desktop/Reflection/PluginContext.cs b/OpenTabletDriver.Desktop/Reflection/PluginContext.cs index c601da108..bd29e36f2 100644 --- a/OpenTabletDriver.Desktop/Reflection/PluginContext.cs +++ b/OpenTabletDriver.Desktop/Reflection/PluginContext.cs @@ -6,7 +6,7 @@ namespace OpenTabletDriver.Desktop.Reflection { public class PluginContext : AssemblyLoadContext { - protected const string PLUGIN_ASSEMBLY_NAMESPACE = nameof(OpenTabletDriver.Plugin); + protected const string PLUGIN_ASSEMBLY_NAMESPACE = nameof(OpenTabletDriver); protected static readonly Assembly PluginAssembly = Default.Assemblies.FirstOrDefault(asm => asm.GetName().FullName == PLUGIN_ASSEMBLY_NAMESPACE); protected override Assembly Load(AssemblyName assemblyName) diff --git a/OpenTabletDriver.Desktop/Reflection/PluginFactory.cs b/OpenTabletDriver.Desktop/Reflection/PluginFactory.cs new file mode 100644 index 000000000..f72490766 --- /dev/null +++ b/OpenTabletDriver.Desktop/Reflection/PluginFactory.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OpenTabletDriver.Attributes; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Reflection +{ + public class PluginFactory : IPluginFactory + { + private readonly IServiceProvider _serviceProvider; + private readonly IPluginManager _pluginManager; + + public PluginFactory(IServiceProvider serviceProvider, IPluginManager pluginManager) + { + _serviceProvider = serviceProvider; + _pluginManager = pluginManager; + } + + public T? Construct(PluginSettings? settings, params object[] args) where T : class + { + if (settings == null) + return null; + + var settingsProvider = new PluginSettingsProvider(settings); + var newArgs = args.Append(settingsProvider).ToArray(); + + return Construct(settings.Path, newArgs); + } + + public T? Construct(string path, params object[] args) where T : class + { + var type = GetPluginType(path); + if (type != null) + { + try + { + return _serviceProvider.CreateInstance(type, args) as T; + } + catch (TargetInvocationException e) when (e.Message == "Exception has been thrown by the target of an invocation.") + { + Log.Write("Plugin", "Object construction has thrown an exception", LogLevel.Error); + Log.Exception(e.GetInnermostException()); + } + catch (Exception e) + { + Log.Write("Plugin", $"Unable to construct object '{path}'", LogLevel.Error, e.StackTrace); + Log.Exception(e); + } + } + else + { + Log.Write("Plugin", $"No constructor found for '{path}'", LogLevel.Warning); + } + + return null; + } + + public Type? GetPluginType(string path) + { + return _pluginManager.PluginTypes.FirstOrDefault(t => t.FullName == path); + } + + public IEnumerable GetMatchingTypes(Type baseType) + { + return from type in _pluginManager.PluginTypes + where type.IsAssignableTo(baseType) + where !type.IsAbstract + where IsPlatformSupported(type) + where !IsPluginIgnored(type) + select type; + } + + public string? GetFriendlyName(string path) + { + var type = GetPluginType(path); + var name = type?.GetCustomAttribute()?.Name; + return name; + } + + private static bool IsPlatformSupported(Type type) + { + var attr = type.GetCustomAttribute(typeof(SupportedPlatformAttribute), false) as SupportedPlatformAttribute; + return attr?.IsCurrentPlatform ?? true; + } + + private static bool IsPluginIgnored(Type type) + { + return type.GetCustomAttributes(false).Any(a => a.GetType() == typeof(PluginIgnoreAttribute)); + } + } +} diff --git a/OpenTabletDriver.Desktop/Reflection/PluginManager.cs b/OpenTabletDriver.Desktop/Reflection/PluginManager.cs index 00dfddf7f..3d4ab917a 100644 --- a/OpenTabletDriver.Desktop/Reflection/PluginManager.cs +++ b/OpenTabletDriver.Desktop/Reflection/PluginManager.cs @@ -1,194 +1,270 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Reflection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Logging; +using System.Security.Cryptography; +using System.Threading.Tasks; +using OpenTabletDriver.Desktop.Interop.AppInfo; +using OpenTabletDriver.Desktop.Reflection.Metadata; + +#nullable enable namespace OpenTabletDriver.Desktop.Reflection { - public class PluginManager : ServiceManager + public class PluginManager : IPluginManager { - public PluginManager() + public PluginManager(IAppInfo appInfo) + : this(appInfo.PluginDirectory, appInfo.TrashDirectory, appInfo.TemporaryDirectory) { - var assemblies = new[] - { - Assembly.Load("OpenTabletDriver.Desktop"), - Assembly.Load("OpenTabletDriver.Configurations"), - Assembly.Load("OpenTabletDriver.Plugin") - }; - - libTypes = (from type in typeof(IDriver).Assembly.GetExportedTypes() - where type.IsAbstract || type.IsInterface - select type).ToArray(); - - var internalTypes = from asm in assemblies - from type in asm.DefinedTypes - where type.IsPublic && !(type.IsInterface || type.IsAbstract) - where IsPluginType(type) - where IsPlatformSupported(type) - select type; - - pluginTypes = new ConcurrentBag(internalTypes); } - public IReadOnlyCollection PluginTypes => pluginTypes; - protected ConcurrentBag pluginTypes; + public PluginManager(string pluginDirectory, string trashDirectory, string temporaryDirectory) + { + PluginDirectory = new DirectoryInfo(pluginDirectory); + TrashDirectory = new DirectoryInfo(trashDirectory); + TemporaryDirectory = new DirectoryInfo(temporaryDirectory); + + if (!PluginDirectory.Exists) + PluginDirectory.Create(); + } + + private readonly List _plugins = new List(); + + private readonly IEnumerable _coreAssemblies = new[] + { + Assembly.Load("OpenTabletDriver"), + Assembly.Load("OpenTabletDriver.Desktop"), + Assembly.Load("OpenTabletDriver.Configurations") + }; + + private DirectoryInfo PluginDirectory { get; } + private DirectoryInfo TrashDirectory { get; } + private DirectoryInfo TemporaryDirectory { get; } - protected readonly Type[] libTypes; + public IReadOnlyList Plugins => _plugins; - public virtual T ConstructObject(string name, object[] args = null) where T : class + public IEnumerable Assemblies => Plugins.SelectMany(c => c.Assemblies).Concat(_coreAssemblies); + + public IEnumerable ExportedTypes => Assemblies.SelectMany(r => r.ExportedTypes); + + public IEnumerable LibraryTypes => from asm in Assemblies + where asm.IsLoadable() + from type in asm.GetExportedTypes() + where type.IsAbstract || type.IsInterface + select type; + + public IEnumerable PluginTypes => from asm in Assemblies + from type in asm.DefinedTypes + where type.IsPublic && !(type.IsInterface || type.IsAbstract) + where IsPluginType(type) + where type.IsPlatformSupported() + select type; + + private bool IsPluginType(Type type) { - args ??= new object[0]; - if (!string.IsNullOrWhiteSpace(name)) + return LibraryTypes.Any(t => + t.IsAssignableFrom(type) || + type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == t)); + } + + public event EventHandler? AssembliesChanged; + + public void Clean() + { + try { - try + if (PluginDirectory.Exists) { - if (PluginTypes.FirstOrDefault(t => t.FullName == name) is TypeInfo type) + foreach (var file in PluginDirectory.GetFiles()) { - var matchingConstructors = from ctor in type.GetConstructors() - let parameters = ctor.GetParameters() - where parameters.Length == args.Length - where IsValidParameterFor(args, parameters) - select ctor; - - if (matchingConstructors.FirstOrDefault() is ConstructorInfo constructor) - { - T obj = (T)constructor.Invoke(args) ?? null; - - if (obj != null) - Inject(this, obj, type); - return obj; - } - else - { - Log.Write("Plugin", $"No constructor found for '{name}'", LogLevel.Error); - } + Log.Write("Plugin", $"Unexpected file found: '{file.FullName}'", LogLevel.Warning); } } - catch (TargetInvocationException e) when (e.Message == "Exception has been thrown by the target of an invocation.") - { - Log.Write("Plugin", "Object construction has thrown an error", LogLevel.Error); - Log.Exception(e.InnerException); - } - catch (Exception e) - { - Log.Write("Plugin", $"Unable to construct object '{name}'", LogLevel.Error); - Log.Exception(e); - } + + if (TrashDirectory.Exists) + Directory.Delete(TrashDirectory.FullName, true); + if (TemporaryDirectory.Exists) + Directory.Delete(TemporaryDirectory.FullName, true); + } + catch (Exception ex) + { + Log.Exception(ex); } - return null; } - public virtual IReadOnlyCollection GetChildTypes() + public void Load() { - var children = from type in PluginTypes - where typeof(T).IsAssignableFrom(type) - select type; + foreach (var dir in PluginDirectory.GetDirectories()) + LoadPlugin(dir); - return children.ToArray(); + AssembliesChanged?.Invoke(this, EventArgs.Empty); } - public virtual string GetFriendlyName(string path) + public bool InstallPlugin(string filePath) { - if (AppInfo.PluginManager.PluginTypes.FirstOrDefault(t => t.FullName == path) is TypeInfo plugin) + var file = new FileInfo(filePath); + if (!file.Exists) + return false; + + var name = file.Name.Replace(file.Extension, string.Empty); + var tempDir = new DirectoryInfo(Path.Join(TemporaryDirectory.FullName, name)); + if (!tempDir.Exists) + tempDir.Create(); + + var pluginPath = Path.Join(PluginDirectory.FullName, name); + var pluginDir = new DirectoryInfo(pluginPath); + switch (file.Extension) { - var attrs = plugin.GetCustomAttributes(true); - var nameattr = attrs.FirstOrDefault(t => t.GetType() == typeof(PluginNameAttribute)); - if (nameattr is PluginNameAttribute attr) - return attr.Name; + case ".zip": + { + ZipFile.ExtractToDirectory(file.FullName, tempDir.FullName, true); + break; + } + case ".dll": + { + file.CopyTo(Path.Join(tempDir.FullName, file.Name)); + break; + } + default: + throw new InvalidOperationException($"Unsupported archive type: {file.Extension}"); } - return null; + var context = Plugins.FirstOrDefault(ctx => ctx.Directory.FullName == pluginDir.FullName); + var result = pluginDir.Exists ? UpdatePlugin(context!, tempDir) : InstallPlugin(pluginDir, tempDir); + + if (!TemporaryDirectory.GetFileSystemInfos().Any()) + Directory.Delete(TemporaryDirectory.FullName, true); + + if (result) + LoadPlugin(pluginDir); + return result; } - public static void Inject(IServiceProvider serviceProvider, object obj) + public async Task DownloadPlugin(PluginMetadata metadata) { - if (obj != null) - Inject(serviceProvider, obj, obj.GetType()); + var sourcePath = Path.Join(TemporaryDirectory.FullName, Guid.NewGuid().ToString()); + var targetPath = Path.Join(PluginDirectory.FullName, metadata.Name); + var metadataPath = Path.Join(targetPath, "metadata.json"); + + var sourceDir = new DirectoryInfo(sourcePath); + var targetDir = new DirectoryInfo(targetPath); + + await metadata.DownloadAsync(sourcePath); + + var context = Plugins.FirstOrDefault(ctx => ctx.Directory.FullName == targetDir.FullName); + var result = targetDir.Exists ? UpdatePlugin(context!, sourceDir) : InstallPlugin(targetDir, sourceDir); + + await using (var fs = File.Create(metadataPath)) + Serialization.Serialize(fs, metadata); + + if (!TemporaryDirectory.GetFileSystemInfos().Any()) + Directory.Delete(TemporaryDirectory.FullName, true); + return result; } - public static void Inject(IServiceProvider serviceProvider, object obj, Type type) + public bool InstallPlugin(DirectoryInfo target, DirectoryInfo source) { - if (obj == null) - return; + Log.Write("Plugin", $"Installing plugin '{target.Name}'"); + CopyDirectory(source, target); + LoadPlugin(target); + return true; + } - var resolvedProperties = from property in type.GetProperties() - where property.GetCustomAttribute() is ResolvedAttribute - select property; + public bool UninstallPlugin(DesktopPluginContext plugin) + { + var random = new Random(); + if (!Directory.Exists(TrashDirectory.FullName)) + TrashDirectory.Create(); - foreach (var property in resolvedProperties) - { - var service = serviceProvider.GetService(property.PropertyType); - if (service != null) - property.SetValue(obj, service); - } + Log.Write("Plugin", $"Uninstalling plugin '{plugin.FriendlyName}'"); - var resolvedFields = from field in type.GetFields() - where field.GetCustomAttribute() is ResolvedAttribute - select field; + var trashPath = Path.Join(TrashDirectory.FullName, $"{plugin.FriendlyName}_{random.Next()}"); + Directory.Move(plugin.Directory.FullName, trashPath); - foreach (var field in resolvedFields) - { - var service = serviceProvider.GetService(field.FieldType); - if (service != null) - field.SetValue(obj, service); - } + return UnloadPlugin(plugin); } - protected virtual bool IsValidParameterFor(object[] args, ParameterInfo[] parameters) + public bool UpdatePlugin(DesktopPluginContext plugin, DirectoryInfo source) { - for (int i = 0; i < parameters.Length; i++) - { - var parameter = parameters[i]; - var arg = args[i]; - if (!parameter.ParameterType.IsAssignableFrom(arg.GetType())) - return false; - } - return true; + // TODO: Fix update not updating the plugin? + var targetDir = new DirectoryInfo(plugin.Directory.FullName); + if (UninstallPlugin(plugin)) + return InstallPlugin(targetDir, source); + return false; } - protected virtual bool IsPluginType(Type type) + public bool UnloadPlugin(DesktopPluginContext context) { - return !type.IsAbstract && !type.IsInterface && - libTypes.Any(t => t.IsAssignableFrom(type) || - type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == t)); + Log.Write("Plugin", $"Unloading plugin '{context.FriendlyName}'", LogLevel.Debug); + _plugins.Remove(context); + AssembliesChanged?.Invoke(this, EventArgs.Empty); + return true; } - protected virtual bool IsPlatformSupported(Type type) + public string GetStateHash() { - var attr = (SupportedPlatformAttribute)type.GetCustomAttribute(typeof(SupportedPlatformAttribute), false); - return attr?.IsCurrentPlatform ?? true; + using var stream = new MemoryStream(); + using var sha = SHA256.Create(); + + foreach (var module in Assemblies.SelectMany(a => a.Modules)) + stream.Write(module.ModuleVersionId.ToByteArray()); + + stream.Position = 0; + var combined = sha.ComputeHash(stream); + return string.Concat(combined.Select(h => h.ToString("x2"))); } - protected virtual bool IsPluginIgnored(Type type) + private void LoadPlugin(DirectoryInfo directory) { - return type.GetCustomAttributes(false).Any(a => a.GetType() == typeof(PluginIgnoreAttribute)); + // "Plugins" are directories that contain managed and unmanaged dll + // These dlls are loaded into a PluginContext per directory + directory.Refresh(); + if (Plugins.All(p => p.Directory.Name != directory.Name)) + { + if (directory.Exists) + { + Log.Write("Plugin", $"Loading plugin '{directory.Name}'", LogLevel.Debug); + var context = new DesktopPluginContext(directory); + + _plugins.Add(context); + } + else + { + Log.Write("Plugin", $"Tried to load a nonexistent plugin '{directory.Name}'", LogLevel.Warning); + } + } + else + { + Log.Write("Plugin", $"Attempted to load the plugin {directory.Name} when it is already loaded.", LogLevel.Debug); + } } - protected virtual bool IsLoadable(Assembly asm) + private static void CopyDirectory(DirectoryInfo source, DirectoryInfo destination) { - try + if (!source.Exists) { - _ = asm.DefinedTypes; - return true; + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + source.FullName); } - catch (Exception ex) + + // If the destination directory doesn't exist, create it. + destination.Create(); + + // Get the files in the directory and copy them to the new location. + foreach (var file in source.GetFiles()) { - var asmName = asm.GetName(); - var hResultHex = ex.HResult.ToString("X"); - var message = new LogMessage - { - Group = "Plugin", - Level = LogLevel.Warning, - Message = $"Plugin '{asmName.Name}, Version={asmName.Version}' can't be loaded and is likely out of date. (HResult: 0x{hResultHex})", - StackTrace = ex.Message + Environment.NewLine + ex.StackTrace - }; - Log.Write(message); - return false; + string tempPath = Path.Combine(destination.FullName, file.Name); + file.CopyTo(tempPath, false); + } + + foreach (DirectoryInfo subdir in source.GetDirectories()) + { + CopyDirectory( + new DirectoryInfo(subdir.FullName), + new DirectoryInfo(Path.Combine(destination.FullName, subdir.Name)) + ); } } } diff --git a/OpenTabletDriver.Desktop/Reflection/PluginSetting.cs b/OpenTabletDriver.Desktop/Reflection/PluginSetting.cs index 69220b492..85a642832 100644 --- a/OpenTabletDriver.Desktop/Reflection/PluginSetting.cs +++ b/OpenTabletDriver.Desktop/Reflection/PluginSetting.cs @@ -1,29 +1,23 @@ using System; +using System.ComponentModel; using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; namespace OpenTabletDriver.Desktop.Reflection { - public class PluginSetting + public class PluginSetting : NotifyPropertyChanged { public PluginSetting(string property, object value) - : this() { Property = property; SetValue(value); } public PluginSetting(PropertyInfo property, object value) - : this(property.Name, value) - { - } - - public PluginSetting(PropertyInfo property) - : this(property, null) { + Property = property.Name; + SetValue(value ?? GetDefault(property)); } [JsonConstructor] @@ -31,23 +25,38 @@ private PluginSetting() { } + private string _property; + private JToken _value; + [JsonProperty] - public string Property { set; get; } + public string Property + { + set => RaiseAndSetIfChanged(ref _property, value); + get => _property; + } [JsonProperty] - public JToken Value { set; get; } + public JToken Value + { + set => RaiseAndSetIfChanged(ref _value, value); + get => _value; + } [JsonIgnore] public bool HasValue => Value != null && Value.Type != JTokenType.Null; - public void SetValue(object value) + public PluginSetting SetValue(object value) { + if (value is PluginSetting) + throw new InvalidOperationException(); + Value = value == null ? null : JToken.FromObject(value); + return this; } public T GetValue() { - return Value == null ? default(T) : Value.Type != JTokenType.Null ? Value.ToObject() : default(T); + return Value == null ? default : Value.Type != JTokenType.Null ? Value.ToObject() : default; } public object GetValue(Type asType) @@ -55,28 +64,20 @@ public object GetValue(Type asType) return Value == null ? default : Value.Type != JTokenType.Null ? Value.ToObject(asType) : default; } - public T GetValueOrDefault(PropertyInfo property) + private static object GetDefault(PropertyInfo property) { - if (this.HasValue) - { - return GetValue(); - } - else + if (property.GetCustomAttribute() is { Value: object value } && + value.GetType() == property.PropertyType) { - if (property.GetCustomAttribute() is DefaultPropertyValueAttribute defaults) - { - try - { - SetValue(defaults.Value); - return (T)defaults.Value; - } - catch (Exception e) - { - Log.Write(nameof(PluginSetting), $"Failed to get custom default of {property.Name}: {e.Message}"); - } - } - return default; + return value; } + + return null; + } + + public override string ToString() + { + return Property + ": " + GetValue(); } } } diff --git a/OpenTabletDriver.Desktop/Reflection/PluginSettingStore.cs b/OpenTabletDriver.Desktop/Reflection/PluginSettingStore.cs deleted file mode 100644 index 73ab27157..000000000 --- a/OpenTabletDriver.Desktop/Reflection/PluginSettingStore.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using Newtonsoft.Json; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Desktop.Reflection -{ - public class PluginSettingStore - { - private static readonly Type _tabletRefType = typeof(TabletReference); - - public PluginSettingStore(Type type, bool enable = true) - { - Path = type?.FullName; - Settings = type != null ? GetSettingsForType(type) : new ObservableCollection(); - Enable = enable; - } - - public PluginSettingStore(object source, bool enable = true) - { - if (source != null) - { - var sourceType = source.GetType(); - Path = sourceType.FullName; - Settings = GetSettingsForType(sourceType, source); - Enable = enable; - } - else - { - throw new NullReferenceException("Creating a plugin setting store from a null object is not allowed."); - } - } - - [JsonConstructor] - private PluginSettingStore() - { - } - - public string Path { set; get; } - - [JsonIgnore] - public string Name => AppInfo.PluginManager.GetFriendlyName(Path); - - public ObservableCollection Settings { set; get; } - - public bool Enable { set; get; } - - public T Construct(TabletReference tabletReference = null, bool trigger = true) where T : class - { - var obj = AppInfo.PluginManager.ConstructObject(Path); - ApplySettings(obj); - if (trigger) - TriggerEventMethods(obj, tabletReference); - return obj; - } - - public T Construct(IServiceManager provider, TabletReference tabletReference = null) where T : class - { - var obj = Construct(tabletReference, false); - PluginManager.Inject(provider, obj); - TriggerEventMethods(obj, tabletReference); - return obj; - } - - public static PluginSettingStore FromPath(string path) - { - var pathType = AppInfo.PluginManager.PluginTypes.FirstOrDefault(t => t.FullName == path); - return pathType != null ? new PluginSettingStore(pathType) : null; - } - - public void ApplySettings(object target) - { - if (target == null) - return; - - var properties = from property in target.GetType().GetProperties() - let attrs = property.GetCustomAttributes(true) - where attrs.Any(attr => attr is PropertyAttribute) - select property; - - foreach (var setting in Settings) - { - if (properties.FirstOrDefault(d => d.Name == setting.Property) is PropertyInfo property) - { - if (setting.HasValue) - property.SetValue(target, setting.GetValue(property.PropertyType)); - else if (property.GetCustomAttribute() is DefaultPropertyValueAttribute defaults) - property.SetValue(target, defaults.Value); - } - } - } - - private static ObservableCollection GetSettingsForType(Type targetType, object source = null) - { - var settings = from property in targetType.GetProperties() - where property.GetCustomAttribute() is PropertyAttribute - select new PluginSetting(property, source == null ? null : property.GetValue(source)); - return new ObservableCollection(settings); - } - - public PluginSetting this[string propertyName] - { - set - { - if (Settings.FirstOrDefault(t => t.Property == propertyName) is PluginSetting setting) - { - Settings.Remove(setting); - Settings.Add(value); - } - else - { - Settings.Add(value); - } - } - get - { - var result = Settings.FirstOrDefault(s => s.Property == propertyName); - if (result == null) - { - var newSetting = new PluginSetting(propertyName, null); - Settings.Add(newSetting); - return newSetting; - } - return result; - } - } - - public PluginSetting this[PropertyInfo property] - { - set => this[property.Name] = value; - get => this[property.Name]; - } - - public string GetHumanReadableString() - { - var name = Name; - string settings = string.Join(", ", this.Settings.Select(s => $"({s.Property}: {s.Value})")); - string suffix = Settings.Any() ? $": {settings}" : string.Empty; - return name + suffix; - } - - public TypeInfo GetTypeInfo() - { - return AppInfo.PluginManager.PluginTypes.FirstOrDefault(t => t.FullName == Path); - } - - public TypeInfo GetTypeInfo() - { - return AppInfo.PluginManager.GetChildTypes().FirstOrDefault(t => t.FullName == Path); - } - - private static void TriggerEventMethods(object obj, TabletReference tabletReference) - { - if (obj == null) - return; - - var properties = from property in obj.GetType().GetProperties() - let attr = property.GetCustomAttribute() - where attr != null && property.PropertyType == _tabletRefType - select property; - - foreach (var property in properties) - property.SetValue(obj, tabletReference); - - var methods = from method in obj.GetType().GetMethods() - let attr = method.GetCustomAttribute() - where attr != null - select method; - - foreach (var method in methods) - method.Invoke(obj, Array.Empty()); - } - } -} diff --git a/OpenTabletDriver.Desktop/Reflection/PluginSettings.cs b/OpenTabletDriver.Desktop/Reflection/PluginSettings.cs new file mode 100644 index 000000000..ff3724b82 --- /dev/null +++ b/OpenTabletDriver.Desktop/Reflection/PluginSettings.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using Newtonsoft.Json; +using OpenTabletDriver.Attributes; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Reflection +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public class PluginSettings + { + public PluginSettings(Type type, bool enable = true) + { + Path = type.FullName!; + Settings = GetSettingsForType(type); + Enable = enable; + } + + public PluginSettings(Type type, IEnumerable settings, bool enable = true) + { + Path = type.FullName!; + Settings = new ObservableCollection(settings); + Enable = enable; + } + + public PluginSettings(Type type, object settings, bool enable = true) + { + Path = type.FullName!; + Settings = GetSettingsFromObject(settings); + Enable = enable; + } + + [JsonConstructor] + private PluginSettings() + { + } + + public string Path { set; get; } = string.Empty; + + public ObservableCollection Settings { get; } = new ObservableCollection(); + + public bool Enable { set; get; } + + public PluginSetting this[string propertyName] + { + set + { + if (Settings.FirstOrDefault(t => t.Property == propertyName) is PluginSetting setting) + { + Settings.Remove(setting); + Settings.Add(value); + } + else + { + Settings.Add(value); + } + } + get + { + var result = Settings.FirstOrDefault(s => s.Property == propertyName); + if (result == null) + { + var newSetting = new PluginSetting(propertyName, null); + Settings!.Add(newSetting); + return newSetting; + } + return result; + } + } + + public override string ToString() + { + return base.ToString() + ": " + Path; + } + + private static ObservableCollection GetSettingsForType(Type targetType, object? source = null) + { + var settings = from property in targetType.GetProperties() + where property.GetCustomAttribute() != null + select new PluginSetting(property, source == null ? null : property.GetValue(source)); + + return new ObservableCollection(settings); + } + + private static ObservableCollection GetSettingsFromObject(object obj) + { + var type = obj.GetType(); + if (type.IsAssignableTo(typeof(PluginSettings))) + { + throw new ArgumentException( + $"Attempted to generate settings from a {nameof(PluginSettings)}.", + nameof(obj) + ); + } + + var settings = from property in type.GetProperties() + let name = property.Name + let value = property.GetValue(obj) + select new PluginSetting(name, value); + + return new ObservableCollection(settings); + } + } +} diff --git a/OpenTabletDriver.Desktop/Reflection/PluginSettingStoreCollection.cs b/OpenTabletDriver.Desktop/Reflection/PluginSettingsCollection.cs similarity index 56% rename from OpenTabletDriver.Desktop/Reflection/PluginSettingStoreCollection.cs rename to OpenTabletDriver.Desktop/Reflection/PluginSettingsCollection.cs index f60ccd9a1..5163e7744 100644 --- a/OpenTabletDriver.Desktop/Reflection/PluginSettingStoreCollection.cs +++ b/OpenTabletDriver.Desktop/Reflection/PluginSettingsCollection.cs @@ -5,18 +5,18 @@ namespace OpenTabletDriver.Desktop.Reflection { - public class PluginSettingStoreCollection : ObservableCollection + public class PluginSettingsCollection : ObservableCollection { - public PluginSettingStoreCollection() + public PluginSettingsCollection() { } - public PluginSettingStoreCollection(IEnumerable collection) + public PluginSettingsCollection(IEnumerable collection) : base(collection) { } - public PluginSettingStoreCollection Trim() + public PluginSettingsCollection Trim() { while (true) { @@ -27,7 +27,7 @@ public PluginSettingStoreCollection Trim() return this; } - public PluginSettingStoreCollection SetExpectedCount(int expectedCount) + public PluginSettingsCollection SetExpectedCount(int expectedCount) { while (Count < expectedCount) Add(null); @@ -35,14 +35,14 @@ public PluginSettingStoreCollection SetExpectedCount(int expectedCount) return this; } - public PluginSettingStore FromType(TypeInfo type) + public PluginSettings FromType(TypeInfo type) { if (type == null) return null; - var store = this.FirstOrDefault(s => s.Path == type.FullName) ?? new PluginSettingStore(type, false); - if (!this.Contains(store)) - this.Add(store); + var store = this.FirstOrDefault(s => s.Path == type.FullName) ?? new PluginSettings(type, false); + if (!Contains(store)) + Add(store); return store; } } diff --git a/OpenTabletDriver.Desktop/Reflection/ServiceManager.cs b/OpenTabletDriver.Desktop/Reflection/ServiceManager.cs deleted file mode 100644 index 03b4c7e72..000000000 --- a/OpenTabletDriver.Desktop/Reflection/ServiceManager.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using OpenTabletDriver.Plugin.DependencyInjection; - -namespace OpenTabletDriver.Desktop.Reflection -{ - public class ServiceManager : IServiceManager - { - private readonly IDictionary> services = new Dictionary>(); - - /// - /// Adds a retrieval method for a service type. - /// - /// The method in which returns the required service type. - /// The type in which is returned by the constructor. - /// True if adding the service was successful, otherwise false. - public bool AddService(Func value) - { - return services.TryAdd(typeof(T), (value as Func)); - } - - /// - /// Clears all of the added services. - /// - public virtual void ResetServices() - { - services.Clear(); - } - - public object GetService(Type serviceType) - { - return services.ContainsKey(serviceType) ? services[serviceType].Invoke() : null; - } - - public T GetService() where T : class => GetService(typeof(T)) as T; - } -} diff --git a/OpenTabletDriver.Desktop/Serialization.cs b/OpenTabletDriver.Desktop/Serialization.cs index 6b00805d0..3534c0971 100644 --- a/OpenTabletDriver.Desktop/Serialization.cs +++ b/OpenTabletDriver.Desktop/Serialization.cs @@ -1,7 +1,7 @@ using System.IO; using Newtonsoft.Json; using OpenTabletDriver.Desktop.Converters; -using OpenTabletDriver.Plugin; +using OpenTabletDriver.Desktop.Json; namespace OpenTabletDriver.Desktop { @@ -9,11 +9,11 @@ public static class Serialization { static Serialization() { - serializer.Error += SerializationErrorHandler; - serializer.Converters.Add(new VersionConverter()); + Serializer.Error += SerializationErrorHandler; + Serializer.Converters.Add(new VersionConverter()); } - private static readonly JsonSerializer serializer = new JsonSerializer + public static JsonSerializer Serializer { get; } = new AdvancedJsonSerializer { Formatting = Formatting.Indented }; @@ -55,12 +55,12 @@ public static void Serialize(Stream stream, object value) public static T Deserialize(JsonTextReader textReader) { - return serializer.Deserialize(textReader); + return Serializer.Deserialize(textReader); } public static void Serialize(JsonTextWriter textWriter, object value) { - serializer.Serialize(textWriter, value); + Serializer.Serialize(textWriter, value); } } } diff --git a/OpenTabletDriver.Desktop/Settings.cs b/OpenTabletDriver.Desktop/Settings.cs index 80e7fa632..7b6ac1a6d 100644 --- a/OpenTabletDriver.Desktop/Settings.cs +++ b/OpenTabletDriver.Desktop/Settings.cs @@ -3,75 +3,53 @@ using System.IO; using System.Text.RegularExpressions; using Newtonsoft.Json; -using OpenTabletDriver.Desktop.Migration; using OpenTabletDriver.Desktop.Profiles; using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin; + +#nullable enable namespace OpenTabletDriver.Desktop { - public class Settings : ViewModel + public class Settings : NotifyPropertyChanged { - private ProfileCollection profiles = new ProfileCollection(); - private bool lockUsableAreaDisplay, lockUsableAreaTablet; - private PluginSettingStoreCollection tools = new PluginSettingStoreCollection(); + private ProfileCollection _profiles = new ProfileCollection(); + private PluginSettingsCollection _tools = new PluginSettingsCollection(); [JsonProperty("Profiles")] public ProfileCollection Profiles { - set => this.RaiseAndSetIfChanged(ref profiles, value); - get => profiles; - } - - [JsonProperty("LockUsableAreaDisplay")] - public bool LockUsableAreaDisplay - { - set => this.RaiseAndSetIfChanged(ref this.lockUsableAreaDisplay, value); - get => this.lockUsableAreaDisplay; - } - - [JsonProperty("LockUsableAreaTablet")] - public bool LockUsableAreaTablet - { - set => this.RaiseAndSetIfChanged(ref this.lockUsableAreaTablet, value); - get => this.lockUsableAreaTablet; + set => RaiseAndSetIfChanged(ref _profiles, value); + get => _profiles; } [JsonProperty("Tools")] - public PluginSettingStoreCollection Tools + public PluginSettingsCollection Tools { - set => RaiseAndSetIfChanged(ref this.tools, value); - get => this.tools; + set => RaiseAndSetIfChanged(ref _tools, value); + get => _tools; } public static Settings GetDefaults() { return new Settings { - Profiles = GetDefaultProfiles(), - LockUsableAreaDisplay = true, - LockUsableAreaTablet = true + Profiles = new ProfileCollection() }; } - private static ProfileCollection GetDefaultProfiles() - { - return new ProfileCollection(AppInfo.PluginManager.GetService().Tablets); - } - #region Custom Serialization static Settings() { - serializer.Error += SerializationErrorHandler; + Serializer.Error += SerializationErrorHandler; } - private static readonly JsonSerializer serializer = new JsonSerializer + private static readonly JsonSerializer Serializer = new JsonSerializer { Formatting = Formatting.Indented }; - private static void SerializationErrorHandler(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args) + private static void SerializationErrorHandler(object? sender, Newtonsoft.Json.Serialization.ErrorEventArgs args) { args.ErrorContext.Handled = true; if (args.ErrorContext.Path is string path) @@ -80,19 +58,22 @@ private static void SerializationErrorHandler(object sender, Newtonsoft.Json.Ser return; var property = args.CurrentObject.GetType().GetProperty(path); - if (property != null && property.PropertyType == typeof(PluginSettingStore)) + if (property != null && property.PropertyType == typeof(PluginSettings)) { - var match = propertyValueRegex.Match(args.ErrorContext.Error.Message); + var match = PropertyValueRegex.Match(args.ErrorContext.Error.Message); if (match.Success) { - var objPath = SettingsMigrator.MigrateNamespace(match.Groups[1].Value); - var newValue = PluginSettingStore.FromPath(objPath); - if (newValue != null) - { - property.SetValue(args.CurrentObject, newValue); - Log.Write("Settings", $"Migrated {path} to {nameof(PluginSettingStore)}"); - return; - } + // TODO: Fix settings auto migration + // var objPath = SettingsMigrator.MigrateNamespace(match.Groups[1].Value); + // var newValue = PluginSettingStore.FromPath(objPath); + // if (newValue != null) + // { + // property.SetValue(args.CurrentObject, newValue); + // Log.Write("Settings", $"Migrated {path} to {nameof(PluginSettingStore)}"); + // return; + // } + Log.Write("Settings", "Ignoring failed migration temporarily.", LogLevel.Error); + return; } } Log.Write("Settings", $"Unable to migrate {path}", LogLevel.Error); @@ -101,15 +82,15 @@ private static void SerializationErrorHandler(object sender, Newtonsoft.Json.Ser Log.Exception(args.ErrorContext.Error); } - private static Regex propertyValueRegex = new Regex(PROPERTY_VALUE_REGEX, RegexOptions.Compiled); + private static readonly Regex PropertyValueRegex = new Regex(PROPERTY_VALUE_REGEX, RegexOptions.Compiled); private const string PROPERTY_VALUE_REGEX = "\\\"(.+?)\\\""; - public static Settings Deserialize(FileInfo file) + public static Settings? Deserialize(FileInfo file) { using (var stream = file.OpenRead()) using (var sr = new StreamReader(stream)) using (var jr = new JsonTextReader(sr)) - return serializer.Deserialize(jr); + return Serializer.Deserialize(jr); } public static void Recover(FileInfo file, Settings settings) @@ -118,24 +99,24 @@ public static void Recover(FileInfo file, Settings settings) using (var sr = new StreamReader(stream)) using (var jr = new JsonTextReader(sr)) { - void propertyWatch(object _, PropertyChangedEventArgs p) + void PropertyWatch(object? _, PropertyChangedEventArgs p) { - var prop = settings.GetType().GetProperty(p.PropertyName).GetValue(settings); + settings.GetType().GetProperty(p.PropertyName!)?.GetValue(settings); Log.Write("Settings", $"Recovered '{p.PropertyName}'", LogLevel.Debug); } - settings.PropertyChanged += propertyWatch; + settings.PropertyChanged += PropertyWatch; try { - serializer.Populate(jr, settings); + Serializer.Populate(jr, settings); } catch (JsonReaderException e) { Log.Write("Settings", $"Recovery ended. Reason: {e.Message}", LogLevel.Debug); } - settings.PropertyChanged -= propertyWatch; + settings.PropertyChanged -= PropertyWatch; } } @@ -148,7 +129,7 @@ public void Serialize(FileInfo file) using (var sw = file.CreateText()) using (var jw = new JsonTextWriter(sw)) - serializer.Serialize(jw, this); + Serializer.Serialize(jw, this); } catch (UnauthorizedAccessException) { diff --git a/OpenTabletDriver.Desktop/SettingsManager.cs b/OpenTabletDriver.Desktop/SettingsManager.cs new file mode 100644 index 000000000..e86f717c1 --- /dev/null +++ b/OpenTabletDriver.Desktop/SettingsManager.cs @@ -0,0 +1,38 @@ +using System.IO; +using OpenTabletDriver.Desktop.Interop.AppInfo; + +#nullable enable + +namespace OpenTabletDriver.Desktop +{ + public class SettingsManager : ISettingsManager + { + private readonly FileInfo _settingsFile; + + public SettingsManager(IAppInfo appInfo) + { + _settingsFile = new FileInfo(appInfo.SettingsFile); + + Settings = Settings.GetDefaults(); + } + + public Settings Settings { set; get; } + + public bool Load() => Load(_settingsFile); + + public void Save() => Save(_settingsFile); + + public bool Load(FileInfo file) + { + if (file.Exists) + Settings = Settings.Deserialize(file)!; + return file.Exists; + } + + public void Save(FileInfo file) + { + Settings.Serialize(file); + file.Refresh(); + } + } +} diff --git a/OpenTabletDriver.Desktop/Updater/GitHubUpdater.cs b/OpenTabletDriver.Desktop/Updater/GitHubUpdater.cs new file mode 100644 index 000000000..8929e9c44 --- /dev/null +++ b/OpenTabletDriver.Desktop/Updater/GitHubUpdater.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Octokit; +using OpenTabletDriver.Desktop.Interop.AppInfo; + +#nullable enable + +namespace OpenTabletDriver.Desktop.Updater +{ + public abstract class GitHubUpdater : Updater + { + private readonly IGitHubClient _github; + + protected GitHubUpdater(Version? currentVersion, IAppInfo appInfo, IGitHubClient client) + : base(currentVersion, appInfo.BinaryDirectory, appInfo.AppDataDirectory, appInfo.BackupDirectory) + { + _github = client; + } + + protected override async Task GetUpdate() + { + var release = await _github.Repository.Release.GetLatest("OpenTabletDriver", "OpenTabletDriver"); + var version = new Version(release!.TagName[1..]); // remove `v` from `vW.X.Y.Z + return new GitHubRelease(release, version); + } + } + + public record GitHubRelease(Release Release, Version Version) : UpdateInfo(Version); +} diff --git a/OpenTabletDriver.Desktop/Updater/GithubUpdater.cs b/OpenTabletDriver.Desktop/Updater/GithubUpdater.cs deleted file mode 100644 index c8cf0efd1..000000000 --- a/OpenTabletDriver.Desktop/Updater/GithubUpdater.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading.Tasks; -using Octokit; - -#nullable enable - -namespace OpenTabletDriver.Desktop.Updater -{ - public abstract class GithubUpdater : Updater - { - private readonly GitHubClient github = new(new ProductHeaderValue("OpenTabletDriver")); - - protected GithubUpdater(Version? currentVersion, string binaryDir, string appDataDir, string rollbackDir) - : base(currentVersion, binaryDir, appDataDir, rollbackDir) - { - } - - protected override async Task GetUpdate() - { - var release = await github.Repository.Release.GetLatest("OpenTabletDriver", "OpenTabletDriver"); - var version = new Version(release!.TagName[1..]); // remove `v` from `vW.X.Y.Z - return new GithubRelease(release, version); - } - } - - public record GithubRelease(Release Release, Version Version) : UpdateInfo(Version); -} diff --git a/OpenTabletDriver.Desktop/Updater/MacOSUpdater.cs b/OpenTabletDriver.Desktop/Updater/MacOSUpdater.cs index 762bcac51..349e89bac 100644 --- a/OpenTabletDriver.Desktop/Updater/MacOSUpdater.cs +++ b/OpenTabletDriver.Desktop/Updater/MacOSUpdater.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.IO; using System.IO.Compression; @@ -6,40 +5,35 @@ using System.Net.Http; using System.Threading.Tasks; using ICSharpCode.SharpZipLib.Tar; +using Octokit; +using OpenTabletDriver.Desktop.Interop.AppInfo; #pragma warning disable 618 #nullable enable namespace OpenTabletDriver.Desktop.Updater { - public class MacOSUpdater : GithubUpdater + public class MacOSUpdater : GitHubUpdater { - public MacOSUpdater() - : this(null, - AppDomain.CurrentDomain.BaseDirectory, - AppInfo.Current.AppDataDirectory, - AppInfo.Current.BackupDirectory) + public MacOSUpdater(IAppInfo appInfo, IGitHubClient client) + : base(AssemblyVersion, appInfo, client) { } - public MacOSUpdater(Version? currentVersion, string binDirectory, string appDataDirectory, string rollBackDirectory) - : base(currentVersion, - binDirectory, - appDataDirectory, - rollBackDirectory) + protected override async Task Install(Release release) { - } + await Download(release); + PerformBackup(); - protected override void PostInstall() - { - var subPath = Path.Join(BinaryDirectory, "OpenTabletDriver.app", "Contents", "MacOS"); + // Mark the binaries executable, SharpZipLib doesn't do this. + var subPath = Path.Join(DownloadDirectory, "OpenTabletDriver.app", "Contents", "MacOS"); Process.Start("chmod", $"+x {subPath}/OpenTabletDriver.UX.MacOS"); Process.Start("chmod", $"+x {subPath}/OpenTabletDriver.Daemon"); + Move(subPath, BinaryDirectory); } - protected override async Task Download(GithubRelease ghRelease) + protected override async Task Download(Release release) { - var release = ghRelease.Release; var asset = release.Assets.First(r => r.Name.Contains("osx-x64")); // Download and extract tar gzip diff --git a/OpenTabletDriver.Desktop/Updater/Updater.cs b/OpenTabletDriver.Desktop/Updater/Updater.cs index 14959be33..56c07f0bd 100644 --- a/OpenTabletDriver.Desktop/Updater/Updater.cs +++ b/OpenTabletDriver.Desktop/Updater/Updater.cs @@ -4,21 +4,37 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using OpenTabletDriver.Plugin; +using Octokit; #nullable enable namespace OpenTabletDriver.Desktop.Updater { - public abstract class Updater : IUpdater where TInfo : UpdateInfo + public abstract class Updater : IUpdater { + protected Updater(Version? currentVersion, string binaryDir, string appDataDir, string rollbackDir) + { + CurrentVersion = currentVersion ?? AssemblyVersion; + BinaryDirectory = binaryDir; + RollbackDirectory = rollbackDir; + AppDataDirectory = appDataDir; + + if (!Directory.Exists(RollbackDirectory)) + Directory.CreateDirectory(RollbackDirectory); + if (!Directory.Exists(DownloadDirectory)) + Directory.CreateDirectory(DownloadDirectory); + } + /// /// 0 allows update install and check. /// 1 disallows update install and check. - /// 2 means update was installed. + /// 2 means update was installed and check will return false. /// - private int updateSentinel = 0; - private TInfo? updateInfo; + private int _updateSentinel = 0; + private readonly GitHubClient _github = new GitHubClient(new ProductHeaderValue("OpenTabletDriver")); + private Release? _latestRelease; + + protected static readonly Version AssemblyVersion = typeof(IUpdater).Assembly.GetName().Version!; protected Version CurrentVersion { get; } protected string BinaryDirectory { get; } @@ -29,35 +45,20 @@ public abstract class Updater : IUpdater where TInfo : UpdateInfo public string? VersionedRollbackDirectory { private set; get; } - protected Updater(Version? currentVersion, string binaryDir, string appDataDir, string rollbackDir) - { - CurrentVersion = currentVersion ?? typeof(IUpdater).Assembly.GetName().Version!; - BinaryDirectory = binaryDir; - RollbackDirectory = rollbackDir; - AppDataDirectory = appDataDir; - - if (!Directory.Exists(RollbackDirectory)) - Directory.CreateDirectory(RollbackDirectory); - if (!Directory.Exists(DownloadDirectory)) - Directory.CreateDirectory(DownloadDirectory); - } - - protected abstract Task GetUpdate(); - protected abstract Task Download(TInfo release); - public Task CheckForUpdates() => CheckForUpdates(true); - private async Task CheckForUpdates(bool forced) + public async Task CheckForUpdates(bool forced) { - if (updateSentinel == 2) + if (_updateSentinel == 2) return false; try { - if (forced || updateInfo == null) - updateInfo = await GetUpdate(); + if (forced || _latestRelease == null) + _latestRelease = await _github.Repository.Release.GetLatest("OpenTabletDriver", "OpenTabletDriver"); - return updateInfo!.Version > CurrentVersion; + var latestVersion = new Version(_latestRelease!.TagName[1..]); // remove `v` from `vW.X.Y.Z + return latestVersion > CurrentVersion; } catch (Exception e) { @@ -68,7 +69,7 @@ private async Task CheckForUpdates(bool forced) public async Task GetInfo() { - if (updateSentinel == 2) + if (_updateSentinel == 2) return null; return await GetUpdate(); @@ -77,23 +78,23 @@ private async Task CheckForUpdates(bool forced) public async Task InstallUpdate() { // Skip if update is already installed, or in the process of installing - if (Interlocked.CompareExchange(ref updateSentinel, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _updateSentinel, 1, 0) == 0) { - if (await CheckForUpdates(false) && updateInfo != null) + if (await CheckForUpdates(false)) { try { - await Install(updateInfo); - updateSentinel = 2; + await Install(_latestRelease!); + _updateSentinel = 2; return; } catch { - updateSentinel = 0; + _updateSentinel = 0; throw; } } - updateSentinel = 0; + _updateSentinel = 0; } } @@ -108,19 +109,19 @@ protected void PerformBackup() static (source, target) => Copy(source, target)); } - private async Task Install(TInfo updateInfo) + protected virtual async Task Install(Release release) { - await Download(updateInfo); + await Download(release); PerformBackup(); Move(DownloadDirectory, BinaryDirectory); - PostInstall(); } - protected virtual void PostInstall() { } + protected abstract Task GetUpdate(); + protected abstract Task Download(Release release); // Avoid moving/copying the rollback directory if under source directory - private static void ExclusiveFileOp(string source, string backupDir, string target, string versionBackupDir, Action fileOp) + private void ExclusiveFileOp(string source, string backupDir, string target, string versionBackupDir, Action fileOp) { var backupTarget = Path.Join(versionBackupDir, target); diff --git a/OpenTabletDriver.Desktop/Updater/WindowsUpdater.cs b/OpenTabletDriver.Desktop/Updater/WindowsUpdater.cs index 14425d8a9..3ca975041 100644 --- a/OpenTabletDriver.Desktop/Updater/WindowsUpdater.cs +++ b/OpenTabletDriver.Desktop/Updater/WindowsUpdater.cs @@ -1,40 +1,29 @@ -using System; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using Octokit; +using OpenTabletDriver.Desktop.Interop.AppInfo; #nullable enable namespace OpenTabletDriver.Desktop.Updater { - public class WindowsUpdater : GithubUpdater + public class WindowsUpdater : GitHubUpdater { - public WindowsUpdater() - : this(null, - AppDomain.CurrentDomain.BaseDirectory, - AppInfo.Current.AppDataDirectory, - AppInfo.Current.BackupDirectory) + public WindowsUpdater(IAppInfo appInfo, IGitHubClient client) + : base(AssemblyVersion, appInfo, client) { } - public WindowsUpdater(Version? currentVersion, string binDirectory, string appDataDirectory, string rollBackDirectory) - : base(currentVersion, - binDirectory, - appDataDirectory, - rollBackDirectory) - { - } - - protected override string[] IncludeList { get; } = new[] + protected override string[] IncludeList { get; } = { "OpenTabletDriver.UX.Wpf.exe", "OpenTabletDriver.Daemon.exe" }; - protected override async Task Download(GithubRelease ghRelease) + protected override async Task Download(Release release) { - var release = ghRelease.Release; var asset = release.Assets.First(r => r.Name.Contains("win-x64")); using (var client = new HttpClient()) diff --git a/OpenTabletDriver.Desktop/ViewModel.cs b/OpenTabletDriver.Desktop/ViewModel.cs deleted file mode 100644 index 86cfe47bf..000000000 --- a/OpenTabletDriver.Desktop/ViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; - -namespace OpenTabletDriver.Desktop -{ - public class ViewModel : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void RaiseAndSetIfChanged(ref T obj, T newValue, [CallerMemberName] string propertyName = "") - { - obj = newValue; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - protected void RaiseChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public void AllPropertiesChanged() - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null)); - } - } -} diff --git a/OpenTabletDriver.Linux.slnf b/OpenTabletDriver.Linux.slnf index bc6e960a1..725ed9ab0 100644 --- a/OpenTabletDriver.Linux.slnf +++ b/OpenTabletDriver.Linux.slnf @@ -9,7 +9,6 @@ ".\\OpenTabletDriver.Daemon\\OpenTabletDriver.Daemon.csproj", ".\\OpenTabletDriver.Desktop\\OpenTabletDriver.Desktop.csproj", ".\\OpenTabletDriver.Native\\OpenTabletDriver.Native.csproj", - ".\\OpenTabletDriver.Plugin\\OpenTabletDriver.Plugin.csproj", ".\\OpenTabletDriver.Tests\\OpenTabletDriver.Tests.csproj", ".\\OpenTabletDriver.Tools.udev\\OpenTabletDriver.Tools.udev.csproj", ".\\OpenTabletDriver.UX\\OpenTabletDriver.UX.csproj", diff --git a/OpenTabletDriver.MacOS.slnf b/OpenTabletDriver.MacOS.slnf index 67dd3c8f6..bcf0eaf78 100644 --- a/OpenTabletDriver.MacOS.slnf +++ b/OpenTabletDriver.MacOS.slnf @@ -9,7 +9,6 @@ ".\\OpenTabletDriver.Daemon\\OpenTabletDriver.Daemon.csproj", ".\\OpenTabletDriver.Desktop\\OpenTabletDriver.Desktop.csproj", ".\\OpenTabletDriver.Native\\OpenTabletDriver.Native.csproj", - ".\\OpenTabletDriver.Plugin\\OpenTabletDriver.Plugin.csproj", ".\\OpenTabletDriver.Tests\\OpenTabletDriver.Tests.csproj", ".\\OpenTabletDriver.UX\\OpenTabletDriver.UX.csproj", ".\\OpenTabletDriver.UX.MacOS\\OpenTabletDriver.UX.MacOS.csproj" diff --git a/OpenTabletDriver.Native/Linux/Evdev/EvdevDevice.cs b/OpenTabletDriver.Native/Linux/Evdev/EvdevDevice.cs index 89c3bc480..e6887507d 100644 --- a/OpenTabletDriver.Native/Linux/Evdev/EvdevDevice.cs +++ b/OpenTabletDriver.Native/Linux/Evdev/EvdevDevice.cs @@ -1,5 +1,6 @@ using System; -using System.IO; +using System.Collections.Generic; +using System.Linq; namespace OpenTabletDriver.Native.Linux.Evdev { @@ -21,7 +22,7 @@ public ERRNO Initialize() { var err = libevdev_uinput_create_from_device(this.device, LIBEVDEV_UINPUT_OPEN_MANAGED, out this.uidev); CanWrite = err == 0; - return (ERRNO)(-err); + return (ERRNO) (-err); } public void Dispose() @@ -35,26 +36,35 @@ public void Dispose() } } - public void EnableType(EventType type) => libevdev_enable_event_type(this.device, (uint)type); + public void EnableType(EventType type) => libevdev_enable_event_type(this.device, (uint) type); - public void EnableCode(EventType type, EventCode code) => libevdev_enable_event_code(this.device, (uint)type, (uint)code, IntPtr.Zero); - public void EnableCodes(EventType type, params EventCode[] codes) + public void EnableCode(EventType type, EventCode code) => + libevdev_enable_event_code(this.device, (uint) type, (uint) code, IntPtr.Zero); + + public void EnableCodes(EventType type, params EventCode[] codes) => + EnableCodes(type, (IEnumerable) codes); + + public void EnableCodes(EventType type, IEnumerable codes) { foreach (var code in codes) EnableCode(type, code); } - public void EnableCustomCode(EventType type, EventCode code, IntPtr ptr) => libevdev_enable_event_code(this.device, (uint)type, (uint)code, ptr); + public void EnableCustomCode(EventType type, EventCode code, IntPtr ptr) => + libevdev_enable_event_code(this.device, (uint) type, (uint) code, ptr); + + public void EnableTypeCodes(EventType type, params EventCode[] codes) => + EnableTypeCodes(type, (IEnumerable) codes); - public void EnableTypeCodes(EventType type, params EventCode[] codes) + public void EnableTypeCodes(EventType type, IEnumerable codes) { EnableType(type); - EnableCodes(type, codes); + EnableCodes(type, codes.ToArray()); } public int Write(EventType type, EventCode code, int value) { - return CanWrite ? libevdev_uinput_write_event(this.uidev, (uint)type, (uint)code, value) : int.MinValue; + return CanWrite ? libevdev_uinput_write_event(this.uidev, (uint) type, (uint) code, value) : int.MinValue; } public bool Sync() diff --git a/OpenTabletDriver.Native/OSX/Generic/CGPoint.cs b/OpenTabletDriver.Native/OSX/Generic/CGPoint.cs index 00cee4bca..a1cf7aefc 100644 --- a/OpenTabletDriver.Native/OSX/Generic/CGPoint.cs +++ b/OpenTabletDriver.Native/OSX/Generic/CGPoint.cs @@ -1,6 +1,4 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace OpenTabletDriver.Native.OSX { diff --git a/OpenTabletDriver.Native/OpenTabletDriver.Native.csproj b/OpenTabletDriver.Native/OpenTabletDriver.Native.csproj index 5c7f33b98..de782472a 100644 --- a/OpenTabletDriver.Native/OpenTabletDriver.Native.csproj +++ b/OpenTabletDriver.Native/OpenTabletDriver.Native.csproj @@ -5,4 +5,8 @@ true + + + + diff --git a/OpenTabletDriver.Native/Windows/Input/KEYBDINPUT.cs b/OpenTabletDriver.Native/Windows/Input/KEYBDINPUT.cs index a567690c6..c204aa87d 100644 --- a/OpenTabletDriver.Native/Windows/Input/KEYBDINPUT.cs +++ b/OpenTabletDriver.Native/Windows/Input/KEYBDINPUT.cs @@ -3,9 +3,7 @@ namespace OpenTabletDriver.Native.Windows.Input { - using Int16 = Int16; using ScanCodeShort = Int16; - using UInt32 = UInt32; using VirtualKeyShort = Int16; [StructLayout(LayoutKind.Sequential)] diff --git a/OpenTabletDriver.Native/Windows/WinUSB.cs b/OpenTabletDriver.Native/Windows/WinUSB.cs index 1892e5a69..1be806fb0 100644 --- a/OpenTabletDriver.Native/Windows/WinUSB.cs +++ b/OpenTabletDriver.Native/Windows/WinUSB.cs @@ -1,7 +1,6 @@ using System; using System.Runtime.InteropServices; using System.Threading; -using Microsoft.Win32.SafeHandles; using OpenTabletDriver.Native.Windows.USB; namespace OpenTabletDriver.Native.Windows diff --git a/OpenTabletDriver.Native/Windows/Windows.cs b/OpenTabletDriver.Native/Windows/Windows.cs index e668efed1..0f5c03444 100644 --- a/OpenTabletDriver.Native/Windows/Windows.cs +++ b/OpenTabletDriver.Native/Windows/Windows.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; using OpenTabletDriver.Native.Windows.Input; using OpenTabletDriver.Native.Windows.Timers; diff --git a/OpenTabletDriver.Plugin/Area.cs b/OpenTabletDriver.Plugin/Area.cs deleted file mode 100644 index 0bb31fab4..000000000 --- a/OpenTabletDriver.Plugin/Area.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin -{ - public class Area - { - public Area() - { - } - - public Area(float width, float height, Vector2 position, float rotation) - { - Width = width; - Height = height; - Position = position; - Rotation = rotation; - } - - /// - /// The width of the area. - /// - public float Width { set; get; } = 0; - - /// - /// The height of the area. - /// - public float Height { set; get; } = 0; - - /// - /// The center offset of the area. - /// - /// - /// This is also the rotation point of the area. - /// - public Vector2 Position { set; get; } = new Vector2(); - - /// - /// The rotation angle of the area. - /// - public float Rotation { set; get; } = 0; - - public override string ToString() => $"[{Width}x{Height}@{Position}:{Rotation}°],"; - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/BooleanPropertyAttribute.cs b/OpenTabletDriver.Plugin/Attributes/BooleanPropertyAttribute.cs deleted file mode 100644 index 206981b4c..000000000 --- a/OpenTabletDriver.Plugin/Attributes/BooleanPropertyAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - /// - /// Marks a property to be modified as a boolean. - /// - [AttributeUsage(AttributeTargets.Property)] - public class BooleanPropertyAttribute : PropertyAttribute - { - public BooleanPropertyAttribute(string displayName, string description) : base(displayName) - { - Description = description; - } - - public string Description { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/DefaultPropertyValueAttribute.cs b/OpenTabletDriver.Plugin/Attributes/DefaultPropertyValueAttribute.cs deleted file mode 100644 index ce8b089fb..000000000 --- a/OpenTabletDriver.Plugin/Attributes/DefaultPropertyValueAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - /// - /// Applies the default value to a property. - /// - [AttributeUsage(AttributeTargets.Property)] - public class DefaultPropertyValueAttribute : Attribute - { - public DefaultPropertyValueAttribute(object value) - { - this.Value = value; - } - - public object Value { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/DeviceHubAttribute.cs b/OpenTabletDriver.Plugin/Attributes/DeviceHubAttribute.cs deleted file mode 100644 index 6815dcca2..000000000 --- a/OpenTabletDriver.Plugin/Attributes/DeviceHubAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - [AttributeUsage(AttributeTargets.Class)] - public class DeviceHubAttribute : Attribute - { - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/PropertyAttribute.cs b/OpenTabletDriver.Plugin/Attributes/PropertyAttribute.cs deleted file mode 100644 index 59fb0f2eb..000000000 --- a/OpenTabletDriver.Plugin/Attributes/PropertyAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - /// - /// Marks a property to be modified and saved by a client to settings. - /// - [AttributeUsage(AttributeTargets.Property)] - public class PropertyAttribute : Attribute - { - public PropertyAttribute(string displayName) - { - DisplayName = displayName; - } - - public string DisplayName { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/PropertyValidatedAttribute.cs b/OpenTabletDriver.Plugin/Attributes/PropertyValidatedAttribute.cs deleted file mode 100644 index 83d04e2a3..000000000 --- a/OpenTabletDriver.Plugin/Attributes/PropertyValidatedAttribute.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace OpenTabletDriver.Plugin.Attributes -{ - [AttributeUsage(AttributeTargets.Property)] - public class PropertyValidatedAttribute : Attribute - { - public PropertyValidatedAttribute(string memberName) - { - MemberName = memberName; - } - - /// - /// The name of the member in which the property this is assigned to is allowed to have. - /// - /// - /// This member must return statically. - /// - public string MemberName { get; } - - public T GetValue(PropertyInfo property) - { - var sourceType = property.ReflectedType; - var member = sourceType.GetMember(MemberName).First(); - try - { - return member.MemberType switch - { - MemberTypes.Property => (T)sourceType.GetProperty(MemberName).GetValue(null), - MemberTypes.Field => (T)sourceType.GetField(MemberName).GetValue(null), - MemberTypes.Method => (T)sourceType.GetMethod(MemberName).Invoke(null, null), - _ => default - }; - } - catch (Exception e) - { - Log.Write("Plugin", $"Failed to get valid binding values for '{MemberName}'", LogLevel.Error); - - var match = Regex.Match(e.Message, "Non-static (.*) requires a target\\."); - - if (e is TargetException && match.Success) - { - Log.Debug("Plugin", $"Validation {match.Groups[1].Value} must be static"); - } - else - { - Log.Exception(e); - } - } - - return default; - } - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/SliderPropertyAttribute.cs b/OpenTabletDriver.Plugin/Attributes/SliderPropertyAttribute.cs deleted file mode 100644 index 6466b96ab..000000000 --- a/OpenTabletDriver.Plugin/Attributes/SliderPropertyAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - /// - /// Creates a slider for a property value between and . - /// - [AttributeUsage(AttributeTargets.Property)] - public class SliderPropertyAttribute : PropertyAttribute - { - public SliderPropertyAttribute(string displayName, float min, float max, float defaultValue = 0f) : base(displayName) - { - Min = min; - Max = max; - DefaultValue = defaultValue; - } - - public float Min { set; get; } - public float Max { set; get; } - public float DefaultValue { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/SupportedPlatformAttribute.cs b/OpenTabletDriver.Plugin/Attributes/SupportedPlatformAttribute.cs deleted file mode 100644 index 6d8a68e94..000000000 --- a/OpenTabletDriver.Plugin/Attributes/SupportedPlatformAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace OpenTabletDriver.Plugin.Attributes -{ - /// - /// Locks a class, struct, or interface to a specific platform. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] - public class SupportedPlatformAttribute : Attribute - { - public SupportedPlatformAttribute(PluginPlatform platform) - { - this.Platform = platform; - } - - public PluginPlatform Platform { get; } - - public bool IsCurrentPlatform => - (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Platform.HasFlag(PluginPlatform.Windows)) || - (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && Platform.HasFlag(PluginPlatform.Linux)) || - (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Platform.HasFlag(PluginPlatform.MacOS)) || - (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) && Platform.HasFlag(PluginPlatform.FreeBSD)); - } -} diff --git a/OpenTabletDriver.Plugin/Attributes/TabletReferenceAttribute.cs b/OpenTabletDriver.Plugin/Attributes/TabletReferenceAttribute.cs deleted file mode 100644 index b239b34c9..000000000 --- a/OpenTabletDriver.Plugin/Attributes/TabletReferenceAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] - public class TabletReferenceAttribute : Attribute - { - } -} diff --git a/OpenTabletDriver.Plugin/Components/ICompositeDeviceHub.cs b/OpenTabletDriver.Plugin/Components/ICompositeDeviceHub.cs deleted file mode 100644 index b7e3df4e5..000000000 --- a/OpenTabletDriver.Plugin/Components/ICompositeDeviceHub.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using OpenTabletDriver.Plugin.Devices; - -namespace OpenTabletDriver.Plugin.Components -{ - public interface ICompositeDeviceHub : IDeviceHub - { - IEnumerable DeviceHubs { get; } - void ConnectDeviceHub() where T : IDeviceHub; - void ConnectDeviceHub(IDeviceHub instance); - void DisconnectDeviceHub() where T : IDeviceHub; - void DisconnectDeviceHub(IDeviceHub instance); - } -} diff --git a/OpenTabletDriver.Plugin/Components/IDeviceConfigurationProvider.cs b/OpenTabletDriver.Plugin/Components/IDeviceConfigurationProvider.cs deleted file mode 100644 index c5af19c98..000000000 --- a/OpenTabletDriver.Plugin/Components/IDeviceConfigurationProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin.Components -{ - public interface IDeviceConfigurationProvider - { - IEnumerable TabletConfigurations { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Components/IDeviceHubsProvider.cs b/OpenTabletDriver.Plugin/Components/IDeviceHubsProvider.cs deleted file mode 100644 index fc06b0279..000000000 --- a/OpenTabletDriver.Plugin/Components/IDeviceHubsProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using OpenTabletDriver.Plugin.Devices; - -namespace OpenTabletDriver.Plugin.Components -{ - public interface IDeviceHubsProvider - { - IEnumerable DeviceHubs { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Components/IReportParserProvider.cs b/OpenTabletDriver.Plugin/Components/IReportParserProvider.cs deleted file mode 100644 index 11eaed797..000000000 --- a/OpenTabletDriver.Plugin/Components/IReportParserProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin.Components -{ - public interface IReportParserProvider - { - IReportParser GetReportParser(string reportParserName); - } -} diff --git a/OpenTabletDriver.Plugin/DependencyInjection/OnDependencyLoadAttribute.cs b/OpenTabletDriver.Plugin/DependencyInjection/OnDependencyLoadAttribute.cs deleted file mode 100644 index ce2780f2c..000000000 --- a/OpenTabletDriver.Plugin/DependencyInjection/OnDependencyLoadAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.DependencyInjection -{ - /// - /// Marks a method to fire when all dependencies have been injected successfully. - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class OnDependencyLoadAttribute : Attribute - { - } -} diff --git a/OpenTabletDriver.Plugin/DependencyInjection/ResolvedAttribute.cs b/OpenTabletDriver.Plugin/DependencyInjection/ResolvedAttribute.cs deleted file mode 100644 index 66cbcf515..000000000 --- a/OpenTabletDriver.Plugin/DependencyInjection/ResolvedAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.DependencyInjection -{ - /// - /// Marks a property or field to be resolved with dependency injection. - /// It's value will be set as soon as the object is constructed. - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] - public class ResolvedAttribute : Attribute - { - } -} diff --git a/OpenTabletDriver.Plugin/Devices/IDeviceEndpoint.cs b/OpenTabletDriver.Plugin/Devices/IDeviceEndpoint.cs deleted file mode 100644 index 2cf908f16..000000000 --- a/OpenTabletDriver.Plugin/Devices/IDeviceEndpoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.IO; - -namespace OpenTabletDriver.Plugin.Devices -{ - public interface IDeviceEndpoint - { - int ProductID { get; } - int VendorID { get; } - int InputReportLength { get; } - int OutputReportLength { get; } - int FeatureReportLength { get; } - - string Manufacturer { get; } - string ProductName { get; } - string FriendlyName { get; } - string SerialNumber { get; } - - string DevicePath { get; } - bool CanOpen { get; } - - IDeviceEndpointStream Open(); - - string GetDeviceString(byte index); - } -} diff --git a/OpenTabletDriver.Plugin/Devices/IDeviceEndpointStream.cs b/OpenTabletDriver.Plugin/Devices/IDeviceEndpointStream.cs deleted file mode 100644 index 7cd00a4c4..000000000 --- a/OpenTabletDriver.Plugin/Devices/IDeviceEndpointStream.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Devices -{ - public interface IDeviceEndpointStream : IDisposable - { - byte[] Read(); - void Write(byte[] buffer); - - void GetFeature(byte[] buffer); - void SetFeature(byte[] buffer); - } -} diff --git a/OpenTabletDriver.Plugin/Devices/IDeviceHub.cs b/OpenTabletDriver.Plugin/Devices/IDeviceHub.cs deleted file mode 100644 index e7c871c46..000000000 --- a/OpenTabletDriver.Plugin/Devices/IDeviceHub.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace OpenTabletDriver.Plugin.Devices -{ - public interface IDeviceHub - { - event EventHandler DevicesChanged; - - IEnumerable GetDevices(); - } -} diff --git a/OpenTabletDriver.Plugin/Devices/SerializedDeviceEndpoint.cs b/OpenTabletDriver.Plugin/Devices/SerializedDeviceEndpoint.cs deleted file mode 100644 index 3dabe060a..000000000 --- a/OpenTabletDriver.Plugin/Devices/SerializedDeviceEndpoint.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace OpenTabletDriver.Plugin.Devices -{ - public class SerializedDeviceEndpoint - { - public SerializedDeviceEndpoint() - { - } - - public SerializedDeviceEndpoint(IDeviceEndpoint endpoint) - { - DevicePath = endpoint.DevicePath; - Manufacturer = endpoint.Manufacturer; - ProductName = endpoint.ProductName; - FriendlyName = endpoint.FriendlyName; - ProductID = endpoint.ProductID; - VendorID = endpoint.VendorID; - InputReportLength = endpoint.InputReportLength; - OutputReportLength = endpoint.OutputReportLength; - FeatureReportLength = endpoint.FeatureReportLength; - SerialNumber = endpoint.SerialNumber; - CanOpen = endpoint.CanOpen; - } - - public string DevicePath { get; set; } - - public string Manufacturer { get; set; } - - public string ProductName { get; set; } - - public string SerialNumber { get; set; } - - public string FriendlyName { get; set; } - - public int VendorID { get; set; } - - public int ProductID { get; set; } - - public int InputReportLength { get; set; } - - public int OutputReportLength { get; set; } - - public int FeatureReportLength { get; set; } - - public bool CanOpen { get; set; } - } -} diff --git a/OpenTabletDriver.Plugin/HPETDeltaStopwatch.cs b/OpenTabletDriver.Plugin/HPETDeltaStopwatch.cs deleted file mode 100644 index fe0c812bb..000000000 --- a/OpenTabletDriver.Plugin/HPETDeltaStopwatch.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Diagnostics; - -namespace OpenTabletDriver.Plugin.Timing -{ - public class HPETDeltaStopwatch - { - public HPETDeltaStopwatch(bool startRunning = true) - { - isRunning = startRunning; - start = isRunning ? internalWatch.Elapsed : default; - } - - public static TimeSpan RuntimeElapsed => internalWatch.Elapsed; - - public TimeSpan Elapsed => isRunning ? internalWatch.Elapsed - start : end - start; - - public void Start() - { - if (!isRunning) - { - isRunning = true; - start = internalWatch.Elapsed; - } - } - - public TimeSpan Restart() - { - if (isRunning) - { - var current = internalWatch.Elapsed; - var delta = current - start; - start = current; - return delta; - } - else - { - var delta = end - start; - Start(); - return delta; - } - } - - public TimeSpan Stop() - { - if (isRunning) - { - isRunning = false; - end = internalWatch.Elapsed; - } - return end - start; - } - - public TimeSpan Reset() - { - var delta = Stop(); - start = end = default; - return delta; - } - - private static Stopwatch internalWatch = Stopwatch.StartNew(); - protected TimeSpan start; - protected TimeSpan end; - protected bool isRunning; - } -} diff --git a/OpenTabletDriver.Plugin/IBinding.cs b/OpenTabletDriver.Plugin/IBinding.cs deleted file mode 100644 index f7f73e962..000000000 --- a/OpenTabletDriver.Plugin/IBinding.cs +++ /dev/null @@ -1,8 +0,0 @@ -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin -{ - public interface IBinding - { - } -} diff --git a/OpenTabletDriver.Plugin/ITimer.cs b/OpenTabletDriver.Plugin/ITimer.cs deleted file mode 100644 index a70739969..000000000 --- a/OpenTabletDriver.Plugin/ITimer.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Timers -{ - public interface ITimer : IDisposable - { - void Start(); - void Stop(); - bool Enabled { get; } - float Interval { get; set; } - event Action Elapsed; - } -} diff --git a/OpenTabletDriver.Plugin/ITool.cs b/OpenTabletDriver.Plugin/ITool.cs deleted file mode 100644 index d84b27078..000000000 --- a/OpenTabletDriver.Plugin/ITool.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin -{ - /// - /// A plugin that will be started up and kept running until OpenTabletDriver is closed. - /// - public interface ITool : IDisposable - { - bool Initialize(); - } -} diff --git a/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj b/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj deleted file mode 100644 index 38792a11b..000000000 --- a/OpenTabletDriver.Plugin/OpenTabletDriver.Plugin.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net5.0;$(FrameworkBase) - - - - $([System.DateTime]::UtcNow.ToString("d")) - - - - - <_Parameter1>$(BuildDate) - - - - - OpenTabletDriver.Plugin - Library used to create OpenTabletDriver plugins. - ../nupkg - - - diff --git a/OpenTabletDriver.Plugin/Output/AsyncPositionedPipelineElement.cs b/OpenTabletDriver.Plugin/Output/AsyncPositionedPipelineElement.cs deleted file mode 100644 index 12854d265..000000000 --- a/OpenTabletDriver.Plugin/Output/AsyncPositionedPipelineElement.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.DependencyInjection; -using OpenTabletDriver.Plugin.Timers; -using OpenTabletDriver.Plugin.Timing; - -namespace OpenTabletDriver.Plugin.Output -{ - public abstract class AsyncPositionedPipelineElement : IPositionedPipelineElement, IDisposable - { - private readonly object synchronizationObject = new object(); - private HPETDeltaStopwatch consumeWatch = new HPETDeltaStopwatch(); - private ITimer scheduler; - private float? reportMsAvg; - private float frequency; - - /// - /// The current state of the . - /// - protected T State { set; get; } - - public event Action Emit; - - public abstract PipelinePosition Position { get; } - - [Resolved] - public ITimer Scheduler - { - set - { - this.scheduler = value; - - if (this.scheduler != null) - { - this.scheduler.Elapsed += () => - { - lock (synchronizationObject) - { - UpdateState(); - } - }; - this.scheduler.Start(); - } - } - get => this.scheduler; - } - - [Property("Frequency"), Unit("hz"), DefaultPropertyValue(1000.0f)] - public float Frequency - { - set - { - this.frequency = value; - if (Scheduler.Enabled) - Scheduler.Stop(); - Scheduler.Interval = 1000f / value; - Scheduler.Start(); - } - get => this.frequency; - } - - public void Consume(T value) - { - lock (synchronizationObject) - { - State = value; - ConsumeState(); - var consumeDelta = (float)consumeWatch.Restart().TotalMilliseconds; - if (consumeDelta < 150) - reportMsAvg = (reportMsAvg + ((consumeDelta - reportMsAvg) * 0.1f)) ?? consumeDelta; - } - } - - /// - /// Sets the internal state of the within a synchronized context - /// to avoid race conditions against . - /// - /// - /// This is called by whenever a report is received from a linked upstream element. - /// - /// - protected abstract void ConsumeState(); - - /// - /// Updates the state of the within a synchronized context. - /// This is invoked by the on the interval defined by . - /// The implementer must invoke to continue the input pipeline. - /// - /// - /// Call to check if the pen is in range and avoid false emit. - /// - protected abstract void UpdateState(); - - /// - /// Determines if pen is in tablet hover range. - /// - /// - /// We determine that a pen is out of range when is called within - /// after a time equivalent to a report and a half has already passed. If however, the report interval is faster than 3ms, - /// we instead check if 3ms has already passed before declaring that the pen is out of range. - /// - /// True if pen is in range - protected bool PenIsInRange() - { - return (float)consumeWatch.Elapsed.TotalMilliseconds < Math.Max(3, (reportMsAvg * 1.5f) ?? float.MaxValue); - } - - /// - /// Invokes event and transfers data to the next element. - /// - protected void OnEmit() - { - if (State != null) - Emit?.Invoke(State); - } - - public void Dispose() - { - Scheduler?.Dispose(); - Scheduler = null; - } - - ~AsyncPositionedPipelineElement() => Dispose(); - } -} diff --git a/OpenTabletDriver.Plugin/Output/IPointerProvider.cs b/OpenTabletDriver.Plugin/Output/IPointerProvider.cs deleted file mode 100644 index 76cdf89ec..000000000 --- a/OpenTabletDriver.Plugin/Output/IPointerProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Output -{ - public interface IPointerProvider where T : class - { - T Pointer { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Output/IPositionedPipelineElement.cs b/OpenTabletDriver.Plugin/Output/IPositionedPipelineElement.cs deleted file mode 100644 index 8c529790b..000000000 --- a/OpenTabletDriver.Plugin/Output/IPositionedPipelineElement.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace OpenTabletDriver.Plugin.Output -{ - public interface IPositionedPipelineElement : IPipelineElement - { - /// - /// The position in which this will be processed. - /// This helps determine what the expected input units will be. - /// - PipelinePosition Position { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Output/OutputMode.cs b/OpenTabletDriver.Plugin/Output/OutputMode.cs deleted file mode 100644 index 573d6497e..000000000 --- a/OpenTabletDriver.Plugin/Output/OutputMode.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin.Output -{ - public abstract class OutputMode : PipelineManager, IOutputMode - { - public OutputMode() - { - Passthrough = true; - } - - private bool passthrough; - private TabletReference tablet; - private IList> elements; - private IPipelineElement entryElement; - - public event Action Emit; - - protected bool Passthrough - { - private set - { - Action output = this.OnOutput; - if (value && !passthrough) - { - this.entryElement = this; - Link(this, output); - this.passthrough = true; - } - else if (!value && passthrough) - { - this.entryElement = null; - Unlink(this, output); - this.passthrough = false; - } - } - get => this.passthrough; - } - - protected IList> PreTransformElements { private set; get; } = Array.Empty>(); - protected IList> PostTransformElements { private set; get; } = Array.Empty>(); - - public Matrix3x2 TransformationMatrix { protected set; get; } - - public IList> Elements - { - set - { - this.elements = value; - - Passthrough = false; - DestroyInternalLinks(); - - if (Elements != null && Elements.Count > 0) - { - PreTransformElements = GroupElements(Elements, PipelinePosition.PreTransform); - PostTransformElements = GroupElements(Elements, PipelinePosition.PostTransform); - - Action output = this.OnOutput; - - if (PreTransformElements.Any() && !PostTransformElements.Any()) - { - entryElement = PreTransformElements.First(); - - // PreTransform --> Transform --> Output - LinkAll(PreTransformElements, this, output); - } - else if (PostTransformElements.Any() && !PreTransformElements.Any()) - { - entryElement = this; - - // Transform --> PostTransform --> Output - LinkAll(this, PostTransformElements, output); - } - else if (PreTransformElements.Any() && PostTransformElements.Any()) - { - entryElement = PreTransformElements.First(); - - // PreTransform --> Transform --> PostTransform --> Output - LinkAll(PreTransformElements, this, PostTransformElements, output); - } - } - else - { - Passthrough = true; - PreTransformElements = Array.Empty>(); - PostTransformElements = Array.Empty>(); - } - } - get => this.elements; - } - - public virtual TabletReference Tablet - { - set - { - this.tablet = value; - this.TransformationMatrix = CreateTransformationMatrix(); - } - get => this.tablet; - } - - public virtual void Consume(IDeviceReport report) - { - if (report is IAbsolutePositionReport tabletReport) - if (Transform(tabletReport) is IAbsolutePositionReport transformedReport) - report = transformedReport; - - Emit?.Invoke(report); - } - - public virtual void Read(IDeviceReport deviceReport) => entryElement?.Consume(deviceReport); - - protected abstract Matrix3x2 CreateTransformationMatrix(); - protected abstract IAbsolutePositionReport Transform(IAbsolutePositionReport tabletReport); - protected abstract void OnOutput(IDeviceReport report); - - private void DestroyInternalLinks() - { - Action output = this.OnOutput; - - if (PreTransformElements.Any() && !PostTransformElements.Any()) - { - UnlinkAll(PreTransformElements, this, output); - } - else if (PostTransformElements.Any() && !PreTransformElements.Any()) - { - UnlinkAll(this, PostTransformElements, output); - } - else if (PreTransformElements.Any() && PostTransformElements.Any()) - { - UnlinkAll(PreTransformElements, this, PostTransformElements, output); - } - } - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Display/IDisplay.cs b/OpenTabletDriver.Plugin/Platform/Display/IDisplay.cs deleted file mode 100644 index f2ca8688d..000000000 --- a/OpenTabletDriver.Plugin/Platform/Display/IDisplay.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Platform.Display -{ - public interface IDisplay - { - int Index { get; } - float Width { get; } - float Height { get; } - Vector2 Position { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Display/IVirtualScreen.cs b/OpenTabletDriver.Plugin/Platform/Display/IVirtualScreen.cs deleted file mode 100644 index 9dc7abbb4..000000000 --- a/OpenTabletDriver.Plugin/Platform/Display/IVirtualScreen.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace OpenTabletDriver.Plugin.Platform.Display -{ - public interface IVirtualScreen : IDisplay - { - IEnumerable Displays { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Keyboard/IVirtualKeyboard.cs b/OpenTabletDriver.Plugin/Platform/Keyboard/IVirtualKeyboard.cs deleted file mode 100644 index c60203824..000000000 --- a/OpenTabletDriver.Plugin/Platform/Keyboard/IVirtualKeyboard.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace OpenTabletDriver.Plugin.Platform.Keyboard -{ - public interface IVirtualKeyboard - { - void Press(string key); - void Release(string key); - - void Press(IEnumerable keys); - void Release(IEnumerable keys); - - IEnumerable SupportedKeys { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IAbsolutePointer.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IAbsolutePointer.cs deleted file mode 100644 index 36ec2c327..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IAbsolutePointer.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IAbsolutePointer - { - void SetPosition(Vector2 pos); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IEraserHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IEraserHandler.cs deleted file mode 100644 index b41c8cb15..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IEraserHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IEraserHandler - { - void SetEraser(bool isEraser); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IHoverDistanceHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IHoverDistanceHandler.cs deleted file mode 100644 index 39e339e90..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IHoverDistanceHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IHoverDistanceHandler - { - void SetHoverDistance(uint distance); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IMouseButtonHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IMouseButtonHandler.cs deleted file mode 100644 index 714a487fa..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IMouseButtonHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IMouseButtonHandler - { - void MouseDown(MouseButton button); - void MouseUp(MouseButton button); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IPressureHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IPressureHandler.cs deleted file mode 100644 index f067b0d7b..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IPressureHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IPressureHandler - { - void SetPressure(float percentage); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IProximityHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IProximityHandler.cs deleted file mode 100644 index c21b3a480..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IProximityHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IProximityHandler - { - void SetProximity(bool proximity); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/IRelativePointer.cs b/OpenTabletDriver.Plugin/Platform/Pointer/IRelativePointer.cs deleted file mode 100644 index f5245bb75..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/IRelativePointer.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface IRelativePointer - { - void SetPosition(Vector2 delta); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/ISynchronousPointer.cs b/OpenTabletDriver.Plugin/Platform/Pointer/ISynchronousPointer.cs deleted file mode 100644 index d4c35d5de..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/ISynchronousPointer.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface ISynchronousPointer - { - void Reset(); - void Flush(); - } -} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/ITiltHandler.cs b/OpenTabletDriver.Plugin/Platform/Pointer/ITiltHandler.cs deleted file mode 100644 index bd562702e..000000000 --- a/OpenTabletDriver.Plugin/Platform/Pointer/ITiltHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Platform.Pointer -{ - public interface ITiltHandler - { - void SetTilt(Vector2 tilt); - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/ButtonSpecifications.cs b/OpenTabletDriver.Plugin/Tablet/ButtonSpecifications.cs deleted file mode 100644 index f9b2b7f56..000000000 --- a/OpenTabletDriver.Plugin/Tablet/ButtonSpecifications.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public class ButtonSpecifications - { - /// - /// The amount of buttons. - /// - public uint ButtonCount { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/ByteExtensions.cs b/OpenTabletDriver.Plugin/Tablet/ByteExtensions.cs deleted file mode 100644 index 973eb182a..000000000 --- a/OpenTabletDriver.Plugin/Tablet/ByteExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public static class ByteExtensions - { - public static bool IsBitSet(this byte a, int bit) - { - return (a & (1 << bit)) != 0; - } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/DetectionRange.cs b/OpenTabletDriver.Plugin/Tablet/DetectionRange.cs deleted file mode 100644 index 96e7648eb..000000000 --- a/OpenTabletDriver.Plugin/Tablet/DetectionRange.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Linq; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public class DetectionRange - { - public DetectionRange() - { - } - - public DetectionRange(uint? start, uint? end) : this() - { - Start = start; - End = end; - } - - public DetectionRange(uint? start, bool startInclusive, uint? end, bool endInclusive) : this(start, end) - { - StartInclusive = startInclusive; - EndInclusive = endInclusive; - } - - public uint? Start { set; get; } - public bool StartInclusive { set; get; } = false; - - public uint? End { set; get; } - public bool EndInclusive { set; get; } = false; - - public const char LeftInclusiveOperator = '['; - public const char LeftExclusiveOperator = '('; - public const char RightInclusiveOperator = ']'; - public const char RightExclusiveOperator = ')'; - - public bool IsInRange(float value) => - (Start.HasValue ? (StartInclusive ? value >= Start : value > Start) : true) & - (End.HasValue ? (EndInclusive ? value <= End : value < End) : true); - - public override string ToString() - { - return $"{(StartInclusive ? "[" : "(")}{(Start?.ToString() ?? "null")}..{(End?.ToString() ?? "null")}{(EndInclusive ? "]" : ")")}"; - } - - public static DetectionRange Parse(string str) - { - var tokens = str.Split("..", 2); - if (tokens.Length == 2 && tokens.All(t => t.Length > 0)) - { - string left = tokens[0][1..^0]; - char leftOp = tokens[0][0]; - - string right = tokens[1][0..^1]; - char rightOp = tokens[1][^1]; - - uint? start = left == "null" ? null : (uint.TryParse(left, out var startValue) ? (uint?)startValue : null); - uint? end = right == "null" ? null : (uint.TryParse(right, out var endValue) ? (uint?)endValue : null); - - bool startInclusive = (leftOp == LeftInclusiveOperator) && (leftOp != LeftExclusiveOperator); - bool endInclusive = (rightOp == RightInclusiveOperator) && (leftOp != RightExclusiveOperator); - - return new DetectionRange(start, startInclusive, end, endInclusive); - } - else if (tokens.Length == 1) - { - uint? start = str == "null" ? null : (uint.TryParse(str, out var startValue) ? (uint?)startValue : null); - return new DetectionRange(start, false, null, false); - } - return new DetectionRange(); - } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IAbsolutePositionReport.cs b/OpenTabletDriver.Plugin/Tablet/IAbsolutePositionReport.cs deleted file mode 100644 index f5b19b12d..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IAbsolutePositionReport.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IAbsolutePositionReport : IDeviceReport - { - Vector2 Position { get; set; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IAreaConverter.cs b/OpenTabletDriver.Plugin/Tablet/IAreaConverter.cs deleted file mode 100644 index df7db9a55..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IAreaConverter.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IAreaConverter - { - DeviceVendor Vendor { get; } - - string Top { get; } - string Left { get; } - string Bottom { get; } - string Right { get; } - - Area Convert(TabletReference tablet, double top, double left, double bottom, double right); - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IAuxReport.cs b/OpenTabletDriver.Plugin/Tablet/IAuxReport.cs deleted file mode 100644 index 1d1e26a85..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IAuxReport.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IAuxReport : IDeviceReport - { - bool[] AuxButtons { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IDeviceReport.cs b/OpenTabletDriver.Plugin/Tablet/IDeviceReport.cs deleted file mode 100644 index 3535dd056..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IDeviceReport.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IDeviceReport - { - byte[] Raw { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IEraserReport.cs b/OpenTabletDriver.Plugin/Tablet/IEraserReport.cs deleted file mode 100644 index 99131cbcf..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IEraserReport.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IEraserReport : IDeviceReport - { - bool Eraser { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IMouseReport.cs b/OpenTabletDriver.Plugin/Tablet/IMouseReport.cs deleted file mode 100644 index b744ee65d..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IMouseReport.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IMouseReport : IAbsolutePositionReport - { - bool[] MouseButtons { set; get; } - Vector2 Scroll { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IProximityReport.cs b/OpenTabletDriver.Plugin/Tablet/IProximityReport.cs deleted file mode 100644 index df17c2c83..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IProximityReport.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IProximityReport : IDeviceReport - { - bool NearProximity { set; get; } - uint HoverDistance { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IReportParser.cs b/OpenTabletDriver.Plugin/Tablet/IReportParser.cs deleted file mode 100644 index 571cd3fd5..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IReportParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IReportParser where T : IDeviceReport - { - T Parse(byte[] report); - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/ITabletReport.cs b/OpenTabletDriver.Plugin/Tablet/ITabletReport.cs deleted file mode 100644 index 58c9a9d1d..000000000 --- a/OpenTabletDriver.Plugin/Tablet/ITabletReport.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface ITabletReport : IAbsolutePositionReport - { - uint Pressure { set; get; } - bool[] PenButtons { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/ITiltReport.cs b/OpenTabletDriver.Plugin/Tablet/ITiltReport.cs deleted file mode 100644 index 87f19b24a..000000000 --- a/OpenTabletDriver.Plugin/Tablet/ITiltReport.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface ITiltReport : IDeviceReport - { - Vector2 Tilt { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/IToolReport.cs b/OpenTabletDriver.Plugin/Tablet/IToolReport.cs deleted file mode 100644 index e6f647406..000000000 --- a/OpenTabletDriver.Plugin/Tablet/IToolReport.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public interface IToolReport : IDeviceReport - { - ulong Serial { set; get; } - uint RawToolID { set; get; } - ToolType Tool { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/OutOfRangeReport.cs b/OpenTabletDriver.Plugin/Tablet/OutOfRangeReport.cs deleted file mode 100644 index 2a94bed2b..000000000 --- a/OpenTabletDriver.Plugin/Tablet/OutOfRangeReport.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public struct OutOfRangeReport : IDeviceReport - { - public OutOfRangeReport(byte[] report) - { - Raw = report; - } - - public byte[] Raw { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/PenSpecifications.cs b/OpenTabletDriver.Plugin/Tablet/PenSpecifications.cs deleted file mode 100644 index 6c50f1c26..000000000 --- a/OpenTabletDriver.Plugin/Tablet/PenSpecifications.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public class PenSpecifications - { - /// - /// The maximum pressure that the pen supports. - /// - public uint MaxPressure { set; get; } - - /// - /// Specifications for the pen buttons. - /// - public ButtonSpecifications Buttons { set; get; } = new ButtonSpecifications(); - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/TabletReference.cs b/OpenTabletDriver.Plugin/Tablet/TabletReference.cs deleted file mode 100644 index 4fe91d469..000000000 --- a/OpenTabletDriver.Plugin/Tablet/TabletReference.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; - -namespace OpenTabletDriver.Plugin.Tablet -{ - public class TabletReference - { - public TabletReference() - { - } - - public TabletReference( - TabletConfiguration properties, - IEnumerable identifiers - ) - { - this.Properties = properties; - this.Identifiers = identifiers; - } - - public TabletConfiguration Properties { set; get; } - public IEnumerable Identifiers { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/TabletSpecifications.cs b/OpenTabletDriver.Plugin/Tablet/TabletSpecifications.cs deleted file mode 100644 index f65e44cca..000000000 --- a/OpenTabletDriver.Plugin/Tablet/TabletSpecifications.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public class TabletSpecifications - { - /// - /// Specifications for the tablet digitizer. - /// - public DigitizerSpecifications Digitizer { set; get; } - - /// - /// Specifications for the tablet's pen. - /// - public PenSpecifications Pen { set; get; } - - /// - /// Specifications for the auxiliary buttons. - /// - public ButtonSpecifications AuxiliaryButtons { set; get; } - - /// - /// Specifications for the mouse buttons. - /// - public ButtonSpecifications MouseButtons { set; get; } - - /// - /// Specifications for the touch digitizer. - /// - public DigitizerSpecifications Touch { set; get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/TiltTabletReportParser.cs b/OpenTabletDriver.Plugin/Tablet/TiltTabletReportParser.cs deleted file mode 100644 index 380c5057c..000000000 --- a/OpenTabletDriver.Plugin/Tablet/TiltTabletReportParser.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public class TiltTabletReportParser : IReportParser - { - public virtual IDeviceReport Parse(byte[] data) - { - return new TiltTabletReport(data); - } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/ToolType.cs b/OpenTabletDriver.Plugin/Tablet/ToolType.cs deleted file mode 100644 index 00dddbc7a..000000000 --- a/OpenTabletDriver.Plugin/Tablet/ToolType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet -{ - public enum ToolType - { - Pen, - Eraser - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/Touch/ITouchReport.cs b/OpenTabletDriver.Plugin/Tablet/Touch/ITouchReport.cs deleted file mode 100644 index a340ac913..000000000 --- a/OpenTabletDriver.Plugin/Tablet/Touch/ITouchReport.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.Plugin.Tablet.Touch -{ - public interface ITouchReport : IDeviceReport - { - TouchPoint[] Touches { get; } - } -} diff --git a/OpenTabletDriver.Plugin/Tablet/Touch/TouchPoint.cs b/OpenTabletDriver.Plugin/Tablet/Touch/TouchPoint.cs deleted file mode 100644 index 70d29b749..000000000 --- a/OpenTabletDriver.Plugin/Tablet/Touch/TouchPoint.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Numerics; - -namespace OpenTabletDriver.Plugin.Tablet.Touch -{ - public class TouchPoint - { - public byte TouchID; - public Vector2 Position; - - public override string ToString() - { - return $"Point #{TouchID}: {Position};"; - } - } -} diff --git a/OpenTabletDriver.Tests/ConfigurationTest.cs b/OpenTabletDriver.Tests/ConfigurationTest.cs index a9a834c55..bdcfb42cb 100644 --- a/OpenTabletDriver.Tests/ConfigurationTest.cs +++ b/OpenTabletDriver.Tests/ConfigurationTest.cs @@ -3,8 +3,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Extensions.DependencyInjection; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Components; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Tablet; using Xunit; #nullable enable @@ -17,14 +18,14 @@ public static IEnumerable Configurations_Have_ExistentParsers_Data { get { - var configurationProvider = new DriverServiceCollection() + var configurationProvider = new DesktopServiceCollection() .BuildServiceProvider() .GetRequiredService(); var parsers = configurationProvider.TabletConfigurations .SelectMany(c => c.DigitizerIdentifiers) .Concat(configurationProvider.TabletConfigurations - .SelectMany(c => c.AuxilaryDeviceIdentifiers)) + .SelectMany(c => c.AuxiliaryDeviceIdentifiers)) .Select(i => i.ReportParser) .Where(r => r != null) .Distinct(); @@ -38,7 +39,7 @@ public static IEnumerable Configurations_Have_ExistentParsers_Data [MemberData(nameof(Configurations_Have_ExistentParsers_Data))] public void Configurations_Have_ExistentParsers(string reportParserName) { - var parserProvider = new DriverServiceCollection() + var parserProvider = new DesktopServiceCollection() .BuildServiceProvider() .GetRequiredService(); @@ -176,7 +177,7 @@ public static IEnumerable Configurations_DeviceIdentifier_IsNotConflic { get { - var configurationProvider = new DriverServiceCollection() + var configurationProvider = new DesktopServiceCollection() .BuildServiceProvider() .GetRequiredService(); @@ -185,7 +186,7 @@ public static IEnumerable Configurations_DeviceIdentifier_IsNotConflic select new IdentificationContext(config, identifier.DeviceIdentifier, IdentifierType.Digitizer, identifier.Index)).ToArray(); var auxIdentificationContexts = (from config in configurationProvider.TabletConfigurations - from identifier in config.AuxilaryDeviceIdentifiers.Select((d, i) => new { DeviceIdentifier = d, Index = i }) + from identifier in config.AuxiliaryDeviceIdentifiers.Select((d, i) => new { DeviceIdentifier = d, Index = i }) select new IdentificationContext(config, identifier.DeviceIdentifier, IdentifierType.Auxilliary, identifier.Index)).ToArray(); var identificationContexts = digitizerIdentificationContexts.Concat(auxIdentificationContexts); diff --git a/OpenTabletDriver.Tests/DetectionRangeTest.cs b/OpenTabletDriver.Tests/DetectionRangeTest.cs deleted file mode 100644 index 3955eb5b0..000000000 --- a/OpenTabletDriver.Tests/DetectionRangeTest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using OpenTabletDriver.Plugin.Tablet; -using Xunit; - -namespace OpenTabletDriver.Tests -{ - public class DetectionRangeTest - { - [Theory] - [InlineData("[0..16)", 16, false)] - [InlineData("[0..16)", 0, true)] - [InlineData("(0..16]", 16, true)] - [InlineData("(0..16]", 0, false)] - public void RangeTest(string rangeStr, int testValue, bool expectedResult) - { - var range = DetectionRange.Parse(rangeStr); - - var result = range.IsInRange(testValue); - - Assert.Equal(expectedResult, result); - } - } -} diff --git a/OpenTabletDriver.Tests/ReportParserProviderTest.cs b/OpenTabletDriver.Tests/ReportParserProviderTest.cs index e13eab583..70832e616 100644 --- a/OpenTabletDriver.Tests/ReportParserProviderTest.cs +++ b/OpenTabletDriver.Tests/ReportParserProviderTest.cs @@ -1,8 +1,9 @@ using System; using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Components; using OpenTabletDriver.Configurations.Parsers.XP_Pen; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Tablet; using Xunit; namespace OpenTabletDriver.Tests @@ -21,7 +22,7 @@ public class ReportParserProviderTest [MemberData(nameof(ReportParserProvider_CanGet_ReportParsers_Data))] public void ReportParserProvider_CanGet_ReportParsers(string reportParserName, Type expectedReportParserType) { - var serviceCollection = new DriverServiceCollection(); + var serviceCollection = new DesktopServiceCollection(); var reportParserProvider = serviceCollection.BuildServiceProvider() .GetRequiredService(); diff --git a/OpenTabletDriver.Tests/StrictServiceCollectionTest.cs b/OpenTabletDriver.Tests/StrictServiceCollectionTest.cs index 9fde6b965..228acf015 100644 --- a/OpenTabletDriver.Tests/StrictServiceCollectionTest.cs +++ b/OpenTabletDriver.Tests/StrictServiceCollectionTest.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Moq; -using OpenTabletDriver.Plugin.Components; +using OpenTabletDriver.Components; +using OpenTabletDriver.Desktop; using Xunit; namespace OpenTabletDriver.Tests @@ -10,7 +11,7 @@ public class StrictServiceCollectionTest [Fact] public void RequiredServices_AlwaysExist() { - var serviceCollection = new DriverServiceCollection(); + var serviceCollection = new DesktopServiceCollection(); var reportParserProvider = serviceCollection.BuildServiceProvider() .GetService(); @@ -22,7 +23,7 @@ public void RequiredServices_AlwaysExist() public void RequiredServices_CanBeReplaced() { var stubReportParserProvider = new Mock().Object; - var serviceCollection = new DriverServiceCollection().AddSingleton(stubReportParserProvider); + var serviceCollection = new DesktopServiceCollection().AddSingleton(stubReportParserProvider); var retrievedReportParserProvider = serviceCollection.BuildServiceProvider() .GetService(); diff --git a/OpenTabletDriver.Tests/TimerTests.cs b/OpenTabletDriver.Tests/TimerTests.cs index eedb295b3..43a8b6153 100644 --- a/OpenTabletDriver.Tests/TimerTests.cs +++ b/OpenTabletDriver.Tests/TimerTests.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin.Timing; using Xunit; namespace OpenTabletDriver.Tests @@ -13,6 +11,12 @@ namespace OpenTabletDriver.Tests public class TimerTests { private const double TOLERANCE = 0.075; + private readonly ITimer _timer; + + public TimerTests(ITimer timer) + { + _timer = timer; + } [Theory] [InlineData(0.1f, 5f)] @@ -26,19 +30,18 @@ public void TimerAccuracy(float interval, float duration) return; var expectedFires = (int)(interval * 1000 / interval * duration); - var timer = DesktopInterop.Timer; var list = new List(expectedFires); var watch = new HPETDeltaStopwatch(true); - timer.Interval = interval; - timer.Elapsed += () => + _timer.Interval = interval; + _timer.Elapsed += () => { list.Add(watch.Restart().TotalMilliseconds); }; - timer.Start(); + _timer.Start(); Thread.Sleep(TimeSpan.FromSeconds(duration)); - timer.Stop(); + _timer.Stop(); // Windows timers are not guaranteed to stop immediately Thread.Sleep(TimeSpan.FromMilliseconds(50)); diff --git a/OpenTabletDriver.Tools.udev/Comparers/IdentifierComparer.cs b/OpenTabletDriver.Tools.udev/Comparers/IdentifierComparer.cs index db6a198bb..0966e9992 100644 --- a/OpenTabletDriver.Tools.udev/Comparers/IdentifierComparer.cs +++ b/OpenTabletDriver.Tools.udev/Comparers/IdentifierComparer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Tools.udev.Comparers { diff --git a/OpenTabletDriver.Tools.udev/OpenTabletDriver.Tools.udev.csproj b/OpenTabletDriver.Tools.udev/OpenTabletDriver.Tools.udev.csproj index 3807f0842..6fad40535 100755 --- a/OpenTabletDriver.Tools.udev/OpenTabletDriver.Tools.udev.csproj +++ b/OpenTabletDriver.Tools.udev/OpenTabletDriver.Tools.udev.csproj @@ -13,12 +13,11 @@ - - + diff --git a/OpenTabletDriver.Tools.udev/Program.cs b/OpenTabletDriver.Tools.udev/Program.cs index 9937f5efd..3a5f8c0fb 100755 --- a/OpenTabletDriver.Tools.udev/Program.cs +++ b/OpenTabletDriver.Tools.udev/Program.cs @@ -5,7 +5,7 @@ using System.IO; using System.Threading.Tasks; using Newtonsoft.Json; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Tools.udev { diff --git a/OpenTabletDriver.Tools.udev/RuleGenerator.cs b/OpenTabletDriver.Tools.udev/RuleGenerator.cs index 7f1b991b5..72a0ce0bb 100755 --- a/OpenTabletDriver.Tools.udev/RuleGenerator.cs +++ b/OpenTabletDriver.Tools.udev/RuleGenerator.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; using OpenTabletDriver.Tools.udev.Comparers; using udev.NET.Rules; using udev.NET.Rules.Names; @@ -21,7 +21,7 @@ public static Rule CreateAccessRule(string module, string subsystem) private static IEnumerable GetDistinctIdentifiers(TabletConfiguration config) { - var allIdentifiers = config.DigitizerIdentifiers.Concat(config.AuxilaryDeviceIdentifiers); + var allIdentifiers = config.DigitizerIdentifiers.Concat(config.AuxiliaryDeviceIdentifiers); return allIdentifiers.Distinct(new IdentifierComparer()); } diff --git a/OpenTabletDriver.UX.Gtk/LinuxApp.cs b/OpenTabletDriver.UX.Gtk/LinuxApp.cs new file mode 100644 index 000000000..0ca736e6c --- /dev/null +++ b/OpenTabletDriver.UX.Gtk/LinuxApp.cs @@ -0,0 +1,21 @@ +using System.Diagnostics; + +namespace OpenTabletDriver.UX.Gtk +{ + public class LinuxApp : App + { + public LinuxApp(string[] args) : base(Eto.Platforms.Gtk, args) + { + } + + protected override void OpenInternal(string uri, bool isDirectory) + { + Process.Start("xdg-open", uri); + } + + public override void StartDaemon() + { + // Don't start the daemon watchdog on Linux + } + } +} diff --git a/OpenTabletDriver.UX.Gtk/OpenTabletDriver.UX.Gtk.csproj b/OpenTabletDriver.UX.Gtk/OpenTabletDriver.UX.Gtk.csproj index 40dbb4f51..edd104f31 100644 --- a/OpenTabletDriver.UX.Gtk/OpenTabletDriver.UX.Gtk.csproj +++ b/OpenTabletDriver.UX.Gtk/OpenTabletDriver.UX.Gtk.csproj @@ -1,16 +1,18 @@ - + Exe $(FrameworkBase) + enable + enable - - - - + + + + diff --git a/OpenTabletDriver.UX.Gtk/Program.cs b/OpenTabletDriver.UX.Gtk/Program.cs index b09ce39f4..5afea3138 100644 --- a/OpenTabletDriver.UX.Gtk/Program.cs +++ b/OpenTabletDriver.UX.Gtk/Program.cs @@ -1,15 +1,10 @@ -using System; -using Eto.Forms; -using GLib; - -namespace OpenTabletDriver.UX.Gtk +namespace OpenTabletDriver.UX.Gtk { - class Program + public class Program { - [STAThread] public static void Main(string[] args) { - App.Run(Eto.Platforms.Gtk, args); + new LinuxApp(args).Start(); } } } diff --git a/OpenTabletDriver.UX.MacOS/Icon.icns b/OpenTabletDriver.UX.MacOS/Icon.icns deleted file mode 100644 index 832285b73..000000000 Binary files a/OpenTabletDriver.UX.MacOS/Icon.icns and /dev/null differ diff --git a/OpenTabletDriver.UX.MacOS/Info.plist b/OpenTabletDriver.UX.MacOS/Info.plist deleted file mode 100644 index d525594cc..000000000 --- a/OpenTabletDriver.UX.MacOS/Info.plist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - CFBundleName - OpenTabletDriver - CFBundleIdentifier - com.infinityghost.opentabletdriver - CFBundleShortVersionString - 1.0 - LSMinimumSystemVersion - 10.12 - CFBundleDevelopmentRegion - en - NSHumanReadableCopyright - - CFBundleIconFile - Icon.icns - - diff --git a/OpenTabletDriver.UX.MacOS/MacOSApp.cs b/OpenTabletDriver.UX.MacOS/MacOSApp.cs new file mode 100644 index 000000000..ce2a042dd --- /dev/null +++ b/OpenTabletDriver.UX.MacOS/MacOSApp.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; + +namespace OpenTabletDriver.UX.MacOS +{ + public class MacOSApp : App + { + public MacOSApp(string[] args) : base(Eto.Platforms.Mac64, args) + { + } + + protected override void OpenInternal(string uri, bool isDirectory) + { + Process.Start("open", $"\"{uri}\""); + } + + public override void StartDaemon() + { + // TODO: Add daemon watchdog + throw new NotImplementedException(); + } + } +} diff --git a/OpenTabletDriver.UX.MacOS/OpenTabletDriver.UX.MacOS.csproj b/OpenTabletDriver.UX.MacOS/OpenTabletDriver.UX.MacOS.csproj index 1ab37ea11..9c81e591a 100644 --- a/OpenTabletDriver.UX.MacOS/OpenTabletDriver.UX.MacOS.csproj +++ b/OpenTabletDriver.UX.MacOS/OpenTabletDriver.UX.MacOS.csproj @@ -3,15 +3,16 @@ Exe $(FrameworkBase) - osx-x64 + enable + enable - - - - - + + + + + diff --git a/OpenTabletDriver.UX.MacOS/Program.cs b/OpenTabletDriver.UX.MacOS/Program.cs index e9c658ca5..e98399726 100644 --- a/OpenTabletDriver.UX.MacOS/Program.cs +++ b/OpenTabletDriver.UX.MacOS/Program.cs @@ -1,13 +1,10 @@ -using System; - -namespace OpenTabletDriver.UX.MacOS +namespace OpenTabletDriver.UX.MacOS { - class Program + public class Program { - [STAThread] public static void Main(string[] args) { - App.Run(Eto.Platforms.Mac64, args); + new MacOSApp(args).Start(); } } } diff --git a/OpenTabletDriver.UX.Wpf/OpenTabletDriver.UX.Wpf.csproj b/OpenTabletDriver.UX.Wpf/OpenTabletDriver.UX.Wpf.csproj index 83dfb7e19..16e3d17dc 100644 --- a/OpenTabletDriver.UX.Wpf/OpenTabletDriver.UX.Wpf.csproj +++ b/OpenTabletDriver.UX.Wpf/OpenTabletDriver.UX.Wpf.csproj @@ -1,17 +1,18 @@ - + - WinExe + Exe $(FrameworkBase)-windows - ../OpenTabletDriver.UX/Assets/otd.ico + enable + enable - - - - + + + + diff --git a/OpenTabletDriver.UX.Wpf/Program.cs b/OpenTabletDriver.UX.Wpf/Program.cs index 0ffbc2470..1ccdb9bfc 100644 --- a/OpenTabletDriver.UX.Wpf/Program.cs +++ b/OpenTabletDriver.UX.Wpf/Program.cs @@ -1,27 +1,10 @@ -using System; -using System.Security.Principal; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Wpf +namespace OpenTabletDriver.UX.Wpf { - class Program + public class Program { - [STAThread] public static void Main(string[] args) { - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - WindowsPrincipal principal = new(identity); - if (principal.IsInRole(WindowsBuiltInRole.Administrator)) - { - _ = new Application(Eto.Platforms.Wpf); - MessageBox.Show("OpenTabletDriver should not be run with administrator privileges.\n" + - "Some features may not work as intended, such as Plugin Manager and Tablet Debugger.\n" + - "If you did not manually set OpenTabletDriver to run with administrator privileges please enable UAC to resolve this.\n" + - "If it isn't resolved after, you are using the Administrator named account which overrides UAC.", MessageBoxType.Warning); - Eto.Platform.AllowReinitialize = true; - } - - App.Run(Eto.Platforms.Wpf, args); + new WindowsApp(args).Start(); } } } diff --git a/OpenTabletDriver.UX.Wpf/WindowsApp.cs b/OpenTabletDriver.UX.Wpf/WindowsApp.cs new file mode 100644 index 000000000..71c48dbe1 --- /dev/null +++ b/OpenTabletDriver.UX.Wpf/WindowsApp.cs @@ -0,0 +1,33 @@ +using System.Diagnostics; + +namespace OpenTabletDriver.UX.Wpf +{ + public class WindowsApp : App + { + public WindowsApp(string[] args) : base(Eto.Platforms.Wpf, args) + { + } + + protected override void OpenInternal(string uri, bool isDirectory) + { + if (isDirectory) + { + Process.Start("explorer", $"\"{uri.Replace("&", "^&")}\""); + } + else + { + var startInfo = new ProcessStartInfo("cmd", $"/c start {uri.Replace("&", "^&")}") + { + CreateNoWindow = true + }; + Process.Start(startInfo); + } + } + + public override void StartDaemon() + { + // TODO: Add daemon watchdog + throw new NotImplementedException(); + } + } +} diff --git a/OpenTabletDriver.UX/App.cs b/OpenTabletDriver.UX/App.cs index 888e5c61c..007ec784e 100644 --- a/OpenTabletDriver.UX/App.cs +++ b/OpenTabletDriver.UX/App.cs @@ -1,138 +1,324 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.CommandLine; using System.CommandLine.Invocation; -using System.Reflection; -using Eto.Drawing; using Eto.Forms; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.UX.RPC; -using OpenTabletDriver.UX.Windows; -using OpenTabletDriver.UX.Windows.Configurations; -using OpenTabletDriver.UX.Windows.Greeter; -using OpenTabletDriver.UX.Windows.Plugins; -using OpenTabletDriver.UX.Windows.Tablet; -using OpenTabletDriver.UX.Windows.Updater; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Desktop.RPC; +using OpenTabletDriver.Logging; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.UX { - public sealed class App : ViewModel + [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] + public abstract class App : NotifyPropertyChanged { - private App() + protected App(string platform, string[] args) { + Platform = platform; + Arguments = args; } - public static void Run(string platform, string[] args) + public void Start() { - var root = new RootCommand("OpenTabletDriver UX") + var command = new RootCommand("OpenTabletDriver UX") { - new Option(new string[] { "-m", "--minimized" }, "Start the application minimized") - { - Argument = new Argument("minimized") - } + Name = "OpenTabletDriver", + TreatUnmatchedTokensAsErrors = true, + Handler = CommandHandler.Create(Invoke) }; - bool startMinimized = false; - root.Handler = CommandHandler.Create((minimized) => + var code = command.Invoke(Arguments); + if (code != 0) + Exit(code); + } + + // Some fields are suppressed because they're all initialized in Invoke() rather than the ctor() + private IServiceProvider _serviceProvider = null!; + private Settings _settings = null!; + private ObservableCollection _tablets = new ObservableCollection(); + private ObservableCollection _displays = null!; + private string _mainFormTitle = DefaultTitle; + + private static string DefaultTitle { get; } = $"OpenTabletDriver v{Metadata.Version}"; + + /// + /// The this app was built with. + /// + public string Platform { get; } + + /// + /// The command line arguments passed to the app. + /// + public string[] Arguments { get; } + + /// + /// The client plugin manager, used to load clientside plugins. + /// + public IPluginManager PluginManager { set; get; } = null!; + + /// + /// The client plugin factory, used to reflect clientside plugins. + /// + public IPluginFactory PluginFactory { set; get; } = null!; + + /// + /// The local client instance of Settings. + /// This is synchronized when is invoked. + /// + public Settings Settings + { + set => RaiseAndSetIfChanged(ref _settings, value); + get => _settings; + } + + /// + /// Collection of all currently connected tablets. + /// + public ObservableCollection Tablets + { + set { - startMinimized = minimized; - }); + RaiseAndSetIfChanged(ref _tablets, value); + var title = DefaultTitle; - int code = root.Invoke(args); - if (code != 0) - Environment.Exit(code); + if (_tablets.Any()) + title += " - " + string.Join(", ", _tablets.Select(t => t.Name).Take(3)); + + MainFormTitle = title; + } + get => _tablets; + } - var app = new Application(platform); - var mainForm = new MainForm(); - if (startMinimized) + public ObservableCollection Displays + { + set => RaiseAndSetIfChanged(ref _displays, value); + get => _displays; + } + + public string MainFormTitle + { + set => RaiseAndSetIfChanged(ref _mainFormTitle, value); + get => _mainFormTitle; + } + + /// + /// Start the OpenTabletDriver application window and its components. + /// + public void Invoke(bool minimized) + { + var serviceCollection = new UXServiceCollection(this); + _serviceProvider = serviceCollection.BuildServiceProvider(); + + var app = new Application(Platform) { - mainForm.WindowState = WindowState.Minimized; - if (EnableTrayIcon) + Name = "OpenTabletDriver" + }; + + app.UnhandledException += (_, args) => + { + try { - mainForm.Show(); - mainForm.Visible = true; - mainForm.WindowState = WindowState.Minimized; - mainForm.ShowInTaskbar = false; - mainForm.Visible = false; + var ex = args.ExceptionObject as Exception; + ex?.Show(); } + catch (Exception ex2) + { + Log.Exception(ex2); + } + }; + + var mainForm = _serviceProvider.GetRequiredService(); + mainForm.Closed += (_, _) => Exit(); + + if (minimized) + { + mainForm.WindowState = WindowState.Minimized; } - app.NotificationActivated += Current.HandleNotification; - app.UnhandledException += ShowUnhandledException; + // We're hooking up the log output handler later than when the log output handler expects + // This may cause issues in the future + var rpc = _serviceProvider.GetRequiredService>(); + Log.Output += (_, message) => LogOutputHandler(rpc, message).Run(); + + // Force unobserved exceptions to be considered observed, stops hanging on async exceptions. + TaskScheduler.UnobservedTaskException += (_, args) => args.SetObserved(); app.Run(mainForm); } - public static App Current { get; } = new App(); + /// + /// Exit the application, with an optional exit code. + /// + public static void Exit(int code = 0) + { + Environment.Exit(code); + } - public const string WikiUrl = "https://opentabletdriver.net/Wiki"; - public static readonly string Version = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; + /// + /// Synchronize with the daemon. + /// This will wipe any local changes. + /// + public async Task Synchronize() + { + var daemon = _serviceProvider.GetRequiredService(); + daemon.TabletsChanged += (_, t) => Tablets = new ObservableCollection(t); - public IDictionary NotificationHandlers { get; } = new Dictionary(); + Tablets = new ObservableCollection(await daemon.GetTablets()); + Displays = new ObservableCollection(await daemon.GetDisplays()); - public static DaemonRpcClient Driver { get; } = new DaemonRpcClient("OpenTabletDriver.Daemon"); - public static Bitmap Logo { get; } = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("OpenTabletDriver.UX.Assets.otd.png")); + Settings = await daemon.GetSettings(); - private Settings settings; - public Settings Settings + var appInfo = await daemon.GetApplicationInfo(); + PluginManager = _serviceProvider.CreateInstance(appInfo); + PluginFactory = _serviceProvider.CreateInstance(PluginManager); + PluginManager.Load(); + + // TODO: Add better handling for non-matching hashes + if (!await daemon.CheckAssemblyHashes(PluginManager.GetStateHash())) + throw new Exception("Client plugin manager is not synchronized to the daemon."); + } + + /// + /// Desynchronize from the daemon. + /// + public void Desynchronize() { - set => this.RaiseAndSetIfChanged(ref this.settings, value); - get => this.settings; + Tablets.Clear(); + Settings = new Settings(); } - public static AboutDialog AboutDialog => new AboutDialog + /// + /// Loads settings then synchronizes with the daemon. + /// + /// The file path to load settings from. + public async Task LoadSettings(string filePath) { - Title = "OpenTabletDriver", - ProgramName = "OpenTabletDriver", - ProgramDescription = "Open source, cross-platform tablet configurator", - WebsiteLabel = "OpenTabletDriver GitHub Repository", - Website = new Uri(@"https://github.com/OpenTabletDriver/OpenTabletDriver"), - Version = $"v{Version}", - Developers = new string[] { "InfinityGhost" }, - Designers = new string[] { "InfinityGhost" }, - Documenters = new string[] { "InfinityGhost" }, - License = string.Empty, - Copyright = string.Empty, - Logo = Logo.WithSize(256, 256) - }; + if (File.Exists(filePath)) + { + var daemon = GetDriverDaemon(); + var settings = Settings.Deserialize(new FileInfo(filePath))!; + await daemon.ApplySettings(settings); + + // Synchronize from daemon rather than using the deserialized file + Settings = await daemon.GetSettings(); + } + } - public readonly static bool EnableTrayIcon = (PluginPlatform.Windows | PluginPlatform.MacOS).HasFlag(DesktopInterop.CurrentPlatform); - public readonly static bool EnableDaemonWatchdog = (PluginPlatform.Windows | PluginPlatform.MacOS).HasFlag(DesktopInterop.CurrentPlatform); + /// + /// Synchronizes client settings with the daemon and saves. + /// + public async Task SaveSettings() + { + await GetDriverDaemon().SaveSettings(Settings); + } - public WindowSingleton StartupGreeterWindow { get; } = new WindowSingleton(); - public WindowSingleton ConfigEditorWindow { get; } = new WindowSingleton(); - public WindowSingleton PluginManagerWindow { get; } = new WindowSingleton(); - public WindowSingleton DebuggerWindow { get; } = new WindowSingleton(); - public WindowSingleton StringReaderWindow { get; } = new WindowSingleton(); - public WindowSingleton UpdaterWindow { get; } = new WindowSingleton(); + /// + /// Synchronizes client settings with the daemon without saving. + /// + public async Task ApplySettings() + { + await GetDriverDaemon().ApplySettings(Settings); + } - public void AddNotificationHandler(string identifier, Action handler) + /// + /// Discards client settings changes and resynchronizes settings with the daemon. + /// + public async Task DiscardSettings() { - NotificationHandlers.Add(identifier, handler); + Settings = await GetDriverDaemon().GetSettings(); } - private void HandleNotification(object sender, NotificationEventArgs e) + /// + /// Discards client and daemon settings, resetting to defaults. + /// + public async Task ResetSettings() { - if (NotificationHandlers.ContainsKey(e.ID)) - NotificationHandlers[e.ID].Invoke(); + Settings = await GetDriverDaemon().ResetSettings(); } - private static void ShowUnhandledException(object sender, Eto.UnhandledExceptionEventArgs e) + /// + /// Shows an instance of a window. + /// + /// The window type to show. + public void ShowWindow() where TWindow : Form + { + var window = Application.Instance.Windows.FirstOrDefault(w => w is TWindow); + if (window == null) + { + _serviceProvider.GetOrCreateInstance().Show(); + } + else + { + window.BringToFront(); + window.Focus(); + } + } + + /// + /// Shows an instance of a dialog. + /// + /// The parent window. + /// Additional arguments to pass to the constructor. + /// The dialog type to show. + public TDialog ShowDialog(Window parent, params object[] args) where TDialog : Dialog + { + if (Application.Instance.Windows.FirstOrDefault(w => w is TDialog) is not TDialog dialog) + { + dialog = _serviceProvider.GetOrCreateInstance(args); + dialog.ShowModal(parent); + } + else + { + dialog.BringToFront(); + dialog.Focus(); + } + + return dialog; + } + + /// + /// Opens a uri with intended default application. + /// + /// The uri to open + /// Whether this uri is a filesystem directory + public virtual void Open(string? uri, bool isDirectory = false) + { + if (uri != null) + OpenInternal(uri, isDirectory); + } + + /// + /// Starts the OpenTabletDriver daemon, if applicable. + /// + public abstract void StartDaemon(); + + /// + protected abstract void OpenInternal(string uri, bool isDirectory); + + /// + /// The event handler for all client calls. + /// + private static async Task LogOutputHandler(RpcClient rpc, LogMessage message) { try { - var exception = e.ExceptionObject as Exception; - Log.Exception(exception); - exception.ShowMessageBox(); + if (rpc.Instance != null) + await rpc.Instance.WriteMessage(message); } catch (Exception ex) { - // Stops recursion of exceptions if the messagebox itself throws an exception - Log.Exception(ex); + ex.Show(); } } + + private IDriverDaemon GetDriverDaemon() + { + return _serviceProvider.GetRequiredService(); + } } } diff --git a/OpenTabletDriver.UX/Attributes/PageNameAttribute.cs b/OpenTabletDriver.UX/Attributes/PageNameAttribute.cs deleted file mode 100644 index 17e37ded1..000000000 --- a/OpenTabletDriver.UX/Attributes/PageNameAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace OpenTabletDriver.UX.Attributes -{ - [AttributeUsage(AttributeTargets.Class)] - public class PageNameAttribute : Attribute - { - public PageNameAttribute(string name) - { - this.Name = name; - } - - public string Name { get; } - } -} diff --git a/OpenTabletDriver.UX/ChildDialog.cs b/OpenTabletDriver.UX/ChildDialog.cs deleted file mode 100644 index 7022d5c78..000000000 --- a/OpenTabletDriver.UX/ChildDialog.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Eto.Forms; - -namespace OpenTabletDriver.UX -{ - using static App; - - public abstract class ChildDialog : Dialog - { - protected ChildDialog(Window parentWindow) - { - Owner = parentWindow; - Title = "OpenTabletDriver"; - Icon = Logo.WithSize(Logo.Size); - } - } -} diff --git a/OpenTabletDriver.UX/Components/AppCommand.cs b/OpenTabletDriver.UX/Components/AppCommand.cs new file mode 100644 index 000000000..6593adc6d --- /dev/null +++ b/OpenTabletDriver.UX/Components/AppCommand.cs @@ -0,0 +1,47 @@ +using Eto.Forms; + +namespace OpenTabletDriver.UX.Components +{ + public class AppCommand : Command + { + public AppCommand(string menuText) + { + MenuText = menuText; + } + + public AppCommand(string menuText, Keys shortcut) : this(menuText) + { + Shortcut = shortcut; + } + + public AppCommand(string menuText, Action handler) : this(menuText) + { + AddActionHandler(handler); + } + + public AppCommand(string menuText, Func handler) : this(menuText) + { + AddTaskHandler(handler); + } + + public AppCommand(string menuText, Action handler, Keys shortcut) : this(menuText, shortcut) + { + AddActionHandler(handler); + } + + public AppCommand(string menuText, Func handler, Keys shortcut) : this(menuText, shortcut) + { + AddTaskHandler(handler); + } + + private void AddActionHandler(Action handler) + { + Executed += (_, _) => handler(); + } + + private void AddTaskHandler(Func handler) + { + Executed += (_, _) => handler().Run(); + } + } +} diff --git a/OpenTabletDriver.UX/Components/ControlBuilder.cs b/OpenTabletDriver.UX/Components/ControlBuilder.cs new file mode 100644 index 000000000..a7acdc229 --- /dev/null +++ b/OpenTabletDriver.UX/Components/ControlBuilder.cs @@ -0,0 +1,224 @@ +using System.Collections.Immutable; +using System.Numerics; +using System.Reflection; +using Eto.Forms; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.UX.Components +{ + public class ControlBuilder : IControlBuilder + { + private readonly IServiceProvider _serviceProvider; + + public ControlBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public T Build(params object[] additionalDependencies) where T : Control + { + return _serviceProvider.CreateInstance(additionalDependencies); + } + + public IEnumerable Generate(PluginSettings? settings, Type type) + { + return from property in type.GetRuntimeProperties() + let settingAttr = property.GetCustomAttribute() + where settingAttr != null + let control = Generate(settings ?? new PluginSettings(type), property, settingAttr) + where control != null + select control; + } + + private Control? Generate(PluginSettings settings, PropertyInfo property, SettingAttribute settingAttr) + { + var binding = new DelegateBinding( + () => settings[property.Name], + s => settings[property.Name] = s + ); + + var control = ControlForProperty(property, binding); + + if (control is null or CheckBox or LabeledGroup || control.Properties.Get("ControlOnly")) + return control; + + return new LabeledGroup(settingAttr.DisplayName, control); + } + + private static readonly IReadOnlyDictionary, Control>> GenericControls + = new Dictionary, Control>> + { + { typeof(string), GetTextBox }, + { typeof(bool), GetCheckBox }, + { typeof(sbyte), GetNumericEditor }, + { typeof(byte), GetNumericEditor }, + { typeof(short), GetNumericEditor }, + { typeof(ushort), GetNumericEditor }, + { typeof(int), GetNumericEditor }, + { typeof(uint), GetNumericEditor }, + { typeof(long), GetNumericEditor }, + { typeof(ulong), GetNumericEditor }, + { typeof(float), GetNumericEditor }, + { typeof(double), GetNumericEditor }, + { typeof(DateTime), GetMaskedEditor }, + { typeof(TimeSpan), GetTimeSpanEditor }, + { typeof(Vector2), GetVectorEditor } + }; + + private Control? ControlForProperty(PropertyInfo property, DirectBinding binding) + { + if (GetAttributeControl(property, binding) is Control control) + return control; + + return GenericControls.GetValueOrDefault(property.PropertyType)?.Invoke(property, binding); + } + + private Control? GetAttributeControl(PropertyInfo property, DirectBinding binding) + { + if (property.GetCustomAttribute() is MemberValidatedAttribute memberValidated) + { + if (memberValidated.GetValue(_serviceProvider, property) is IEnumerable enumerable) + { + var dropDown = new DropDown + { + DataStore = enumerable.ToImmutableList(), + ID = property.Name + }; + dropDown.SelectedValueBinding.Bind(binding.ValueSetting()); + return dropDown; + } + } + + return null; + } + + private static Control GetTextBox(PropertyInfo property, DirectBinding binding) + { + var control = new TextBox + { + ID = property.Name + }; + + control.TextBinding.Bind(binding.ValueSetting()); + return control; + } + + private static Control GetNumericEditor(PropertyInfo property, DirectBinding binding) + { + return GetMaskedEditor, T>(property, binding); + } + + private static Control GetMaskedEditor(PropertyInfo property, DirectBinding binding) + { + return GetMaskedEditor, T>(property, binding); + } + + private static Control GetMaskedEditor(PropertyInfo property, DirectBinding binding) + where TControl : MaskedTextBox, new() + { + var control = new TControl + { + ID = property.Name + }; + + control.ValueBinding.Bind(binding.ValueSetting()); + + if (property.GetCustomAttribute() is UnitAttribute attr) + { + var name = property.GetCustomAttribute()!.DisplayName; + return new LabeledGroup(name, control, attr.Unit); + } + + return control; + } + + private static Control GetCheckBox(PropertyInfo property, DirectBinding binding) + { + var attr = property.GetCustomAttribute()!; + var control = new CheckBox + { + Text = attr.DisplayName, + ToolTip = attr.Description, + ID = property.Name + }; + + control.CheckedBinding.Convert(c => c ?? false, v => v) + .Bind(binding.ValueSetting()); + + return control; + } + + private static Control GetTimeSpanEditor(PropertyInfo property, DirectBinding binding) + { + var ms = new NumericMaskedTextBox(); + + var timeSpanBinding = binding.ValueSetting(); + + ms.ValueBinding.Bind(timeSpanBinding.Convert( + c => c.Milliseconds, + v => TimeSpan.FromMilliseconds(v) + )); + + var text = property.GetCustomAttribute()!.DisplayName; + return new LabeledGroup(text, ms, "ms"); + } + + private static Control GetVectorEditor(PropertyInfo property, DirectBinding binding) + { + var xComponent = new NumericMaskedTextBox(); + var yComponent = new NumericMaskedTextBox(); + + var vectorBinding = binding.ValueSetting(); + + xComponent.ValueBinding.Convert( + v => + { + var value = binding.DataValue.GetValue(); + value.X = v; + return value; + }, + f => f.X + ).Bind(vectorBinding); + + yComponent.ValueBinding.Convert( + v => + { + var value = binding.DataValue.GetValue(); + value.Y = v; + return value; + }, + f => f.Y + ).Bind(vectorBinding); + + var prefix = property.GetCustomAttribute()!.DisplayName; + + var layout = new StackLayout + { + Orientation = Orientation.Horizontal, + Spacing = 5, + Items = + { + new StackLayoutItem(new LabeledGroup($"{prefix} X", xComponent), true), + new StackLayoutItem(new LabeledGroup($"{prefix} Y", yComponent), true) + }, + Properties = + { + { "ControlOnly", true } + } + }; + + if (property.GetCustomAttribute() is UnitAttribute attr) + { + foreach (var group in layout.Items.Where(c => c.Control is LabeledGroup)) + { + var groupLayout = ((LabeledGroup) group.Control).FindChild(); + groupLayout.Items.Add(attr.Unit); + } + } + + return layout; + } + } +} diff --git a/OpenTabletDriver.UX/Components/DesktopForm.cs b/OpenTabletDriver.UX/Components/DesktopForm.cs new file mode 100644 index 000000000..bf3eb8175 --- /dev/null +++ b/OpenTabletDriver.UX/Components/DesktopForm.cs @@ -0,0 +1,28 @@ +using Eto.Forms; +using JetBrains.Annotations; + +namespace OpenTabletDriver.UX.Components +{ + [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] + public class DesktopForm : Form + { + public BindableBinding DataContextBinding => new BindableBinding( + this, + c => c.DataContext, + (p, o) => p.DataContext = o, + (p, h) => p.DataContextChanged += h, + (p, h) => p.DataContextChanged -= h + ); + + public BindableBinding TitleBinding => new BindableBinding( + this, + c => c.Title, + (c, v) => Application.Instance.AsyncInvoke(() => c.Title = v), + (c, e) => c.TitleChanged += e, + (c, e) => c.TitleChanged -= e + ); + + #pragma warning disable CS0067 + public event EventHandler? TitleChanged; + } +} diff --git a/OpenTabletDriver.UX/Components/DesktopPanel.cs b/OpenTabletDriver.UX/Components/DesktopPanel.cs new file mode 100644 index 000000000..e0ba8cdb7 --- /dev/null +++ b/OpenTabletDriver.UX/Components/DesktopPanel.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.UX.Components +{ + public class DesktopPanel : Panel + { + public DesktopPanel() + { + if (Debugger.IsAttached) + { + var type = GetType(); + var group = type.GetFullyQualifiedName(); + MouseEnter += (_, _) => Console.WriteLine($"[{group}] {DataContext?.ToString() ?? "Null DataContext"}"); + } + } + + public BindableBinding DataContextBinding => new BindableBinding( + this, + c => c.DataContext, + (p, o) => p.DataContext = o, + (p, h) => p.DataContextChanged += h, + (p, h) => p.DataContextChanged -= h + ); + } +} diff --git a/OpenTabletDriver.UX/Components/IControlBuilder.cs b/OpenTabletDriver.UX/Components/IControlBuilder.cs new file mode 100644 index 000000000..f3649efe1 --- /dev/null +++ b/OpenTabletDriver.UX/Components/IControlBuilder.cs @@ -0,0 +1,23 @@ +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.UX.Components +{ + public interface IControlBuilder + { + /// + /// Builds a control of . + /// + /// Any additional dependencies to be provided to the constructor. + /// The control type. + /// A built control of + T Build(params object[] additionalDependencies) where T : Control; + + /// + /// Generates controls for plugin settings based on the provided type. + /// + /// The plugin settings to modify + /// The plugin type + IEnumerable Generate(PluginSettings? settings, Type type); + } +} diff --git a/OpenTabletDriver.UX/Components/LabeledGroup.cs b/OpenTabletDriver.UX/Components/LabeledGroup.cs new file mode 100644 index 000000000..a43ef1193 --- /dev/null +++ b/OpenTabletDriver.UX/Components/LabeledGroup.cs @@ -0,0 +1,34 @@ +using Eto.Drawing; +using Eto.Forms; + +namespace OpenTabletDriver.UX.Components +{ + public class LabeledGroup : DesktopPanel + { + public LabeledGroup(string text, Control? primaryControl, params StackLayoutItem[] additionalControls) + { + var layout = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Center, + Padding = new Padding(5, 0), + Spacing = 10, + Items = + { + text, + new StackLayoutItem(primaryControl, true) + } + }; + + foreach (var suffix in additionalControls) + layout.Items.Add(suffix); + + Content = new GroupBox + { + Padding = 5, + BackgroundColor = Platform.IsMac ? default : SystemColors.WindowBackground, + Content = layout + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Generic/ListBox.cs b/OpenTabletDriver.UX/Components/ListBox.cs similarity index 78% rename from OpenTabletDriver.UX/Controls/Generic/ListBox.cs rename to OpenTabletDriver.UX/Components/ListBox.cs index a64c4bd80..1fc203be3 100644 --- a/OpenTabletDriver.UX/Controls/Generic/ListBox.cs +++ b/OpenTabletDriver.UX/Components/ListBox.cs @@ -1,8 +1,9 @@ -using System.Collections.Generic; using Eto.Forms; +using JetBrains.Annotations; -namespace OpenTabletDriver.UX.Controls.Generic +namespace OpenTabletDriver.UX.Components { + [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public class ListBox : ListBox where T : class { public T SelectedItem diff --git a/OpenTabletDriver.UX/Components/LogDataStore.cs b/OpenTabletDriver.UX/Components/LogDataStore.cs new file mode 100644 index 000000000..15036749a --- /dev/null +++ b/OpenTabletDriver.UX/Components/LogDataStore.cs @@ -0,0 +1,123 @@ +using System.Collections; +using System.Collections.Specialized; +using OpenTabletDriver.Logging; + +namespace OpenTabletDriver.UX.Components +{ + public sealed class LogDataStore : INotifyCollectionChanged, IList + { + public LogDataStore(IEnumerable currentMessages) + { + _messages = new Queue(currentMessages.TakeLast(MAX_NUM_MESSAGES)); + _filteredMessages = new Queue(GetFilteredMessages()); + } + + private const int MAX_NUM_MESSAGES = 250; + + private readonly Queue _messages; + private Queue _filteredMessages; + private LogLevel _filter = LogLevel.Info; + + public LogLevel Filter + { + set + { + _filter = value; + _filteredMessages = new Queue(GetFilteredMessages()); + OnCollectionChanged(); + } + get => _filter; + } + + public int Count => _filteredMessages.Count; + + public bool IsReadOnly => false; + + public event NotifyCollectionChangedEventHandler? CollectionChanged; + + public void Add(LogMessage message) + { + _messages.Enqueue(message); + while (_messages.Count > MAX_NUM_MESSAGES) + _messages.TryDequeue(out _); + + if (message.Level >= Filter) + { + _filteredMessages.Enqueue(message); + while (_filteredMessages.Count > MAX_NUM_MESSAGES) + _filteredMessages.TryDequeue(out _); + + OnCollectionChanged(NotifyCollectionChangedAction.Add, message); + } + } + + private void OnCollectionChanged(NotifyCollectionChangedAction action = NotifyCollectionChangedAction.Reset) + { + var args = new NotifyCollectionChangedEventArgs(action); + CollectionChanged?.Invoke(this, args); + } + + private void OnCollectionChanged(NotifyCollectionChangedAction action, object obj) + { + var args = new NotifyCollectionChangedEventArgs(action, obj); + CollectionChanged?.Invoke(this, args); + } + + private IEnumerable GetFilteredMessages() + { + return _messages.Where(m => m.Level >= Filter); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _filteredMessages.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (_filteredMessages as IEnumerable).GetEnumerator(); + } + + void ICollection.Clear() + { + _messages.Clear(); + _filteredMessages.Clear(); + } + + bool ICollection.Contains(LogMessage item) + { + return _messages.Contains(item); + } + + void ICollection.CopyTo(LogMessage[] array, int arrayIndex) + { + _messages.CopyTo(array, arrayIndex); + } + + int IList.IndexOf(LogMessage item) + { + return _filteredMessages.ToList().IndexOf(item); + } + + void IList.Insert(int index, LogMessage item) + { + throw new NotSupportedException(); + } + + bool ICollection.Remove(LogMessage item) + { + throw new NotSupportedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + LogMessage IList.this[int index] + { + get => (_filteredMessages as IList)![index]; + set => (_filteredMessages as IList)![index] = value; + } + } +} diff --git a/OpenTabletDriver.UX/Components/TrayIcon.cs b/OpenTabletDriver.UX/Components/TrayIcon.cs new file mode 100644 index 000000000..4f3a34e24 --- /dev/null +++ b/OpenTabletDriver.UX/Components/TrayIcon.cs @@ -0,0 +1,14 @@ +using Eto.Forms; + +namespace OpenTabletDriver.UX.Components +{ + public class TrayIcon : TrayIndicator + { + public TrayIcon() + { + // TODO: Implement Tray Icon + Title = "OpenTabletDriver"; + Image = Metadata.Logo; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/AuxPanel.cs b/OpenTabletDriver.UX/Controls/AuxPanel.cs new file mode 100644 index 000000000..205d2e53e --- /dev/null +++ b/OpenTabletDriver.UX/Controls/AuxPanel.cs @@ -0,0 +1,38 @@ +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class AuxPanel : BindingPanel + { + public AuxPanel(IControlBuilder controlBuilder, App app) : base(controlBuilder) + { + var buttons = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5 + }; + + DataContextChanged += delegate + { + buttons.Items.Clear(); + + if (DataContext is not Profile profile) + return; + + var tablet = app.Tablets.First(t => t.Name == profile.Tablet); + var buttonCount = tablet.Specifications.AuxiliaryButtons?.ButtonCount ?? 0; + + foreach (var button in ButtonsFor(c => c.BindingSettings.AuxButtons, buttonCount)) + buttons.Items.Add(button); + }; + + Content = new GroupBox + { + Padding = 5, + Content = buttons + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/BindingDisplay.cs b/OpenTabletDriver.UX/Controls/BindingDisplay.cs deleted file mode 100644 index f01b29487..000000000 --- a/OpenTabletDriver.UX/Controls/BindingDisplay.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Windows.Bindings; - -namespace OpenTabletDriver.UX.Controls -{ - public class BindingDisplay : Panel - { - public BindingDisplay() - { - this.Content = new StackLayout - { - Spacing = 5, - MinimumSize = new Size(300, 0), - Orientation = Orientation.Horizontal, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = mainButton = new Button() - }, - new StackLayoutItem - { - Control = advancedButton = new Button - { - Text = "...", - Width = 25 - } - } - } - }; - - mainButton.TextBinding.Bind(this.StoreBinding.Convert(s => s?.GetHumanReadableString())); - - mainButton.Click += async (sender, e) => - { - var dialog = new BindingEditorDialog(Store); - this.Store = await dialog.ShowModalAsync(this); - }; - - advancedButton.Click += async (sender, e) => - { - var dialog = new AdvancedBindingEditorDialog(Store); - this.Store = await dialog.ShowModalAsync(this); - }; - } - - private Button mainButton, advancedButton; - - public event EventHandler StoreChanged; - - private PluginSettingStore store; - public PluginSettingStore Store - { - set - { - this.store = value; - StoreChanged?.Invoke(this, new EventArgs()); - } - get => this.store; - } - - public BindableBinding StoreBinding - { - get - { - return new BindableBinding( - this, - c => c.Store, - (c, v) => c.Store = v, - (c, h) => c.StoreChanged += h, - (c, h) => c.StoreChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/BindingPanel.cs b/OpenTabletDriver.UX/Controls/BindingPanel.cs new file mode 100644 index 000000000..05255ae5f --- /dev/null +++ b/OpenTabletDriver.UX/Controls/BindingPanel.cs @@ -0,0 +1,118 @@ +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reflection; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls.Editors; +using OpenTabletDriver.UX.Utilities; +using Container = Eto.Forms.Container; + +namespace OpenTabletDriver.UX.Controls +{ + public abstract class BindingPanel : DesktopPanel + { + private readonly IControlBuilder _controlBuilder; + + protected BindingPanel(IControlBuilder controlBuilder) + { + _controlBuilder = controlBuilder; + } + + protected IEnumerable ButtonsFor(Expression> expression, uint count) + { + // this.((TabletConfiguration)DataContext). + var thisExpr = Expression.Constant(this); + var dataContextExpr = Expression.Convert(Expression.Property(thisExpr, nameof(DataContext)), typeof(Profile)); + var baseExpr = new ExpressionMemberAccessor().AccessMember(dataContextExpr, expression); + var getExpr = Expression.Lambda>(baseExpr); + var get = getExpr.Compile(); + + var prefix = GetName(expression); + + for (var i = 0; i < count; i++) + { + var index = i; + var binding = new DelegateBinding( + () => get()[index], + v => get()[index] = v, + h => get().CollectionChanged += h.Invoke, + h => get().CollectionChanged -= h.Invoke + ); + var editor = _controlBuilder.Build(binding); + yield return new LabeledGroup($"{prefix} {index + 1}", editor); + } + } + + /// + /// Creates a button for a plugin settings. + /// + /// Expression pointing to the plugin settings. + /// Friendly name for the button, otherwise pulled via reflection. + protected Container ButtonFor(Expression> expression, string? name = null) + { + var binding = DataContextBinding.Cast().Child(expression); + var editor = _controlBuilder.Build(binding); + + return new LabeledGroup(name ?? GetName(expression), editor); + } + + /// + /// Creates a with a for fine tuning. + /// + /// An expression pointing to a setting. + protected Control SliderFor(Expression> expression) + { + var binding = DataContextBinding.Cast().Child(expression); + + var slider = new Slider + { + MinValue = 0, + MaxValue = 100 + }; + + var valueBinding = new BindableBinding( + slider, + c => c.Value, + (c, v) => c.Value = (int)v, + (s, h) => s.ValueChanged += h, + (s, h) => s.ValueChanged -= h + ); + valueBinding.Bind(binding); + + var textBox = new NumericMaskedTextBox(); + textBox.ValueBinding.Bind(binding); + + var stackLayout = new StackLayout + { + Spacing = 5, + Orientation = Orientation.Horizontal, + Items = + { + new StackLayoutItem(slider, true), + textBox + } + }; + + return new LabeledGroup(GetName(expression), stackLayout); + } + + /// + /// Returns a friendly name for the target member in an expression. + /// + /// The expression pointing to a target member. + /// The target member type. + private static string GetName(Expression> expression) + { + var body = expression.Body switch + { + MemberExpression expr => expr, + UnaryExpression uExpr => (MemberExpression) uExpr.Operand, + _ => throw new NotSupportedException() + }; + var member = body.Member; + return member.GetCustomAttribute()?.DisplayName ?? member.Name; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Bindings/AuxiliaryBindingEditor.cs b/OpenTabletDriver.UX/Controls/Bindings/AuxiliaryBindingEditor.cs deleted file mode 100644 index ac542c3b1..000000000 --- a/OpenTabletDriver.UX/Controls/Bindings/AuxiliaryBindingEditor.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Bindings -{ - public sealed class AuxiliaryBindingEditor : BindingEditor - { - public AuxiliaryBindingEditor() - { - this.Content = new Scrollable - { - Border = BorderType.None, - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - new Group - { - Text = "Auxiliary", - Content = auxButtons = new BindingDisplayList - { - Prefix = "Auxiliary Binding" - } - } - } - } - }; - - auxButtons.ItemSourceBinding.Bind(SettingsBinding.Child(c => (IList)c.AuxButtons)); - } - - private BindingDisplayList auxButtons; - } -} diff --git a/OpenTabletDriver.UX/Controls/Bindings/BindingDisplayList.cs b/OpenTabletDriver.UX/Controls/Bindings/BindingDisplayList.cs deleted file mode 100644 index df26d891a..000000000 --- a/OpenTabletDriver.UX/Controls/Bindings/BindingDisplayList.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Bindings -{ - public class BindingDisplayList : GeneratedItemList - { - public string Prefix { set; get; } - - protected virtual string GetTextForIndex(int index) - { - return $"{Prefix} {index + 1}"; - } - - protected override Control CreateControl(int index, DirectBinding itemBinding) - { - BindingDisplay display = new BindingDisplay(); - display.StoreBinding.Bind(itemBinding); - - return new Group - { - Text = GetTextForIndex(index), - Orientation = Orientation.Horizontal, - ExpandContent = false, - Content = display - }; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Bindings/BindingEditor.cs b/OpenTabletDriver.UX/Controls/Bindings/BindingEditor.cs deleted file mode 100644 index f567fde99..000000000 --- a/OpenTabletDriver.UX/Controls/Bindings/BindingEditor.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; - -namespace OpenTabletDriver.UX.Controls.Bindings -{ - public abstract class BindingEditor : Panel - { - public DirectBinding SettingsBinding => ProfileBinding.Child(b => b.BindingSettings); - - private Profile profile; - public Profile Profile - { - set - { - this.profile = value; - this.OnProfileChanged(); - } - get => this.profile; - } - - public event EventHandler ProfileChanged; - - protected virtual void OnProfileChanged() => ProfileChanged?.Invoke(this, new EventArgs()); - - public BindableBinding ProfileBinding - { - get - { - return new BindableBinding( - this, - c => c.Profile, - (c, v) => c.Profile = v, - (c, h) => c.ProfileChanged += h, - (c, h) => c.ProfileChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Bindings/MouseBindingEditor.cs b/OpenTabletDriver.UX/Controls/Bindings/MouseBindingEditor.cs deleted file mode 100644 index 1d5d7c730..000000000 --- a/OpenTabletDriver.UX/Controls/Bindings/MouseBindingEditor.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections.Generic; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Bindings -{ - public class MouseBindingEditor : BindingEditor - { - public MouseBindingEditor() - { - this.Content = new Scrollable - { - Border = BorderType.None, - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new Group - { - Text = "Mouse Buttons", - Content = mouseButtons = new MouseBindingDisplayList - { - Prefix = "Mouse Binding" - } - }, - new Group - { - Text = "Mouse Scrollwheel", - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - new Group - { - Text = "Scroll Up", - Orientation = Orientation.Horizontal, - ExpandContent = false, - Content = scrollUp = new BindingDisplay() - }, - new Group - { - Text = "Scroll Down", - Orientation = Orientation.Horizontal, - ExpandContent = false, - Content = scrollDown = new BindingDisplay() - } - } - } - } - } - } - }; - - mouseButtons.ItemSourceBinding.Bind(SettingsBinding.Child(c => (IList)c.MouseButtons)); - scrollUp.StoreBinding.Bind(SettingsBinding.Child(c => c.MouseScrollUp)); - scrollDown.StoreBinding.Bind(SettingsBinding.Child(c => c.MouseScrollDown)); - } - - private MouseBindingDisplayList mouseButtons; - private BindingDisplay scrollUp, scrollDown; - - private class MouseBindingDisplayList : BindingDisplayList - { - protected override string GetTextForIndex(int index) - { - return index switch - { - 0 => "Primary Binding", - 1 => "Alternate Binding", - 2 => "Middle Binding", - _ => base.GetTextForIndex(index) - }; - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Bindings/PenBindingEditor.cs b/OpenTabletDriver.UX/Controls/Bindings/PenBindingEditor.cs deleted file mode 100644 index 33ca944ff..000000000 --- a/OpenTabletDriver.UX/Controls/Bindings/PenBindingEditor.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Collections.Generic; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Bindings -{ - public sealed class PenBindingEditor : BindingEditor - { - public PenBindingEditor() - { - this.Content = new Scrollable - { - Border = BorderType.None, - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new TableLayout - { - Rows = - { - new TableRow - { - Cells = - { - new Group - { - Text = "Tip Settings", - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - new Group - { - Text = "Tip Binding", - Orientation = Orientation.Horizontal, - ExpandContent = false, - Content = tipButton = new BindingDisplay() - }, - new Group - { - Text = "Tip Threshold", - ToolTip = "The minimum threshold in order for the assigned binding to activate.", - Orientation = Orientation.Horizontal, - Content = tipThreshold = new FloatSlider() - } - } - } - }, - new Group - { - Text = "Eraser Settings", - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - new Group - { - Text = "Eraser Binding", - ExpandContent = false, - Orientation = Orientation.Horizontal, - Content = eraserButton = new BindingDisplay() - }, - new Group - { - Text = "Eraser Threshold", - ToolTip = "The minimum threshold in order for the assigned binding to activate.", - Orientation = Orientation.Horizontal, - Content = eraserThreshold = new FloatSlider() - } - } - } - } - } - } - } - }, - new Group - { - Text = "Pen Buttons", - Content = penButtons = new BindingDisplayList - { - Prefix = "Pen Binding" - } - } - } - } - }; - - tipButton.StoreBinding.Bind(SettingsBinding.Child(c => c.TipButton)); - eraserButton.StoreBinding.Bind(SettingsBinding.Child(c => c.EraserButton)); - tipThreshold.ValueBinding.Bind(SettingsBinding.Child(c => c.TipActivationThreshold)); - eraserThreshold.ValueBinding.Bind(SettingsBinding.Child(c => c.EraserActivationThreshold)); - penButtons.ItemSourceBinding.Bind(SettingsBinding.Child(c => (IList)c.PenButtons)); - } - - private BindingDisplay tipButton, eraserButton; - private FloatSlider tipThreshold, eraserThreshold; - private BindingDisplayList penButtons; - } -} diff --git a/OpenTabletDriver.UX/Controls/ControlPanel.cs b/OpenTabletDriver.UX/Controls/ControlPanel.cs deleted file mode 100644 index d3f1277ac..000000000 --- a/OpenTabletDriver.UX/Controls/ControlPanel.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Bindings; -using OpenTabletDriver.UX.Controls.Output; - -namespace OpenTabletDriver.UX.Controls -{ - public class ControlPanel : Panel - { - public ControlPanel() - { - this.Content = tabControl = new TabControl - { - Pages = - { - new TabPage - { - Text = "Output", - Content = outputModeEditor = new() - }, - new TabPage - { - Text = "Filters", - Padding = 5, - Content = filterEditor = new() - }, - new TabPage - { - Text = "Pen Settings", - Content = penBindingEditor = new PenBindingEditor() - }, - new TabPage - { - Text = "Auxiliary Settings", - Content = auxBindingEditor = new AuxiliaryBindingEditor() - }, - new TabPage - { - Text = "Mouse Settings", - Content = mouseBindingEditor = new MouseBindingEditor() - }, - new TabPage - { - Text = "Tools", - Padding = 5, - Content = toolEditor = new() - }, - new TabPage - { - Text = "Info", - Padding = 5, - Content = placeholder = new Placeholder - { - Text = "No tablets are detected." - } - }, - new TabPage - { - Text = "Console", - Padding = 5, - Content = logView = new() - } - } - }; - - outputModeEditor.ProfileBinding.Bind(ProfileBinding); - penBindingEditor.ProfileBinding.Bind(ProfileBinding); - auxBindingEditor.ProfileBinding.Bind(ProfileBinding); - mouseBindingEditor.ProfileBinding.Bind(ProfileBinding); - filterEditor.StoreCollectionBinding.Bind(ProfileBinding.Child(p => p.Filters)); - toolEditor.StoreCollectionBinding.Bind(App.Current, a => a.Settings.Tools); - - outputModeEditor.SetDisplaySize(DesktopInterop.VirtualScreen.Displays); - - Log.Output += (_, message) => Application.Instance.AsyncInvoke(() => - { - if (message.Level > LogLevel.Info) - { - tabControl.SelectedPage = logView.Parent as TabPage; - } - }); - } - - private TabControl tabControl; - private Placeholder placeholder; - private LogView logView; - private OutputModeEditor outputModeEditor; - private BindingEditor penBindingEditor, auxBindingEditor, mouseBindingEditor; - private PluginSettingStoreCollectionEditor> filterEditor; - private PluginSettingStoreCollectionEditor toolEditor; - - private Profile profile; - public Profile Profile - { - set - { - this.profile = value; - this.OnProfileChanged(); - } - get => this.profile; - } - - public event EventHandler ProfileChanged; - - protected virtual void OnProfileChanged() => Application.Instance.AsyncInvoke(async () => - { - ProfileChanged?.Invoke(this, EventArgs.Empty); - - var tablet = Profile != null ? await Profile.GetTabletReference() : null; - - if (Platform.IsMac) - tabControl.Pages.Clear(); - - if (tablet != null) - { - bool switchToOutput = tabControl.SelectedPage == placeholder.Parent; - - SetPageVisibility(placeholder, false); - SetPageVisibility(outputModeEditor, true); - SetPageVisibility(filterEditor, true); - SetPageVisibility(penBindingEditor, tablet.Properties.Specifications.Pen != null); - SetPageVisibility(auxBindingEditor, tablet.Properties.Specifications.AuxiliaryButtons != null); - SetPageVisibility(mouseBindingEditor, tablet.Properties.Specifications.MouseButtons != null); - SetPageVisibility(toolEditor, true); - - if (switchToOutput) - tabControl.SelectedIndex = 0; - } - else - { - SetPageVisibility(placeholder, true); - SetPageVisibility(outputModeEditor, false); - SetPageVisibility(filterEditor, false); - SetPageVisibility(penBindingEditor, false); - SetPageVisibility(auxBindingEditor, false); - SetPageVisibility(mouseBindingEditor, false); - SetPageVisibility(toolEditor, false); - - if (tabControl.SelectedPage != logView.Parent) - tabControl.SelectedIndex = 0; - } - - SetPageVisibility(logView, true); - }); - - public BindableBinding ProfileBinding - { - get - { - return new BindableBinding( - this, - c => c.Profile, - (c, v) => c.Profile = v, - (c, h) => c.ProfileChanged += h, - (c, h) => c.ProfileChanged -= h - ); - } - } - - private void SetPageVisibility(Control control, bool visible) - { - // This works around a bug in Eto.Forms with TabPage visibility - // https://github.com/picoe/Eto/issues/1224 - if (Platform.IsMac) - { - if (visible) - { - var page = control.Parent as TabPage; - tabControl.Pages.Add(page); - } - } - else - { - control.Parent.Visible = visible; - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/DigitizerPanel.cs b/OpenTabletDriver.UX/Controls/DigitizerPanel.cs new file mode 100644 index 000000000..4902f8dfa --- /dev/null +++ b/OpenTabletDriver.UX/Controls/DigitizerPanel.cs @@ -0,0 +1,63 @@ +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Output; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls.Editors; + +namespace OpenTabletDriver.UX.Controls +{ + public class DigitizerPanel : DesktopPanel + { + public DigitizerPanel(IControlBuilder controlBuilder, IPluginFactory pluginFactory) + { + var editorPanel = new Panel(); + + var outputModePicker = controlBuilder.Build(); + outputModePicker.SelectedKeyBinding.BindDataContext((Profile p) => p.OutputMode.Path); + outputModePicker.SelectedKeyChanged += delegate + { + if (DataContext is not Profile profile) + return; + + var type = pluginFactory.GetPluginType(profile.OutputMode.Path)!; + if (type.IsAssignableTo(typeof(AbsoluteOutputMode))) + { + if (editorPanel.Content is AbsoluteAreaEditor) + return; + + editorPanel.Content = controlBuilder.Build(); + } + else + { + if (editorPanel.Content.ID == profile.OutputMode.Path) + return; + + var layout = new StackLayout + { + ID = profile.OutputMode.Path, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5, + }; + + foreach (var control in controlBuilder.Generate(profile.OutputMode, type)) + layout.Items.Add(control); + + editorPanel.Content = layout; + } + }; + + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem(editorPanel, true), + new StackLayoutItem(outputModePicker, HorizontalAlignment.Left) + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/AbsoluteAreaEditor.cs b/OpenTabletDriver.UX/Controls/Editors/AbsoluteAreaEditor.cs new file mode 100644 index 000000000..3e97cca28 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/AbsoluteAreaEditor.cs @@ -0,0 +1,245 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Output; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class AbsoluteAreaEditor : DesktopPanel + { + private readonly IControlBuilder _controlBuilder; + private readonly IPluginFactory _pluginFactory; + private readonly StackLayout _advancedSettings; + + // TODO: Fix aspect ratio locking stopping SetDisplay from scaling + public AbsoluteAreaEditor(IControlBuilder controlBuilder, IPluginFactory pluginFactory) + { + _controlBuilder = controlBuilder; + _pluginFactory = pluginFactory; + + _advancedSettings = new StackLayout(); + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem(controlBuilder.Build(), true), + new StackLayoutItem(controlBuilder.Build(), true), + new Expander + { + ID = "Advanced", + Header = new Panel + { + Padding = new Padding(0, 5), + Content = "Advanced" + }, + Content = new Scrollable + { + Padding = 5, + Content = _advancedSettings + } + } + } + }; + } + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + if (DataContext is not Profile profile) + return; + + var settings = profile.OutputMode; + var type = _pluginFactory.GetPluginType(settings.Path); + + _advancedSettings.Items.Clear(); + foreach (var control in _controlBuilder.Generate(settings, type!)) + { + switch (control) + { + // Disallow area editors in advanced settings + case null or AreaEditor: + continue; + case CheckBox { ID: nameof(AbsoluteOutputMode.LockAspectRatio) } lockAspectRatio: + { + var output = FindChild(); + var outputWidth = output.FindChild>("Width"); + var outputHeight = output.FindChild>("Height"); + + var input = FindChild(); + var inputWidth = input.FindChild>("Width"); + var inputHeight = input.FindChild>("Height"); + + _lockingAspectRatio = lockAspectRatio.Checked ?? false; + lockAspectRatio.CheckedChanged += (_, _) => + { + _lockingAspectRatio = lockAspectRatio.Checked ?? false; + if (_lockingAspectRatio) + EnforceAspectRatio(inputWidth, inputHeight, outputWidth, outputHeight); + }; + + HookAspectRatio(inputWidth, inputHeight, outputWidth, outputHeight); + HookAspectRatio(inputHeight, inputWidth, outputHeight, outputWidth); + HookAspectRatio(outputWidth, outputHeight, inputWidth, inputHeight); + HookAspectRatio(outputHeight, outputWidth, inputHeight, inputWidth); + + break; + } + case CheckBox {ID: nameof(AbsoluteOutputMode.LockToBounds)} lockToBounds: + { + var output = FindChild(); + var outputDisplay = output.FindChild(); + var outputWidth = output.FindChild>(nameof(AbsoluteOutputMode.Output.Width)); + var outputHeight = output.FindChild>(nameof(AbsoluteOutputMode.Output.Height)); + var outputX = output.FindChild>(nameof(AbsoluteOutputMode.Output.XPosition)); + var outputY = output.FindChild>(nameof(AbsoluteOutputMode.Output.YPosition)); + + var input = FindChild(); + var inputDisplay = input.FindChild(); + var inputX = input.FindChild>(nameof(AbsoluteOutputMode.Input.XPosition)); + var inputY = input.FindChild>(nameof(AbsoluteOutputMode.Input.YPosition)); + + AngledArea? GetArea() => (inputDisplay?.DataContext as Profile)?.OutputMode?.Get((AbsoluteOutputMode f) => f.Input); + + _lockingToBounds = lockToBounds.Checked ?? false; + lockToBounds.CheckedChanged += (_, _) => + { + _lockingToBounds = lockToBounds.Checked ?? false; + if (_lockingToBounds) + { + EnforcePositionToBounds(outputX, outputWidth.Value, outputDisplay.FullBackground.Width); + EnforcePositionToBounds(outputY, outputHeight.Value, outputDisplay.FullBackground.Height); + EnforceAngledAreaToBounds(inputX, inputX, inputY, inputDisplay, GetArea); + EnforceAngledAreaToBounds(inputY, inputX, inputY, inputDisplay, GetArea); + } + }; + + HookPositionToBounds(outputX, outputWidth, () => outputDisplay.FullBackground.Width); + HookPositionToBounds(outputY, outputHeight, () => outputDisplay.FullBackground.Height); + LockAngledAreaToBounds(inputX, inputX, inputY, inputDisplay, GetArea); + LockAngledAreaToBounds(inputY, inputX, inputY, inputDisplay, GetArea); + + break; + } + } + + _advancedSettings.Items.Add(control); + } + } + + private bool _lockingAspectRatio; + private bool _lockingToBounds; + private bool _valueChanging; + + private void HookAspectRatio( + MaskedTextBox source, + MaskedTextBox sourceOpposite, + MaskedTextBox alt, + MaskedTextBox altOpposite + ) + { + source.ValueChanged += delegate + { + if (!_lockingAspectRatio || _valueChanging) + return; + + _valueChanging = true; + EnforceAspectRatio(source, sourceOpposite, alt, altOpposite); + _valueChanging = false; + }; + } + + private static void EnforceAspectRatio( + MaskedTextBox source, + MaskedTextBox sourceOpposite, + MaskedTextBox alt, + MaskedTextBox altOpposite + ) + { + sourceOpposite.Value = altOpposite.Value / alt.Value * source.Value; + } + + private void HookPositionToBounds( + MaskedTextBox position, + MaskedTextBox axisSize, + Func getAxisBounds + ) + { + position.ValueChanged += delegate + { + if (!_lockingToBounds || _valueChanging) + return; + + _valueChanging = true; + EnforcePositionToBounds(position, axisSize.Value, getAxisBounds()); + _valueChanging = false; + }; + } + + private void LockAngledAreaToBounds( + MaskedTextBox source, + MaskedTextBox inputX, + MaskedTextBox inputY, + AreaDisplay display, + Func getArea + ) + { + source.ValueChanged += delegate + { + if (!_lockingToBounds || _valueChanging) + return; + + _valueChanging = true; + EnforceAngledAreaToBounds(source, inputX, inputY, display, getArea); + _valueChanging = false; + }; + } + + private void EnforceAngledAreaToBounds( + MaskedTextBox source, + MaskedTextBox inputX, + MaskedTextBox inputY, + AreaDisplay display, + Func getArea + ) + { + if (getArea() is AngledArea area) + { + var corners = area.GetCorners(); + var areaSize = RectangleF.FromSides( + corners.Min(t => t.X), + corners.Min(t => t.Y), + corners.Max(t => t.X), + corners.Max(t => t.Y) + ); + if (source == inputX) + EnforcePositionToBounds(inputX, areaSize.Width, display.FullBackground.Width); + if (source == inputY) + EnforcePositionToBounds(inputY, areaSize.Height, display.FullBackground.Height); + } + } + + private void EnforcePositionToBounds(MaskedTextBox position, float axisSize, float boundsSize) + { + // This stops the position value from flickering due to rounding errors + if (axisSize > boundsSize || Math.Abs(axisSize - boundsSize) < 0.0005f) + { + position.Value = boundsSize / 2; + return; + } + + var offset = axisSize / 2; + var normalized = position.Value - offset; + if (normalized < 0) + position.Value = offset; + else if (normalized > boundsSize - axisSize) + position.Value = boundsSize - offset; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/AreaDisplay.cs b/OpenTabletDriver.UX/Controls/Editors/AreaDisplay.cs new file mode 100644 index 000000000..91624f6b0 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/AreaDisplay.cs @@ -0,0 +1,141 @@ +using System.Globalization; +using System.Linq.Expressions; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Output; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public abstract class AreaDisplay : Drawable + { + private Func? _getArea; + + public float Scale { private set; get; } = 0; + public PointF ControlOffset { private set; get; } = PointF.Empty; + + protected abstract string Unit { get; } + protected abstract IEnumerable Backgrounds { get; } + protected abstract Expression> Foreground { get; } + + public RectangleF FullBackground + { + get + { + var left = Backgrounds.Min(r => r.Left); + var top = Backgrounds.Min(r => r.Top); + var right = Backgrounds.Max(r => r.Right); + var bottom = Backgrounds.Max(r => r.Bottom); + return RectangleF.FromSides(left, top, right, bottom); + } + } + + private static Font Font { get; } = SystemFonts.User(9); + private static Color ForegroundFillColor { get; } = new Color(SystemColors.Highlight, 0.75f); + private static Color ForegroundBorderColor { get; } = SystemColors.ControlText; + private static Color BackgroundFillColor { get; } = SystemColors.WindowBackground; + private static Color BackgroundBorderColor { get; } = SystemColors.Control; + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + if (DataContext is Profile profile) + { + _getArea = profile.OutputMode.GetterFor(Foreground); + } + else + { + _getArea = null; + } + + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + var graphics = e.Graphics; + + if (_getArea == null) + { + DrawText(graphics, "Invalid area!"); + } + else + { + DrawArea(graphics, _getArea()); + } + } + + private void DrawText(Graphics graphics, string text) + { + var formattedText = new FormattedText + { + Font = Font, + Text = text + }; + + using (graphics.SaveTransformState()) + { + graphics.TranslateTransform(Width / 2f, Height / 2f); + graphics.DrawText(formattedText, PointF.Empty); + } + } + + private void DrawArea(Graphics graphics, Area area) + { + var scaleX = (Width - 2) / FullBackground.Width; + var scaleY = (Height - 2) / FullBackground.Height; + Scale = scaleX > scaleY ? scaleY : scaleX; + + var clientCenter = new PointF(Width, Height); + var backgroundCenter = new PointF(FullBackground.Width, FullBackground.Height) * Scale; + ControlOffset = (clientCenter - backgroundCenter) / 2; + + graphics.TranslateTransform(ControlOffset); + + // Draw background area + var backgrounds = Backgrounds.Select(r => r * Scale); + foreach (var rect in backgrounds) + { + graphics.FillRectangle(BackgroundFillColor, rect); + graphics.DrawRectangle(BackgroundBorderColor, rect); + } + + // Draw foreground area + using (graphics.SaveTransformState()) + { + var offset = new PointF(area.XPosition, area.YPosition) * Scale; + graphics.TranslateTransform(offset); + + if (area is AngledArea angledArea) + graphics.RotateTransform(angledArea.Rotation); + + var size = new SizeF(area.Width, area.Height) * Scale; + var foreground = RectangleF.FromCenter(PointF.Empty, size); + graphics.FillRectangle(ForegroundFillColor, foreground); + graphics.DrawRectangle(ForegroundBorderColor, foreground); + + var centerPoint = RectangleF.FromCenter(PointF.Empty, new SizeF(3, 3)); + graphics.DrawEllipse(SystemColors.ControlText, centerPoint); + + var widthText = new FormattedText + { + Text = area.Width.ToString(CultureInfo.CurrentUICulture) + Unit, + Font = Font + }; + var widthSize = widthText.Measure(); + graphics.DrawText(widthText, new PointF(-widthSize.Width / 2, size.Height / 2 - widthSize.Height - 5)); + + var heightText = new FormattedText + { + Text = area.Height.ToString(CultureInfo.CurrentUICulture) + Unit, + Font = Font + }; + var heightSize = heightText.Measure(); + graphics.RotateTransform(270); + graphics.DrawText(heightText, new PointF(-heightSize.Width / 2, size.Width / 2 - heightSize.Height - 5)); + } + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/AreaEditor.cs b/OpenTabletDriver.UX/Controls/Editors/AreaEditor.cs new file mode 100644 index 000000000..accc0afb4 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/AreaEditor.cs @@ -0,0 +1,56 @@ +using System.Linq.Expressions; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Output; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public abstract class AreaEditor : DesktopPanel + { + protected Control CreateUnitBox(string text, Control control, string unit) + { + return new GroupBox + { + Padding = 5, + BackgroundColor = Platform.IsMac ? default : SystemColors.WindowBackground, + Content = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Center, + Padding = new Padding(5, 0), + Spacing = 10, + Items = + { + text, + new StackLayoutItem(control, true), + unit + } + } + }; + } + + protected NumericMaskedTextBox TextBoxFor(Expression> expression) + { + var memberExpression = (MemberExpression) expression.Body; + var member = memberExpression.Member; + + var control = new NumericMaskedTextBox + { + ID = member.Name + }; + + var binding = new DelegateBinding( + () => (DataContext as Profile)?.OutputMode.GetChild(expression) ?? 0, + v => (DataContext as Profile)?.OutputMode.SetChild(expression, v), + h => DataContextChanged += h, + h => DataContextChanged -= h + ); + + control.ValueBinding.Bind(binding); + + return control; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/BindingEditor.cs b/OpenTabletDriver.UX/Controls/Editors/BindingEditor.cs new file mode 100644 index 000000000..b5878f9b6 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/BindingEditor.cs @@ -0,0 +1,52 @@ +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Dialogs; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class BindingEditor : DesktopPanel + { + private readonly App _app; + private readonly DirectBinding _binding; + + public BindingEditor(App app, IPluginFactory pluginFactory, DirectBinding binding) + { + _app = app; + _binding = binding; + + var button = new Button(ShowBindingDialog); + button.TextBinding.Bind(binding.Convert(s => s == null ? string.Empty : s.Format(pluginFactory))); + + var advancedButton = new Button(ShowAdvancedBindingDialog) + { + Text = "..." + }; + + Content = new StackLayout + { + Orientation = Orientation.Horizontal, + Spacing = 5, + Items = + { + new StackLayoutItem(button, true), + advancedButton + } + }; + } + + private void ShowBindingDialog(object? sender, EventArgs e) + { + var dialog = _app.ShowDialog(ParentWindow, _binding); + if (dialog.Result?.Result == DialogResult.Ok) + _binding.DataValue = dialog.Result.Model; + } + + private void ShowAdvancedBindingDialog(object? sender, EventArgs e) + { + var dialog = _app.ShowDialog(ParentWindow, _binding); + if (dialog.Result?.Result == DialogResult.Ok) + _binding.DataValue = dialog.Result.Model; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/InputAreaDisplay.cs b/OpenTabletDriver.UX/Controls/Editors/InputAreaDisplay.cs new file mode 100644 index 000000000..dfa8a90b3 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/InputAreaDisplay.cs @@ -0,0 +1,39 @@ +using System.Linq.Expressions; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Output; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class InputAreaDisplay : AreaDisplay + { + private readonly App _app; + private IList? _backgrounds; + + public InputAreaDisplay(App app) + { + _app = app; + + DataContextChanged += delegate + { + _backgrounds = GetBackgrounds(); + }; + } + + protected override string Unit => "mm"; + protected override Expression> Foreground { get; } = mode => mode.Input!; + protected override IEnumerable Backgrounds => _backgrounds ??= GetBackgrounds(); + + private IList GetBackgrounds() + { + var profile = Parents.Select(w => w as IBindable).First(b => b?.DataContext is Profile)!.DataContext as Profile; + var tablet = _app.Tablets.First(t => t.Name == profile!.Tablet); + var digitizer = tablet.Specifications.Digitizer!; + return new [] + { + new RectangleF(0, 0, digitizer.Width, digitizer.Height) + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/InputAreaEditor.cs b/OpenTabletDriver.UX/Controls/Editors/InputAreaEditor.cs new file mode 100644 index 000000000..991964684 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/InputAreaEditor.cs @@ -0,0 +1,172 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class InputAreaEditor : AreaEditor + { + public InputAreaEditor(IControlBuilder controlBuilder) + { + var width = TextBoxFor(m => m.Input!.Width); + var height = TextBoxFor(m => m.Input!.Height); + var xPosition = TextBoxFor(m => m.Input!.XPosition); + var yPosition = TextBoxFor(m => m.Input!.YPosition); + var rotation = TextBoxFor(m => m.Input!.Rotation); + + var areaDisplay = controlBuilder.Build(); + width.ValueChanged += (_, _) => areaDisplay.Invalidate(); + height.ValueChanged += (_, _) => areaDisplay.Invalidate(); + xPosition.ValueChanged += (_, _) => areaDisplay.Invalidate(); + yPosition.ValueChanged += (_, _) => areaDisplay.Invalidate(); + rotation.ValueChanged += (_, _) => areaDisplay.Invalidate(); + + PointF? prevPos = null; + void AreaMouseHandler(object? _, MouseEventArgs e) + { + if ((e.Buttons & MouseButtons.Primary) != 0) + { + if (prevPos == null) + { + var x = e.Location.X - xPosition.Value * areaDisplay.Scale; + var y = e.Location.Y - yPosition.Value * areaDisplay.Scale; + prevPos = new PointF(x, y); + return; + } + + var newPos = (e.Location - prevPos.Value) / areaDisplay.Scale; + xPosition.Value = newPos.X; + yPosition.Value = newPos.Y; + return; + } + + prevPos = null; + + if ((e.Buttons & MouseButtons.Middle) != 0) + { + var newPosition = (e.Location - areaDisplay.ControlOffset) / areaDisplay.Scale; + xPosition.Value = newPosition.X; + yPosition.Value = newPosition.Y; + } + } + + areaDisplay.MouseMove += AreaMouseHandler; + areaDisplay.MouseDown += AreaMouseHandler; + + // TODO: Fix rotation handling, doesn't move to actual edges + var alignMenu = new ButtonMenuItem + { + Text = "Align", + Items = + { + new AppCommand("Left", () => xPosition.Value = width.Value / 2), + new AppCommand("Right", () => xPosition.Value = areaDisplay.FullBackground.Width - width.Value / 2), + new AppCommand("Top", () => yPosition.Value = height.Value / 2), + new AppCommand("Bottom", () => yPosition.Value = areaDisplay.FullBackground.Height - height.Value / 2), + new AppCommand("Center", () => + { + xPosition.Value = areaDisplay.FullBackground.Width / 2; + yPosition.Value = areaDisplay.FullBackground.Height / 2; + }) + } + }; + + var scaleMenu = new ButtonMenuItem + { + Text = "Scale", + Items = + { + new AppCommand("Full", () => + { + width.Value = areaDisplay.FullBackground.Width; + height.Value = areaDisplay.FullBackground.Height; + xPosition.Value = width.Value / 2; + yPosition.Value = height.Value / 2; + }), + new AppCommand("Half", () => + { + width.Value = areaDisplay.FullBackground.Width / 2; + height.Value = areaDisplay.FullBackground.Height / 2; + xPosition.Value = areaDisplay.FullBackground.Width / 2; + yPosition.Value = areaDisplay.FullBackground.Height / 2; + }) + } + }; + + var flipMenu = new ButtonMenuItem + { + Text = "Flip", + Items = + { + new AppCommand("Horizontal", () => xPosition.Value = areaDisplay.FullBackground.Width - xPosition.Value), + new AppCommand("Vertical", () => yPosition.Value = areaDisplay.FullBackground.Height - yPosition.Value), + new AppCommand("Handedness", () => + { + rotation.Value += 180; + rotation.Value %= 360; + xPosition.Value = areaDisplay.FullBackground.Width - xPosition.Value; + yPosition.Value = areaDisplay.FullBackground.Height - yPosition.Value; + }) + } + }; + + var contextMenu = new ContextMenu + { + Items = + { + alignMenu, + scaleMenu, + flipMenu + } + }; + + areaDisplay.MouseDown += (_, e) => + { + if ((e.Buttons & MouseButtons.Alternate) != 0) + contextMenu.Show(); + }; + + var editorControls = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Center, + Spacing = 5, + Items = + { + new StackLayoutItem(null, true), + CreateUnitBox("Width", width, "mm"), + CreateUnitBox("Height", height, "mm"), + CreateUnitBox("X", xPosition, "mm"), + CreateUnitBox("Y", yPosition, "mm"), + CreateUnitBox("Rotation", rotation, "°"), + new StackLayoutItem(null, true) + } + }; + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem + { + Control = new GroupBox + { + Text = "Input", + Padding = 5, + Content = areaDisplay + }, + Expand = true + }, + new Scrollable + { + Border = BorderType.None, + Content = editorControls + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/OutputAreaDisplay.cs b/OpenTabletDriver.UX/Controls/Editors/OutputAreaDisplay.cs new file mode 100644 index 000000000..975a5c7f5 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/OutputAreaDisplay.cs @@ -0,0 +1,23 @@ +using System.Linq.Expressions; +using Eto.Drawing; +using OpenTabletDriver.Output; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class OutputAreaDisplay : AreaDisplay + { + private readonly App _app; + private IEnumerable? _backgrounds; + + public OutputAreaDisplay(App app) + { + _app = app; + } + + protected override string Unit => "px"; + protected override Expression> Foreground { get; } = m => m.Output!; + + protected override IEnumerable Backgrounds => _backgrounds ??= + _app.Displays.Select(d => new RectangleF(d.Position.X, d.Position.Y, d.Width, d.Height)).ToArray(); + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/OutputAreaEditor.cs b/OpenTabletDriver.UX/Controls/Editors/OutputAreaEditor.cs new file mode 100644 index 000000000..fdd8e683c --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/OutputAreaEditor.cs @@ -0,0 +1,180 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class OutputAreaEditor : AreaEditor + { + public OutputAreaEditor(IControlBuilder controlBuilder, App app) + { + var width = TextBoxFor(m => m.Output!.Width); + var height = TextBoxFor(m => m.Output!.Height); + var xPosition = TextBoxFor(m => m.Output!.XPosition); + var yPosition = TextBoxFor(m => m.Output!.YPosition); + + var areaDisplay = controlBuilder.Build(); + width.ValueChanged += (_, _) => areaDisplay.Invalidate(); + height.ValueChanged += (_, _) => areaDisplay.Invalidate(); + xPosition.ValueChanged += (_, _) => areaDisplay.Invalidate(); + yPosition.ValueChanged += (_, _) => areaDisplay.Invalidate(); + + PointF? prevPos = null; + void AreaMouseHandler(object? _, MouseEventArgs e) + { + if ((e.Buttons & MouseButtons.Primary) != 0) + { + if (prevPos == null) + { + var x = e.Location.X - xPosition.Value * areaDisplay.Scale; + var y = e.Location.Y - yPosition.Value * areaDisplay.Scale; + prevPos = new PointF(x, y); + return; + } + + var newPos = (e.Location - prevPos.Value) / areaDisplay.Scale; + xPosition.Value = newPos.X; + yPosition.Value = newPos.Y; + return; + } + + prevPos = null; + + if ((e.Buttons & MouseButtons.Middle) != 0) + { + var newPosition = (e.Location - areaDisplay.ControlOffset) / areaDisplay.Scale; + xPosition.Value = newPosition.X; + yPosition.Value = newPosition.Y; + } + } + + areaDisplay.MouseMove += AreaMouseHandler; + areaDisplay.MouseDown += AreaMouseHandler; + + var alignMenu = new ButtonMenuItem + { + Text = "Align...", + Items = + { + new AppCommand("Left", () => xPosition.Value = width.Value / 2), + new AppCommand("Right", () => xPosition.Value = areaDisplay.FullBackground.Width - width.Value / 2), + new AppCommand("Top", () => yPosition.Value = height.Value / 2), + new AppCommand("Bottom", () => yPosition.Value = areaDisplay.FullBackground.Height - height.Value / 2), + new AppCommand("Center", () => + { + xPosition.Value = areaDisplay.FullBackground.Width / 2; + yPosition.Value = areaDisplay.FullBackground.Height / 2; + }) + } + }; + + var scaleMenu = new ButtonMenuItem + { + Text = "Scale...", + Items = + { + new AppCommand("Full", () => + { + width.Value = areaDisplay.FullBackground.Width; + height.Value = areaDisplay.FullBackground.Height; + xPosition.Value = width.Value / 2; + yPosition.Value = height.Value / 2; + }), + new AppCommand("Half", () => + { + width.Value = areaDisplay.FullBackground.Width / 2; + height.Value = areaDisplay.FullBackground.Height / 2; + xPosition.Value = areaDisplay.FullBackground.Width / 2; + yPosition.Value = areaDisplay.FullBackground.Height / 2; + }) + } + }; + + var flipMenu = new ButtonMenuItem + { + Text = "Flip...", + Items = + { + new AppCommand("Horizontal", () => xPosition.Value = areaDisplay.FullBackground.Width - xPosition.Value), + new AppCommand("Vertical", () => yPosition.Value = areaDisplay.FullBackground.Height - yPosition.Value) + } + }; + + var displayMenu = new ButtonMenuItem + { + Text = "Display..." + }; + + foreach (var display in app.Displays) + { + var text = display.Index == 1 ? "Primary" : $"Display {display.Index}"; + var setDisplayCommand = new AppCommand(text, () => + { + width.Value = display.Width; + height.Value = display.Height; + xPosition.Value = display.Position.X + display.Width / 2; + yPosition.Value = display.Position.Y + display.Height / 2; + }); + displayMenu.Items.Add(setDisplayCommand); + } + + var contextMenu = new ContextMenu + { + Items = + { + alignMenu, + scaleMenu, + flipMenu, + displayMenu + } + }; + + areaDisplay.MouseDown += (_, e) => + { + if ((e.Buttons & MouseButtons.Alternate) != 0) + contextMenu.Show(); + }; + + var editorControls = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Center, + Spacing = 5, + Items = + { + new StackLayoutItem(null, true), + CreateUnitBox("Width", width, "px"), + CreateUnitBox("Height", height, "px"), + CreateUnitBox("X", xPosition, "px"), + CreateUnitBox("Y", yPosition, "px"), + new StackLayoutItem(null, true) + } + }; + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem + { + Control = new GroupBox + { + Text = "Output", + Padding = 5, + Content = areaDisplay + }, + Expand = true + }, + new Scrollable + { + Border = BorderType.None, + Content = editorControls + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditor.cs b/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditor.cs new file mode 100644 index 000000000..099f9899d --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditor.cs @@ -0,0 +1,59 @@ +using Eto.Forms; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.ViewModels; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class PluginSettingsEditor : DesktopPanel + { + private readonly IControlBuilder _controlBuilder; + private readonly Placeholder _placeholder; + + public PluginSettingsEditor(IControlBuilder controlBuilder) + { + _controlBuilder = controlBuilder; + + Content = _placeholder = new Placeholder + { + Text = "No plugin selected." + }; + } + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + if (DataContext is not SettingsViewModel { Settings: not null, Type: not null } model) + { + Content = _placeholder; + return; + } + + var enableToggle = new CheckBox + { + Text = "Enable" + }; + + enableToggle.CheckedBinding.BindDataContext((SettingsViewModel m) => m.Settings.Enable); + + var layout = new StackLayout + { + Padding = 5, + Spacing = 5, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + enableToggle + } + }; + + foreach (var control in _controlBuilder.Generate(model.Settings, model.Type)) + { + var item = new StackLayoutItem(control); + layout.Items.Add(item); + } + + Content = layout; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditorList.cs b/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditorList.cs new file mode 100644 index 000000000..87c018ddd --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Editors/PluginSettingsEditorList.cs @@ -0,0 +1,60 @@ +using System.Collections.Immutable; +using System.Reflection; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.ViewModels; + +namespace OpenTabletDriver.UX.Controls.Editors +{ + public class PluginSettingsEditorList : DesktopPanel where T : class + { + // TODO: Fix discard/load not updating settings + public PluginSettingsEditorList(IControlBuilder controlBuilder, IPluginFactory pluginFactory, App app) + { + var list = new ListBox + { + ItemKeyBinding = Binding.Property(t => t.FullName!), + ItemTextBinding = Binding.Property(t => t.GetFriendlyName() ?? t.FullName!), + DataStore = pluginFactory.GetMatchingTypes(typeof(T)).ToImmutableArray() + }; + + var settingsPanel = controlBuilder.Build(); + + list.SelectedIndexChanged += (_, _) => + { + if (DataContext is PluginSettingsCollection settingsCollection) + { + var type = list.SelectedItem; + var settings = settingsCollection.FromType(type); + settingsPanel.DataContext = new SettingsViewModel(settings, type); + } + }; + + if (list.DataStore.Any()) + { + Content = new Splitter + { + Panel1 = new Panel + { + MinimumSize = new Size(250, 0), + Content = list + }, + Panel2 = settingsPanel + }; + } + else + { + Content = new Placeholder + { + Text = "No plugins of this type are installed.", + ExtraContent = new Button((_, _) => app.ShowWindow()) + { + Text = "Show plugin manager..." + } + }; + } + } + } +} diff --git a/OpenTabletDriver.UX/Controls/FiltersPanel.cs b/OpenTabletDriver.UX/Controls/FiltersPanel.cs new file mode 100644 index 000000000..be849b327 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/FiltersPanel.cs @@ -0,0 +1,17 @@ +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.Output; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls.Editors; + +namespace OpenTabletDriver.UX.Controls +{ + public class FiltersPanel : DesktopPanel + { + public FiltersPanel(IControlBuilder controlBuilder) + { + var editor = controlBuilder.Build>(); + editor.DataContextBinding.BindDataContext((Profile p) => p.Filters); + Content = editor; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/GeneratedControls.cs b/OpenTabletDriver.UX/Controls/GeneratedControls.cs deleted file mode 100644 index 10b4ed9d8..000000000 --- a/OpenTabletDriver.UX/Controls/GeneratedControls.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Controls -{ - public static class GeneratedControls - { - private static readonly IReadOnlyDictionary, Control>> genericControls - = new Dictionary, Control>> - { - { typeof(sbyte), GetNumericMaskedTextBox }, - { typeof(byte), GetNumericMaskedTextBox }, - { typeof(short), GetNumericMaskedTextBox }, - { typeof(ushort), GetNumericMaskedTextBox }, - { typeof(int), GetNumericMaskedTextBox }, - { typeof(uint), GetNumericMaskedTextBox }, - { typeof(long), GetNumericMaskedTextBox }, - { typeof(ulong), GetNumericMaskedTextBox }, - { typeof(double), GetMaskedTextBox }, - { typeof(DateTime), GetMaskedTextBox }, - { typeof(TimeSpan), GetMaskedTextBox } - }; - - public static Control GetControlForProperty(PluginSettingStore store, PropertyInfo property) - { - var attr = property.GetCustomAttribute(); - PluginSetting setting = store[property]; - - if (setting == null) - { - setting = new PluginSetting(property, null); - store.Settings.Add(setting); - } - - var settingBinding = new DelegateBinding( - () => store[property], - (v) => store[property] = v - ); - - var control = GetControlForSetting(property, settingBinding); - - if (control != null) - { - // Apply all visual modifier attributes - foreach (ModifierAttribute modifierAttr in property.GetCustomAttributes()) - control = ApplyModifierAttribute(control, modifierAttr); - - control.Width = 400; - return new Group(attr.DisplayName ?? property.Name, control, Orientation.Horizontal, false); - } - else - { - throw new NullReferenceException($"{nameof(control)} is null. This is likely due to {property.PropertyType.Name} being an unsupported type."); - } - } - - private static Control GetControlForSetting(PropertyInfo property, DirectBinding binding) - { - if (property.PropertyType == typeof(string)) - { - if (property.GetCustomAttribute() is PropertyValidatedAttribute validateAttr) - { - var comboBox = new DropDown - { - DataStore = validateAttr.GetValue>(property), - }; - comboBox.SelectedItemBinding.Bind(binding.Convert(property)); - return comboBox; - } - else - { - var textBox = new TextBox(); - textBox.TextBinding.Bind(binding.Convert(property)); - return textBox; - } - } - else if (property.PropertyType == typeof(bool)) - { - string description = property.Name; - if (property.GetCustomAttribute() is BooleanPropertyAttribute attribute) - description = attribute.Description; - - var checkbox = new CheckBox - { - Text = description - }; - checkbox.CheckedBinding.Cast().Bind( - binding.Convert( - s => s.GetValueOrDefault(property), - v => new PluginSetting(property, v) - ) - ); - return checkbox; - } - else if (property.PropertyType == typeof(float)) - { - var tb = GetMaskedTextBox(property, binding); - - if (property.GetCustomAttribute() is SliderPropertyAttribute sliderAttr) - { - // TODO: replace with slider when possible (https://github.com/picoe/Eto/issues/1772) - tb.ToolTip = $"Minimum: {sliderAttr.Min}, Maximum: {sliderAttr.Max}"; - tb.PlaceholderText = $"{sliderAttr.DefaultValue}"; - - if (!binding.DataValue.HasValue) - binding.DataValue.SetValue(sliderAttr.DefaultValue); - } - return tb; - } - else if (genericControls.TryGetValue(property.PropertyType, out var getGenericTextBox)) - { - return getGenericTextBox(property, binding); - } - throw new NotSupportedException($"'{property.PropertyType}' is not supported for generated controls."); - } - - private static Control ApplyModifierAttribute(Control control, ModifierAttribute attribute) - { - switch (attribute) - { - case ToolTipAttribute toolTipAttr: - { - control.ToolTip = toolTipAttr.ToolTip; - return control; - } - // This might cause issues if this is done before another attribute. - case UnitAttribute unitAttr: - { - var label = new Label { Text = unitAttr.Unit }; - var layout = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem(control, true), - new StackLayoutItem(label, VerticalAlignment.Center) - } - }; - return layout; - } - default: - return control; - } - } - - private static NumericMaskedTextBox GetNumericMaskedTextBox(PropertyInfo property, DirectBinding binding) - { - return GetMaskedTextBox, T>(property, binding); - } - - private static MaskedTextBox GetMaskedTextBox(PropertyInfo property, DirectBinding binding) - { - return GetMaskedTextBox, T>(property, binding); - } - - private static TControl GetMaskedTextBox(PropertyInfo property, DirectBinding binding) where TControl : MaskedTextBox, new() - { - var textBox = new TControl(); - textBox.ValueBinding.Bind(binding.Convert(property)); - return textBox; - } - - private static DirectBinding Convert(this DirectBinding binding, PropertyInfo property) - { - return binding.Convert( - s => s.GetValueOrDefault(property), - v => new PluginSetting(property, v) - ); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Dictionary/DictionaryEditor.cs b/OpenTabletDriver.UX/Controls/Generic/Dictionary/DictionaryEditor.cs deleted file mode 100644 index 1a61c906f..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Dictionary/DictionaryEditor.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic.Dictionary -{ - public abstract class DictionaryEditor : Panel - { - public DictionaryEditor() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = layout - }, - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Right, - Control = add = new Button - { - Text = "+" - } - } - } - }; - - add.Click += (sender, e) => - { - if (ItemSource == null) - ItemSource = new Dictionary(); - AddNew(); - }; - } - - private Button add; - - protected StackLayout layout = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5 - }; - - private IDictionary itemSource; - public IDictionary ItemSource - { - set - { - this.itemSource = value; - this.OnItemSourceChanged(); - } - get => this.itemSource; - } - - public event EventHandler ItemSourceChanged; - - protected virtual void OnItemSourceChanged() - { - ItemSourceChanged?.Invoke(this, new EventArgs()); - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - public BindableBinding, IDictionary> ItemSourceBinding - { - get - { - return new BindableBinding, IDictionary>( - this, - c => c.ItemSource, - (c, v) => c.ItemSource = v, - (c, h) => c.ItemSourceChanged += h, - (c, h) => c.ItemSourceChanged -= h - ); - } - } - - /// - /// Creates a control for an instance of a T in the enumerable. - /// - /// The index in which the item was sourced from. - /// A binding to the item sourced from . - /// A control that handles the object - protected abstract Control CreateControl(DirectBinding keyBinding, DirectBinding valueBinding); - - /// - /// Internally used to create the base control item for the control created at . - /// - /// A binding that specifies the key in the dictionary - /// A binding that modifies the 's value in the dictionary. - /// A base control that handles the object - protected virtual Control CreateControlBase(DirectBinding keyBinding, DirectBinding valueBinding) - { - var remove = new Button - { - Text = "-" - }; - remove.Click += (sender, e) => Remove(keyBinding.DataValue); - - return new StackLayout - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Top, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = CreateControl(keyBinding, valueBinding) - }, - new StackLayoutItem - { - VerticalAlignment = VerticalAlignment.Bottom, - Control = remove - } - } - }; - } - - protected virtual void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - // Others are not implemented right now, no use case we currently have requires their implementation. - case NotifyCollectionChangedAction.Reset: - default: - { - layout.Items.Clear(); - - int index = 0; - foreach (KeyValuePair item in ItemSource ?? new Dictionary()) - { - Insert(item.Key); - index++; - } - break; - } - } - } - - protected virtual void Insert(TKey key) - { - var keyBinding = new DelegateBinding( - () => key, - (newKey) => - { - var value = ItemSource[key]; - ItemSource.Remove(key); - ItemSource.Add(newKey, value); - key = newKey; - } - ); - - var valueBinding = new DelegateBinding( - () => ItemSource[key], - (newValue) => ItemSource[key] = newValue - ); - - var control = CreateControlBase(keyBinding, valueBinding); - var index = layout.Items.Count; - layout.Items.Insert(index, control); - } - - protected abstract void AddNew(); - - protected virtual void Add(TKey key, TValue value) - { - if (ItemSource.TryAdd(key, value)) - { - var pair = ItemSource.First(t => t.Key.Equals(key)); - if (!(ItemSource is INotifyCollectionChanged)) - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, pair)); - } - } - - protected virtual void Remove(TKey key) - { - var oldObj = ItemSource.First(k => k.Key.Equals(key)); - ItemSource.Remove(key); - - if (ItemSource.Count == 0) - ItemSource = null; - - if (!(ItemSource is INotifyCollectionChanged)) - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldObj)); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Dictionary/StringDictionaryEditor.cs b/OpenTabletDriver.UX/Controls/Generic/Dictionary/StringDictionaryEditor.cs deleted file mode 100644 index d54c4cd0a..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Dictionary/StringDictionaryEditor.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Linq; -using Eto.Drawing; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic.Dictionary -{ - public class StringDictionaryEditor : DictionaryEditor - { - protected override Control CreateControl(DirectBinding keyBinding, DirectBinding valueBinding) - { - TextBox keyBox = new TextBox(); - keyBox.TextBinding.Bind(keyBinding); - keyBox.TextChanging += (sender, e) => e.Cancel = ItemSource.Keys.Contains(e.NewText); - - TextBox valueBox = new TextBox(); - valueBox.TextBinding.Bind(valueBinding); - - return new StackLayout - { - Orientation = Orientation.Horizontal, - Padding = new Padding(0, 0, 5, 0), - Spacing = 5, - Items = - { - new StackLayoutItem(keyBox, true), - new StackLayoutItem(valueBox, true) - } - }; - } - - protected override void AddNew() => Add(string.Empty, string.Empty); - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/DropDown.cs b/OpenTabletDriver.UX/Controls/Generic/DropDown.cs deleted file mode 100644 index b3ae4e743..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/DropDown.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class DropDown : DropDown where T : class - { - public T SelectedItem - { - set => this.SelectedValue = value; - get => this.SelectedValue as T; - } - - public BindableBinding, T> SelectedItemBinding - { - get - { - return new BindableBinding, T>( - this, - c => c.SelectedValue as T, - (c, v) => c.SelectedValue = v, - (c, h) => c.SelectedValueChanged += h, - (c, h) => c.SelectedValueChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/FloatSlider.cs b/OpenTabletDriver.UX/Controls/Generic/FloatSlider.cs deleted file mode 100644 index 6e0da150d..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/FloatSlider.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.ComponentModel; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Controls -{ - /// - /// A slider with a textbox for fine tuning a floating point value. - /// - public class FloatSlider : Panel - { - public FloatSlider() - { - var slider = new Slider - { - MinValue = Minimum, - MaxValue = Maximum - }; - - var nb = new FloatNumberBox(); - - slider.Bind( - s => s.Value, - nb.ValueBinding - ); - - nb.ValueBinding.Bind(this.ValueBinding); - - this.Content = new StackView - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Center, - Items = - { - new StackLayoutItem(slider, true), - new StackLayoutItem(nb, false) - } - }; - } - - public event EventHandler ValueChanged; - - private float value; - public float Value - { - set - { - this.value = value; - ValueChanged?.Invoke(this, new EventArgs()); - } - get => this.value; - } - - [DefaultValue(0)] - public int Minimum { set; get; } = 0; - - [DefaultValue(100)] - public int Maximum { set; get; } = 100; - - public BindableBinding ValueBinding - { - get - { - return new BindableBinding( - this, - c => c.Value, - (c, v) => c.Value = v, - (c, h) => c.ValueChanged += h, - (c, h) => c.ValueChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/GeneratedItemList.cs b/OpenTabletDriver.UX/Controls/Generic/GeneratedItemList.cs deleted file mode 100644 index bacb43105..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/GeneratedItemList.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public abstract class GeneratedItemList : Panel - { - public GeneratedItemList() - { - this.Content = layout; - } - - protected StackLayout layout = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5 - }; - - private IList itemSource; - public IList ItemSource - { - set - { - // Unhook the old collection change event if applicable - if (ItemSource is INotifyCollectionChanged oldNotify) - oldNotify.CollectionChanged -= HandleCollectionChanged; - - this.itemSource = value; - this.OnItemSourceChanged(); - - // Hook the collection change event if applicable - if (ItemSource is INotifyCollectionChanged newNotify) - newNotify.CollectionChanged += HandleCollectionChanged; - } - get => this.itemSource; - } - - public event EventHandler ItemSourceChanged; - - public BindableBinding, IList> ItemSourceBinding - { - get - { - return new BindableBinding, IList>( - this, - c => c.ItemSource, - (c, v) => c.ItemSource = v, - (c, h) => c.ItemSourceChanged += h, - (c, h) => c.ItemSourceChanged -= h - ); - } - } - - /// - /// Creates a control for an instance of a T in the enumerable. - /// - /// The index in which the item was sourced from. - /// A binding to the item sourced from . - /// A control that handles the object - protected abstract Control CreateControl(int index, DirectBinding itemBinding); - - /// - /// Internally used to create the base control item for the control created at . - /// - /// The index in which the item was sourced from. - /// A binding to the item sourced from . - /// A base control that handles the object - protected virtual Control CreateControlBase(int index, DirectBinding itemBinding) => CreateControl(index, itemBinding); - - protected virtual void OnItemSourceChanged() - { - ItemSourceChanged?.Invoke(this, new EventArgs()); - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - protected virtual void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - // Others are not implemented right now, no use case we currently have requires their implementation. - case NotifyCollectionChangedAction.Reset: - default: - { - layout.Items.Clear(); - - int index = 0; - foreach (T item in ItemSource ?? Array.Empty()) - { - Insert(index); - index++; - } - break; - } - } - } - - protected virtual void Insert(int index) - { - var itemBinding = new DelegateBinding( - () => ItemSource[index], - (v) => ItemSource[index] = v - ); - - var control = CreateControlBase(index, itemBinding); - layout.Items.Insert(index, control); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Group.cs b/OpenTabletDriver.UX/Controls/Generic/Group.cs deleted file mode 100644 index 97e228c81..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Group.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class Group : Panel - { - public Group() - { - } - - public Group(string text, Control content, Orientation orientation = DEFAULT_ORIENTATION, bool expand = true) - : this() - { - this.Text = text; - this.Content = content; - this.Orientation = orientation; - this.ExpandContent = expand; - } - - private const Orientation DEFAULT_ORIENTATION = Orientation.Vertical; - - protected virtual Padding ContentPadding => DesktopInterop.CurrentPlatform == PluginPlatform.Windows ? new Padding(5, 10, 5, 5) : new Padding(5); - - protected virtual Color HorizontalBackgroundColor => SystemColors.ControlBackground; - protected virtual Color VerticalBackgroundColor => SystemColors.WindowBackground; - - private string text; - public string Text - { - set - { - this.text = value; - UpdateControlLayout(); - } - get => text; - } - - private Control content; - public new Control Content - { - set - { - this.content = value; - UpdateControlLayout(); - } - get => content; - } - - public Orientation Orientation { set; get; } = DEFAULT_ORIENTATION; - public bool ExpandContent { set; get; } = true; - public HorizontalAlignment TitleHorizontalAlignment { set; get; } = HorizontalAlignment.Left; - public VerticalAlignment TitleVerticalAlignment { set; get; } = VerticalAlignment.Center; - - protected void UpdateControlLayout() - { - if (!this.Loaded) - return; - - switch (Orientation, DesktopInterop.CurrentPlatform) - { - case (_, PluginPlatform.MacOS): - { - base.Content = new GroupBox - { - Text = this.Text, - Padding = new Padding(0, 2, 0, 0), - Content = this.Content - }; - break; - } - case (Orientation.Horizontal, _): - { - StackLayout contentLayout; - base.Content = new GroupBox - { - BackgroundColor = HorizontalBackgroundColor, - Content = contentLayout = new StackLayout - { - VerticalContentAlignment = VerticalAlignment.Stretch, - Orientation = Orientation.Horizontal, - Spacing = 5, - Padding = ContentPadding, - Items = - { - new StackLayoutItem - { - VerticalAlignment = TitleVerticalAlignment, - Control = new Label - { - Text = this.Text - } - }, - new StackLayoutItem(this.Content, ExpandContent) - } - } - }; - if (!ExpandContent) - contentLayout.Items.Insert(1, new StackLayoutItem(null, true)); - break; - } - case (Orientation.Vertical, _): - { - base.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - VerticalContentAlignment = VerticalAlignment.Center, - Spacing = 5, - Padding = ContentPadding, - Items = - { - new StackLayoutItem - { - HorizontalAlignment = TitleHorizontalAlignment, - Control = new Label - { - Text = this.Text, - Font = SystemFonts.Bold(9) - } - }, - new StackLayoutItem - { - Expand = true, - Control = new GroupBox - { - BackgroundColor = VerticalBackgroundColor, - Padding = ContentPadding, - Content = this.Content - } - } - } - }; - break; - } - } - } - - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - UpdateControlLayout(); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/InputBox.cs b/OpenTabletDriver.UX/Controls/Generic/InputBox.cs deleted file mode 100644 index 50cd44d16..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/InputBox.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class InputBox : Group - { - public InputBox( - string name, - Func getValue, - Action setValue, - string placeholder = null, - int textboxWidth = 300 - ) - { - this.Orientation = Orientation.Horizontal; - this.ExpandContent = false; - - var textBox = new TextBox - { - Width = textboxWidth, - PlaceholderText = placeholder - }; - textBox.TextBinding.Bind(getValue, setValue); - - base.Content = textBox; - base.Text = name; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/ModifiableConstructableItemList.cs b/OpenTabletDriver.UX/Controls/Generic/ModifiableConstructableItemList.cs deleted file mode 100644 index de072bae2..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/ModifiableConstructableItemList.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OpenTabletDriver.UX.Controls.Generic -{ - public abstract class ModifiableConstructableItemList : ModifiableItemList where T : new() - { - protected override void AddNew() => base.Add(ItemSource.Count, new T()); - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/ModifiableItemList.cs b/OpenTabletDriver.UX/Controls/Generic/ModifiableItemList.cs deleted file mode 100644 index 03170f91b..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/ModifiableItemList.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public abstract class ModifiableItemList : GeneratedItemList - { - public ModifiableItemList() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = layout - }, - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Right, - Control = add = new Button - { - Text = "+" - } - } - } - }; - - add.Click += (sender, e) => - { - if (ItemSource == null) - ItemSource = new List(); - AddNew(); - }; - } - - private Button add; - - /// - /// Invoked by the user to request addition of a new object at a specific index. - /// - protected abstract void AddNew(); - - /// - /// Invoked internally to add a new object at a specific - /// - /// The index in which the item will be added. - /// The object to insert at the index. - protected virtual void Add(int index, T obj) - { - ItemSource.Insert(index, obj); - if (!(ItemSource is INotifyCollectionChanged)) - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, obj, index)); - } - - /// - /// Invoked by the user to request deletion of an object at a specific index. - /// - /// The index in which the item will be removed. - protected virtual void Remove(int index) - { - var oldObj = ItemSource[index]; - ItemSource.RemoveAt(index); - - if (ItemSource.Count == 0) - ItemSource = null; - - if (!(ItemSource is INotifyCollectionChanged)) - HandleCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldObj, index)); - } - - protected override Control CreateControlBase(int index, DirectBinding itemBinding) - { - var remove = new Button - { - Text = "-" - }; - remove.Click += (sender, e) => Remove(index); - - return new StackLayout - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Top, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = base.CreateControlBase(index, itemBinding) - }, - new StackLayoutItem - { - VerticalAlignment = VerticalAlignment.Bottom, - Control = remove - } - } - }; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/PaddingSpacerItem.cs b/OpenTabletDriver.UX/Controls/Generic/PaddingSpacerItem.cs deleted file mode 100644 index 80185fbd9..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/PaddingSpacerItem.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class PaddingSpacerItem : StackLayoutItem - { - public PaddingSpacerItem() - { - this.Control = null; - this.Expand = true; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Reflection/Extensions.cs b/OpenTabletDriver.UX/Controls/Generic/Reflection/Extensions.cs deleted file mode 100644 index 4eb5a192e..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Reflection/Extensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Reflection; -using OpenTabletDriver.Plugin.Attributes; - -namespace OpenTabletDriver.UX.Controls.Generic.Reflection -{ - public static class Extensions - { - public static string GetFriendlyName(this TypeInfo type) - { - return type.GetCustomAttribute()?.Name ?? type.FullName; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeDropDown.cs b/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeDropDown.cs deleted file mode 100644 index 1e5ed9f88..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeDropDown.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Eto.Forms; -using OpenTabletDriver.Desktop; - -namespace OpenTabletDriver.UX.Controls.Generic.Reflection -{ - public class TypeDropDown : DropDown where T : class - { - public TypeDropDown() - { - this.ItemTextBinding = Binding.Property(t => t.GetFriendlyName()); - this.ItemKeyBinding = Binding.Property(t => t.FullName); - - AppInfo.PluginManager.AssembliesChanged += HandleAssembliesChanged; - } - - public T ConstructSelectedType(params object[] args) - { - if (SelectedItem != null) - { - args ??= Array.Empty(); - return AppInfo.PluginManager.ConstructObject(SelectedItem.FullName); - } - return null; - } - - public void Select(Func predicate) - { - foreach (TypeInfo type in DataStore) - { - var obj = AppInfo.PluginManager.ConstructObject(type.FullName); - if (predicate(obj)) - { - this.SelectedValue = type; - break; - } - } - } - - protected override IEnumerable CreateDefaultDataStore() - { - var query = from type in AppInfo.PluginManager.GetChildTypes() - orderby type.GetFriendlyName() - select type; - return query.ToList(); - } - - private void HandleAssembliesChanged(object sender, EventArgs e) => Application.Instance.AsyncInvoke(() => - { - this.DataStore = CreateDefaultDataStore(); - }); - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeListBox.cs b/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeListBox.cs deleted file mode 100644 index 6f6ce141d..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Reflection/TypeListBox.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Eto.Forms; -using OpenTabletDriver.Desktop; - -namespace OpenTabletDriver.UX.Controls.Generic.Reflection -{ - public class TypeListBox : ListBox where T : class - { - public TypeListBox() - { - this.ItemTextBinding = Binding.Property(t => t.GetFriendlyName()); - this.ItemKeyBinding = Binding.Property(t => t.FullName); - - AppInfo.PluginManager.AssembliesChanged += HandleAssembliesChanged; - // Manual update of the DataStore seems to be required, however it isn't on DropDown. Bug? - this.DataStore = CreateDefaultDataStore(); - } - - public T ConstructSelectedType(params object[] args) - { - if (SelectedItem != null) - { - args ??= Array.Empty(); - return AppInfo.PluginManager.ConstructObject(SelectedItem.FullName); - } - return null; - } - - public void Select(Func predicate) - { - foreach (TypeInfo type in DataStore) - { - var obj = AppInfo.PluginManager.ConstructObject(type.FullName); - if (predicate(obj)) - { - this.SelectedValue = type; - break; - } - } - } - - protected override IEnumerable CreateDefaultDataStore() - { - var query = from type in AppInfo.PluginManager.GetChildTypes() - orderby type.GetFriendlyName() - select type; - return query.ToList(); - } - - private void HandleAssembliesChanged(object sender, EventArgs e) => Application.Instance.AsyncInvoke(() => this.DataStore = CreateDefaultDataStore()); - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/ScheduledDrawable.cs b/OpenTabletDriver.UX/Controls/Generic/ScheduledDrawable.cs deleted file mode 100644 index 4ad4c5c2b..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/ScheduledDrawable.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.UX.Tools; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public abstract class ScheduledDrawable : Drawable - { - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - base.ParentWindow.Closing += (sender, e) => CompositionScheduler.Unregister(OnCompose); - base.ParentWindow.WindowStateChanged += (sender, e) => - { - if (base.ParentWindow == null || base.ParentWindow.WindowState == WindowState.Minimized) - CompositionScheduler.Unregister(OnCompose); - else - CompositionScheduler.Register(OnCompose); - }; - - CompositionScheduler.Register(OnCompose); - } - - protected abstract void OnNextFrame(PaintEventArgs e); - - protected override void OnPaint(PaintEventArgs e) - { - base.OnPaint(e); - OnNextFrame(e); - } - - protected void OnCompose(object _, EventArgs a) - { - Invalidate(); - } - - ~ScheduledDrawable() - { - CompositionScheduler.Unregister(OnCompose); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/StackView.cs b/OpenTabletDriver.UX/Controls/Generic/StackView.cs deleted file mode 100644 index 28f52e0f1..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/StackView.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class StackView : StackLayout - { - public StackView(IEnumerable controls) : this() - { - foreach (var ctrl in controls) - AddControl(ctrl); - } - - public StackView(params Control[] controls) : this((IEnumerable)controls) - { - } - - protected StackView() - { - base.HorizontalContentAlignment = defaultHorizontalAlignment; - base.VerticalContentAlignment = defaultVerticalAlignment; - base.Spacing = 5; - } - - private const HorizontalAlignment defaultHorizontalAlignment = HorizontalAlignment.Stretch; - private const VerticalAlignment defaultVerticalAlignment = VerticalAlignment.Stretch; - - public void AddControl(Control ctrl, bool expand = false) - { - var newItem = new StackLayoutItem(ctrl, expand); - base.Items.Add(newItem); - } - - public void AddControls(IEnumerable controls) - { - foreach (var ctrl in controls) - AddControl(ctrl); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/StackedContent.cs b/OpenTabletDriver.UX/Controls/Generic/StackedContent.cs deleted file mode 100644 index d76179383..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/StackedContent.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class StackedContent : StackLayout, IEnumerable, IEnumerable - { - public StackedContent() - { - this.Padding = 5; - this.Spacing = 5; - this.HorizontalContentAlignment = HorizontalAlignment.Center; - } - - public void Add(Control control) - { - var item = new StackLayoutItem(control); - this.Items.Add(item); - } - - public void Add(params string[] items) - { - var control = new TextContent(items); - this.Add(control); - } - - public void Add(StackLayoutItem item) => this.Items.Add(item); - - IEnumerator IEnumerable.GetEnumerator() => this.Controls.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => this.Items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => this.Controls.GetEnumerator(); - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/StylizedText.cs b/OpenTabletDriver.UX/Controls/Generic/StylizedText.cs deleted file mode 100644 index 788ef6077..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/StylizedText.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Eto.Drawing; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class StylizedText : Panel - { - public StylizedText() - : base() - { - Content = new Label(); - } - - public StylizedText(string text, Font font) - : this() - { - Text = text; - Font = font; - } - - public StylizedText(string text, Font font, Padding padding) - : this(text, font) - { - Padding = padding; - } - - public string Text - { - get => ((Label)Content).Text; - set => ((Label)Content).Text = value; - } - - public Font Font - { - get => ((Label)Content).Font; - set => ((Label)Content).Font = value; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/DoubleNumberBox.cs b/OpenTabletDriver.UX/Controls/Generic/Text/DoubleNumberBox.cs deleted file mode 100644 index da01ad853..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/DoubleNumberBox.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Globalization; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; - -namespace OpenTabletDriver.UX.Controls.Generic.Text -{ - public class DoubleNumberBox : MaskedTextBox - { - public DoubleNumberBox() - { - Provider = new DoubleTextProvider(); - } - - private class DoubleTextProvider : NumberTextProvider - { - public override double Value - { - set => Text = value.ToString(CultureInfo.InvariantCulture); - get => double.TryParse(Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var val) ? val : default(double); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/FloatNumberBox.cs b/OpenTabletDriver.UX/Controls/Generic/Text/FloatNumberBox.cs deleted file mode 100644 index 9d3bdff01..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/FloatNumberBox.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Globalization; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; - -namespace OpenTabletDriver.UX.Controls.Generic.Text -{ - public class FloatNumberBox : MaskedTextBox - { - public FloatNumberBox() - { - Provider = new FloatTextProvider(); - } - - private class FloatTextProvider : NumberTextProvider - { - public override float Value - { - set => Text = value.ToString(CultureInfo.InvariantCulture); - get => float.TryParse(Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var val) ? val : default(float); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/HexByteArrayBox.cs b/OpenTabletDriver.UX/Controls/Generic/Text/HexByteArrayBox.cs deleted file mode 100644 index ab8af7fce..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/HexByteArrayBox.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Globalization; -using System.Text.RegularExpressions; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; - -namespace OpenTabletDriver.UX.Controls.Generic.Text -{ - public class HexByteArrayBox : MaskedTextBox - { - public HexByteArrayBox() - { - Provider = new HexByteArrayTextProvider(); - } - - private class HexByteArrayTextProvider : RegexTextProvider - { - protected override Regex Regex => new Regex(@"^(?:(?:(?:(?<=^| )0?(?<=0)x?)?(?:(?<=0x)[0-9A-F]{1,2})?)(?:(? Text = ToHexString(value); - get => ToByteArray(Text); - } - - private bool TryGetHexValue(string str, out byte value) => byte.TryParse(str.Replace("0x", string.Empty), NumberStyles.HexNumber, null, out value); - - private string ToHexString(byte[] value) - { - if (value is byte[] array) - return "0x" + BitConverter.ToString(array).Replace("-", " 0x") ?? string.Empty; - else - return string.Empty; - } - - private byte[] ToByteArray(string hex) - { - var raw = hex.Split(' '); - byte[] buffer = new byte[raw.Length]; - for (int i = 0; i < raw.Length; i++) - { - if (TryGetHexValue(raw[i], out var val)) - buffer[i] = val; - else - return null; - } - return buffer; - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/IntegerNumberBox.cs b/OpenTabletDriver.UX/Controls/Generic/Text/IntegerNumberBox.cs deleted file mode 100644 index bdaab3572..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/IntegerNumberBox.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Globalization; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; - -namespace OpenTabletDriver.UX.Controls.Generic.Text -{ - public class IntegerNumberBox : MaskedTextBox - { - public IntegerNumberBox() - { - Provider = new IntegerTextProvider(); - } - - private class IntegerTextProvider : NumberTextProvider - { - public override int Value - { - set => Text = value.ToString(CultureInfo.InvariantCulture); - get => int.TryParse(Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var val) ? val : default(int); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/NumberTextProvider.cs b/OpenTabletDriver.UX/Controls/Generic/Text/Providers/NumberTextProvider.cs deleted file mode 100644 index 35c0fbf82..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/NumberTextProvider.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace OpenTabletDriver.UX.Controls.Generic.Text.Providers -{ - public abstract class NumberTextProvider : MaskedTextProvider - { - private const char DECIMAL_CHAR = '.'; - - protected override bool Allow(ref char character, ref int position) - { - bool allow = false; - if (!allow && character == DECIMAL_CHAR) - { - character = DECIMAL_CHAR; - var decimalIndex = Text.IndexOf(DECIMAL_CHAR); - - if (decimalIndex >= 0) - { - builder.Remove(decimalIndex, 1); - if (position > decimalIndex) - position--; - } - - allow = true; - if (position < builder.Length && !char.IsDigit(builder[position])) - { - // insert at correct location and move cursor - int idx; - for (idx = 0; idx < builder.Length; idx++) - { - if (char.IsDigit(builder[idx])) - { - break; - } - } - position = idx; - allow = true; - } - } - - if (!allow && character == '-') - { - var val = Text; - if (val.IndexOf('-') == 0) - { - builder.Remove(0, 1); - if (position == 0) - position++; - } - else - position++; - builder.Insert(0, character); - return false; - } - allow |= char.IsDigit(character); - return allow; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/RegexTextProvider.cs b/OpenTabletDriver.UX/Controls/Generic/Text/Providers/RegexTextProvider.cs deleted file mode 100644 index 23bdf988c..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/RegexTextProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.RegularExpressions; - -namespace OpenTabletDriver.UX.Controls.Generic.Text.Providers -{ - public abstract class RegexTextProvider : MaskedTextProvider - { - protected abstract Regex Regex { get; } - - protected override bool Allow(ref char character, ref int position) - { - builder.Insert(position, character); - var builderResult = builder.ToString(); - builder.Remove(position, 1); - return Regex.IsMatch(builderResult); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/UnsignedIntegerNumberBox.cs b/OpenTabletDriver.UX/Controls/Generic/Text/UnsignedIntegerNumberBox.cs deleted file mode 100644 index f61ed51aa..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/Text/UnsignedIntegerNumberBox.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Globalization; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; - -namespace OpenTabletDriver.UX.Controls.Generic.Text -{ - public class UnsignedIntegerNumberBox : MaskedTextBox - { - public UnsignedIntegerNumberBox() - { - Provider = new UnsignedIntegerTextProvider(); - } - - private class UnsignedIntegerTextProvider : NumberTextProvider - { - public override uint Value - { - set => Text = value.ToString(CultureInfo.InvariantCulture); - get => uint.TryParse(Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var val) ? val : default(uint); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Generic/TextContent.cs b/OpenTabletDriver.UX/Controls/Generic/TextContent.cs deleted file mode 100644 index a2d506381..000000000 --- a/OpenTabletDriver.UX/Controls/Generic/TextContent.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Generic -{ - public class TextContent : Label, IEnumerable - { - public TextContent() - { - this.TextAlignment = TextAlignment.Center; - } - - public TextContent(params string[] lines) - : this() - { - foreach (var line in lines) - Add(line); - } - - public IList Lines => base.Text.Split(Environment.NewLine); - - public void Add(string line) - { - if (!string.IsNullOrWhiteSpace(base.Text)) - base.Text += Environment.NewLine; - base.Text += line; - } - - public IEnumerator GetEnumerator() - { - return Lines.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return Lines.GetEnumerator(); - } - - public static implicit operator TextContent(string[] lines) - { - return new TextContent(lines); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/LogView.cs b/OpenTabletDriver.UX/Controls/LogView.cs deleted file mode 100644 index f34b87259..000000000 --- a/OpenTabletDriver.UX/Controls/LogView.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Logging; -using OpenTabletDriver.UX.Tools; - -namespace OpenTabletDriver.UX.Controls -{ - public class LogView : StackLayout - { - public LogView() - { - this.Orientation = Orientation.Vertical; - - var filterSelector = new FilterDropDown(); - filterSelector.SelectedValueChanged += (sender, filter) => this.messageStore.Filter = filterSelector.SelectedValue; - - var toolbar = new StackLayout - { - Orientation = Orientation.Horizontal, - Padding = new Padding(0, 5, 0, 0), - Spacing = 5, - HorizontalContentAlignment = HorizontalAlignment.Left, - Items = - { - filterSelector, - new Button((sender, e) => Copy(this.messageStore)) - { - Text = "Copy All" - } - } - }; - - var copyCommand = new Command((sender, e) => Copy(messageList.SelectedItems)) - { - MenuText = "Copy" - }; - - messageList.ContextMenu = new ContextMenu - { - Items = - { - copyCommand - } - }; - - messageList.KeyDown += (sender, e) => - { - switch (e.Modifiers, e.Key) - { - case (Keys.Control, Keys.C): - { - copyCommand.Execute(); - break; - } - } - }; - - this.Items.Add(new StackLayoutItem(messageList, HorizontalAlignment.Stretch, true)); - this.Items.Add(new StackLayoutItem(toolbar, HorizontalAlignment.Stretch)); - - _ = InitializeAsync(); - } - - private async Task InitializeAsync() - { - var currentMessages = await App.Driver.Instance.GetCurrentLog(); - messageList.DataStore = messageStore = new LogDataStore(currentMessages); - - this.messageStore.CollectionChanged += (sender, e) => - { - Application.Instance.AsyncInvoke(() => - { - if (this.messageStore.Count > 0) - { - if (messageList.SelectedRow == -1) - messageList.ScrollToRow(this.messageStore.Count - 1); - else - messageList.ScrollToRow(messageList.SelectedRow); - } - }); - }; - - App.Driver.Message += (sender, message) => AddMessage(message); - } - - private readonly GridView messageList = new GridView - { - AllowMultipleSelection = true, - Columns = - { - new GridColumn - { - HeaderText = "Time", - DataCell = new TextBoxCell - { - Binding = Binding.Property(m => m.Time.ToLongTimeString()) - } - }, - new GridColumn - { - HeaderText = "Level", - DataCell = new TextBoxCell - { - Binding = Binding.Property(m => Enum.GetName(m.Level)) - } - }, - new GridColumn - { - HeaderText = "Group", - DataCell = new TextBoxCell - { - Binding = Binding.Property(m => m.Group) - } - }, - new GridColumn - { - HeaderText = "Message", - DataCell = new TextBoxCell - { - Binding = Binding.Property(m => m.Message) - } - } - } - }; - - private LogDataStore messageStore; - - private void AddMessage(LogMessage message) - { - Application.Instance.AsyncInvoke(() => this.messageStore.Add(message)); - - if (message.Notification) - { - var notify = new Notification - { - Title = "OpenTabletDriver - " + message.Level.ToString(), - Message = message.Message, - ContentImage = App.Logo, - ID = "log-message-notification" - }; - notify.Show(); - } - } - - private static void Copy(IEnumerable messages) - { - if (messages.Any()) - { - StringBuilder sb = new StringBuilder(); - foreach (var message in messages) - { - var line = Log.GetStringFormat(message); - sb.AppendLine(line); - } - Clipboard.Instance.Clear(); - Clipboard.Instance.Text = sb.ToString(); - } - } - - private class FilterDropDown : EnumDropDown - { - public FilterDropDown(LogLevel activeFilter = LogLevel.Info) - { - SelectedValue = activeFilter; - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/LogViewer.cs b/OpenTabletDriver.UX/Controls/LogViewer.cs new file mode 100644 index 000000000..6d157669d --- /dev/null +++ b/OpenTabletDriver.UX/Controls/LogViewer.cs @@ -0,0 +1,171 @@ +using System.Text; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.RPC; +using OpenTabletDriver.Logging; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class LogViewer : DesktopPanel + { + private readonly IDriverDaemon _daemon; + private readonly GridView _list; + + private LogDataStore? _logMessages; + + public LogViewer(RpcClient rpc) + { + _daemon = rpc.Instance!; + + _list = new GridView + { + AllowMultipleSelection = true, + Columns = + { + new GridColumn + { + HeaderText = "Time", + DataCell = new TextBoxCell + { + Binding = Binding.Property(m => m.Time.ToLongTimeString()) + } + }, + new GridColumn + { + HeaderText = "Level", + DataCell = new TextBoxCell + { + Binding = Binding.Property(m => + Enum.GetName(m.Level) ?? m.Level.ToString()) + } + }, + new GridColumn + { + HeaderText = "Group", + DataCell = new TextBoxCell + { + Binding = Binding.Property(m => m.Group ?? string.Empty) + } + }, + new GridColumn + { + HeaderText = "Message", + DataCell = new TextBoxCell + { + Binding = Binding.Property(m => m.Message ?? string.Empty) + } + } + } + }; + + var filter = new EnumDropDown + { + SelectedValue = LogLevel.Info + }; + filter.SelectedValueChanged += (_, _) => _logMessages!.Filter = filter.SelectedValue; + + var toolbar = new StackLayout + { + Orientation = Orientation.Horizontal, + Padding = new Padding(0, 5, 0, 0), + Spacing = 5, + HorizontalContentAlignment = HorizontalAlignment.Left, + Items = + { + filter, + new Button((_, _) => Copy(_logMessages)) + { + Text = "Copy All" + } + } + }; + + var copyCommand = new Command((_, _) => Copy(_list.SelectedItems)) + { + MenuText = "Copy" + }; + _list.ContextMenu = new ContextMenu + { + Items = + { + copyCommand + } + }; + + _list.KeyDown += (_, e) => + { + switch (e.Modifiers, e.Key) + { + case (Keys.Control, Keys.C): + { + copyCommand.Execute(); + break; + } + } + }; + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + new StackLayoutItem(_list, true), + new StackLayoutItem(toolbar) + } + }; + + InitializeAsync().Run(); + } + + private async Task InitializeAsync() + { + var messages = await _daemon.GetCurrentLog(); + _list.DataStore = _logMessages = new LogDataStore(messages); + _logMessages.CollectionChanged += (_, _) => + { + if (_logMessages.Count > 0) + { + var row = _list.SelectedRow == -1 ? _logMessages.Count - 1 : _list.SelectedRow; + _list.ScrollToRow(row); + } + }; + + _daemon.Message += (_, m) => Application.Instance.AsyncInvoke(() => + { + _logMessages.Add(m); + if (m.Level >= LogLevel.Info) + _list.SelectedRow = _logMessages.Count - 2; + + // TODO: Improve message notifications + if (m.Notification) + MessageBox.Show(m.Group, m.Message); + }); + } + + /// + /// Copies log messages to the clipboard. + /// + /// The messages to copy. + private static void Copy(IEnumerable? messages) + { + if (messages == null) + return; + + var sb = new StringBuilder(); + foreach (var message in messages) + { + var line = Log.GetStringFormat(message); + sb.AppendLine(line); + } + + if (sb.Length > 0) + { + Clipboard.Instance.Clear(); + Clipboard.Instance.Text = sb.ToString(); + } + } + } +} diff --git a/OpenTabletDriver.UX/Controls/MousePanel.cs b/OpenTabletDriver.UX/Controls/MousePanel.cs new file mode 100644 index 000000000..07fa46202 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/MousePanel.cs @@ -0,0 +1,66 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class MousePanel : BindingPanel + { + public MousePanel(IControlBuilder controlBuilder, App app) : base(controlBuilder) + { + var scrollButtons = new GroupBox + { + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = new Padding(5, 5, 5, 0), + Spacing = 5, + Items = + { + ButtonFor(p => p.BindingSettings.MouseScrollUp), + ButtonFor(p => p.BindingSettings.MouseScrollDown) + } + } + }; + + var buttons = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5 + }; + + DataContextChanged += delegate + { + buttons.Items.Clear(); + + if (DataContext is not Profile profile) + return; + + var tablet = app.Tablets.First(t => t.Name == profile.Tablet); + var buttonCount = tablet.Specifications.MouseButtons?.ButtonCount ?? 0; + + foreach (var button in ButtonsFor(c => c.BindingSettings.MouseButtons, buttonCount)) + buttons.Items.Add(button); + }; + + Content = new StackLayout + { + Spacing = 5, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + scrollButtons, + new StackLayoutItem + { + Expand = true, + Control = new Scrollable + { + Content = buttons + } + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/HexNumberBox.cs b/OpenTabletDriver.UX/Controls/Numeric/HexNumberTextBox.cs similarity index 56% rename from OpenTabletDriver.UX/Controls/Generic/Text/HexNumberBox.cs rename to OpenTabletDriver.UX/Controls/Numeric/HexNumberTextBox.cs index bb8a2394a..c218815a2 100644 --- a/OpenTabletDriver.UX/Controls/Generic/Text/HexNumberBox.cs +++ b/OpenTabletDriver.UX/Controls/Numeric/HexNumberTextBox.cs @@ -1,9 +1,9 @@ using System.Globalization; using System.Text.RegularExpressions; using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text.Providers; +using OpenTabletDriver.UX.Controls.Numeric.TextProviders; -namespace OpenTabletDriver.UX.Controls.Generic.Text +namespace OpenTabletDriver.UX.Controls.Numeric { public class HexNumberBox : MaskedTextBox { @@ -19,7 +19,13 @@ private class HexTextProvider : RegexTextProvider public override int Value { set => Text = "0x" + value.ToString("X4"); - get => int.TryParse(Text.Replace("0x", string.Empty), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) ? result : default(int); + get + { + var text = Text?.Replace("0x", string.Empty) ?? string.Empty; + return int.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) + ? result + : default; + } } } } diff --git a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/MaskedTextProvider.cs b/OpenTabletDriver.UX/Controls/Numeric/TextProviders/NumericTextProvider.cs similarity index 60% rename from OpenTabletDriver.UX/Controls/Generic/Text/Providers/MaskedTextProvider.cs rename to OpenTabletDriver.UX/Controls/Numeric/TextProviders/NumericTextProvider.cs index bb86145d0..b0efdda34 100644 --- a/OpenTabletDriver.UX/Controls/Generic/Text/Providers/MaskedTextProvider.cs +++ b/OpenTabletDriver.UX/Controls/Numeric/TextProviders/NumericTextProvider.cs @@ -1,41 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using Eto.Forms; -namespace OpenTabletDriver.UX.Controls.Generic.Text.Providers +namespace OpenTabletDriver.UX.Controls.Numeric.TextProviders { - public abstract class MaskedTextProvider : IMaskedTextProvider + public abstract class NumericTextProvider : IMaskedTextProvider { - protected readonly StringBuilder builder = new StringBuilder(); + protected StringBuilder Builder { get; } = new StringBuilder(); public abstract T Value { get; set; } - public string DisplayText => builder.ToString(); + public string DisplayText => Builder.ToString(); - public string Text + public string? Text { set { - builder.Clear(); + Builder.Clear(); if (value != null) { - int pos = 0; - foreach (char ch in value) + var pos = 0; + foreach (var c in value) { - Insert(ch, ref pos); + Insert(c, ref pos); } } } - get => builder.ToString(); + get => Builder.ToString(); } public bool MaskCompleted => true; - public IEnumerable EditPositions => Enumerable.Range(0, builder.Length); + public IEnumerable EditPositions => Enumerable.Range(0, Builder.Length); - public bool IsEmpty => builder.Length == 0; + public bool IsEmpty => Builder.Length == 0; public bool Clear(ref int position, int length, bool forward) { @@ -44,16 +41,16 @@ public bool Clear(ref int position, int length, bool forward) public bool Delete(ref int position, int length, bool forward) { - if (builder.Length == 0) + if (Builder.Length == 0) return false; if (forward) { - length = Math.Min(length, builder.Length - position); - builder.Remove(position, length); + length = Math.Min(length, Builder.Length - position); + Builder.Remove(position, length); } else if (position >= length) { - builder.Remove(position - length, length); + Builder.Remove(position - length, length); position = Math.Max(0, position - length); } return true; @@ -63,7 +60,7 @@ public bool Insert(char character, ref int position) { if (Allow(ref character, ref position)) { - builder.Insert(position, character); + Builder.Insert(position, character); position++; return true; } @@ -74,9 +71,9 @@ public bool Replace(char character, ref int position) { if (Allow(ref character, ref position)) { - if (position >= builder.Length) + if (position >= Builder.Length) return Insert(character, ref position); - builder[position] = character; + Builder[position] = character; position++; } return true; diff --git a/OpenTabletDriver.UX/Controls/Numeric/TextProviders/RegexTextProvider.cs b/OpenTabletDriver.UX/Controls/Numeric/TextProviders/RegexTextProvider.cs new file mode 100644 index 000000000..22c09ab56 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/Numeric/TextProviders/RegexTextProvider.cs @@ -0,0 +1,17 @@ +using System.Text.RegularExpressions; + +namespace OpenTabletDriver.UX.Controls.Numeric.TextProviders +{ + public abstract class RegexTextProvider : NumericTextProvider + { + protected abstract Regex Regex { get; } + + protected override bool Allow(ref char character, ref int position) + { + Builder.Insert(position, character); + var builderResult = Builder.ToString(); + Builder.Remove(position, 1); + return Regex.IsMatch(builderResult); + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Output/AbsoluteModeEditor.cs b/OpenTabletDriver.UX/Controls/Output/AbsoluteModeEditor.cs deleted file mode 100644 index 92fefa837..000000000 --- a/OpenTabletDriver.UX/Controls/Output/AbsoluteModeEditor.cs +++ /dev/null @@ -1,461 +0,0 @@ -using System; -using System.Numerics; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Plugin.Platform.Display; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Output.Area; -using OpenTabletDriver.UX.Controls.Utilities; -using OpenTabletDriver.UX.Windows; - -namespace OpenTabletDriver.UX.Controls.Output -{ - public class AbsoluteModeEditor : Panel - { - public AbsoluteModeEditor() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new Group - { - Text = "Display", - Content = displayAreaEditor = new DisplayAreaEditor - { - Unit = "px" - } - } - }, - new StackLayoutItem - { - Expand = true, - Control = new Group - { - Text = "Tablet", - Content = tabletAreaEditor = new TabletAreaEditor - { - InvalidBackgroundError = "No tablet detected.", - Unit = "mm" - } - } - } - } - }; - - displayAreaEditor.AreaBinding.Bind(SettingsBinding.Child(c => c.Display)); - displayAreaEditor.LockToUsableAreaBinding.Bind(App.Current, c => c.Settings.LockUsableAreaDisplay); - - tabletAreaEditor.AreaBinding.Bind(SettingsBinding.Child(c => c.Tablet)); - tabletAreaEditor.LockToUsableAreaBinding.Bind(App.Current, c => c.Settings.LockUsableAreaTablet); - - tabletAreaEditor.LockAspectRatioBinding.Bind(SettingsBinding.Child(c => c.LockAspectRatio)); - tabletAreaEditor.AreaClippingBinding.Bind(SettingsBinding.Child(c => c.EnableClipping)); - tabletAreaEditor.IgnoreOutsideAreaBinding.Bind(SettingsBinding.Child(c => c.EnableAreaLimiting)); - - displayWidth = SettingsBinding.Child(c => c.Display.Width); - displayHeight = SettingsBinding.Child(c => c.Display.Height); - tabletWidth = SettingsBinding.Child(c => c.Tablet.Width); - tabletHeight = SettingsBinding.Child(c => c.Tablet.Height); - tabletWidth.DataValueChanged += HandleTabletAreaConstraint; - tabletHeight.DataValueChanged += HandleTabletAreaConstraint; - displayWidth.DataValueChanged += HandleDisplayAreaConstraint; - displayHeight.DataValueChanged += HandleDisplayAreaConstraint; - - tabletAreaEditor.LockAspectRatioChanged += HookAspectRatioLock; - HookAspectRatioLock(tabletAreaEditor, EventArgs.Empty); - } - - internal DisplayAreaEditor displayAreaEditor; - internal TabletAreaEditor tabletAreaEditor; - - private bool handlingArLock; - private bool handlingForcedArConstraint; - private bool handlingSettingsChanging; - private float? prevDisplayWidth; - private float? prevDisplayHeight; - private DirectBinding displayWidth; - private DirectBinding displayHeight; - private DirectBinding tabletWidth; - private DirectBinding tabletHeight; - - private AbsoluteModeSettings settings; - public AbsoluteModeSettings Settings - { - set - { - this.settings = value; - this.OnSettingsChanged(); - } - get => this.settings; - } - - public event EventHandler SettingsChanged; - - protected virtual void OnSettingsChanged() - { - handlingSettingsChanging = true; - SettingsChanged?.Invoke(this, new EventArgs()); - handlingSettingsChanging = false; - } - - public BindableBinding SettingsBinding - { - get - { - return new BindableBinding( - this, - c => c.Settings, - (c, v) => c.Settings = v, - (c, h) => c.SettingsChanged += h, - (c, h) => c.SettingsChanged -= h - ); - } - } - - private void HookAspectRatioLock(object sender, EventArgs args) - { - if (Settings?.LockAspectRatio ?? false) - { - lock (this) - { - HandleAspectRatioLock(tabletAreaEditor, EventArgs.Empty); - - displayWidth.DataValueChanged += HandleAspectRatioLock; - displayHeight.DataValueChanged += HandleAspectRatioLock; - tabletWidth.DataValueChanged += HandleAspectRatioLock; - tabletHeight.DataValueChanged += HandleAspectRatioLock; - } - } - else - { - lock (this) - { - displayWidth.DataValueChanged -= HandleAspectRatioLock; - displayHeight.DataValueChanged -= HandleAspectRatioLock; - tabletWidth.DataValueChanged -= HandleAspectRatioLock; - tabletHeight.DataValueChanged -= HandleAspectRatioLock; - } - } - } - - private void HookAreaConstraint(object sender, EventArgs args) - { - var areaEditor = (AreaEditor)sender; - if (areaEditor.LockToUsableArea) - { - lock (this) - { - if (sender == tabletAreaEditor) - { - tabletWidth.DataValueChanged += HandleTabletAreaConstraint; - tabletHeight.DataValueChanged += HandleTabletAreaConstraint; - } - else if (sender == displayAreaEditor) - { - displayWidth.DataValueChanged += HandleDisplayAreaConstraint; - displayHeight.DataValueChanged += HandleDisplayAreaConstraint; - } - } - } - else - { - lock (this) - { - if (sender == tabletAreaEditor) - { - tabletWidth.DataValueChanged -= HandleTabletAreaConstraint; - tabletHeight.DataValueChanged -= HandleTabletAreaConstraint; - } - else if (sender == displayAreaEditor) - { - displayWidth.DataValueChanged -= HandleDisplayAreaConstraint; - displayHeight.DataValueChanged -= HandleDisplayAreaConstraint; - } - } - } - } - - private void HandleAspectRatioLock(object sender, EventArgs e) - { - if (!handlingArLock && !handlingSettingsChanging) - { - // Avoids looping - handlingArLock = true; - - if (sender == tabletWidth || sender == tabletAreaEditor) - tabletHeight.DataValue = displayHeight.DataValue / displayWidth.DataValue * tabletWidth.DataValue; - else if (sender == tabletHeight) - tabletWidth.DataValue = displayWidth.DataValue / displayHeight.DataValue * tabletHeight.DataValue; - else if ((sender == displayWidth) && prevDisplayWidth is float prevWidth) - tabletWidth.DataValue *= displayWidth.DataValue / prevWidth; - else if ((sender == displayHeight) && prevDisplayHeight is float prevHeight) - tabletHeight.DataValue *= displayHeight.DataValue / prevHeight; - - prevDisplayWidth = displayWidth.DataValue; - prevDisplayHeight = displayHeight.DataValue; - - handlingArLock = false; - } - } - - private void HandleTabletAreaConstraint(object sender, EventArgs args) - { - ForceAreaConstraint(tabletAreaEditor.Display, args); - } - - private void HandleDisplayAreaConstraint(object sender, EventArgs args) - { - ForceAreaConstraint(displayAreaEditor.Display, args); - } - - private void ForceAreaConstraint(object sender, EventArgs args) - { - var display = (AreaDisplay)sender; - if (!handlingForcedArConstraint && !handlingSettingsChanging && display.LockToUsableArea && display.Area != null) - { - handlingForcedArConstraint = true; - var fullBounds = display.FullAreaBounds; - - if (fullBounds.Width != 0 && fullBounds.Height != 0) - { - if (display.Area.Width > fullBounds.Width) - display.Area.Width = fullBounds.Width; - if (display.Area.Height > fullBounds.Height) - display.Area.Height = fullBounds.Height; - - var correction = GetOutOfBoundsAmount(display, display.Area.X, display.Area.Y); - display.Area.X -= correction.X; - display.Area.Y -= correction.Y; - } - - handlingForcedArConstraint = false; - } - } - - private static Vector2 GetOutOfBoundsAmount(AreaDisplay display, float X, float Y) - { - var bounds = display.FullAreaBounds; - bounds.X = 0; - bounds.Y = 0; - - var area = display.Area; - var rect = RectangleF.FromCenter(PointF.Empty, new SizeF(area.Width, area.Height)); - - var corners = new PointF[] - { - PointF.Rotate(rect.TopLeft, area.Rotation), - PointF.Rotate(rect.TopRight, area.Rotation), - PointF.Rotate(rect.BottomRight, area.Rotation), - PointF.Rotate(rect.BottomLeft, area.Rotation) - }; - - var pseudoArea = new RectangleF( - PointF.Min(corners[0], PointF.Min(corners[1], PointF.Min(corners[2], corners[3]))), - PointF.Max(corners[0], PointF.Max(corners[1], PointF.Max(corners[2], corners[3]))) - ); - - pseudoArea.Center += new PointF(X, Y); - - return new Vector2 - { - X = Math.Max(pseudoArea.Right - bounds.Right - 1, 0) + Math.Min(pseudoArea.Left - bounds.Left, 0), - Y = Math.Max(pseudoArea.Bottom - bounds.Bottom - 1, 0) + Math.Min(pseudoArea.Top - bounds.Top, 0) - }; - } - - public class DisplayAreaEditor : AreaEditor - { - public DisplayAreaEditor() - : base() - { - this.ToolTip = "You can right click the area editor to set the area to a display, adjust alignment, or resize the area."; - } - - protected override void CreateMenu() - { - base.CreateMenu(); - - var subMenu = base.ContextMenu.Items.GetSubmenu("Set to display"); - foreach (var display in DesktopInterop.VirtualScreen.Displays) - { - subMenu.Items.Add( - new ActionCommand - { - MenuText = display.ToString(), - Action = () => - { - this.Area.Width = display.Width; - this.Area.Height = display.Height; - if (display is IVirtualScreen virtualScreen) - { - this.Area.X = virtualScreen.Width / 2; - this.Area.Y = virtualScreen.Height / 2; - } - else - { - virtualScreen = DesktopInterop.VirtualScreen; - this.Area.X = display.Position.X + virtualScreen.Position.X + (display.Width / 2); - this.Area.Y = display.Position.Y + virtualScreen.Position.Y + (display.Height / 2); - } - } - } - ); - } - } - } - - public class TabletAreaEditor : RotationAreaEditor - { - public TabletAreaEditor() - : base() - { - this.ToolTip = "You can right click the area editor to enable aspect ratio locking, adjust alignment, or resize the area."; - } - - private BooleanCommand lockArCmd, areaClippingCmd, ignoreOutsideAreaCmd; - private bool lockAspectRatio, areaClipping, ignoreOutsideArea; - - public event EventHandler LockAspectRatioChanged; - public event EventHandler AreaClippingChanged; - public event EventHandler IgnoreOutsideAreaChanged; - - protected virtual void OnLockAspectRatioChanged() => LockAspectRatioChanged?.Invoke(this, new EventArgs()); - protected virtual void OnAreaClippingChanged() => AreaClippingChanged?.Invoke(this, new EventArgs()); - protected virtual void OnIgnoreOutsideAreaChanged() => IgnoreOutsideAreaChanged?.Invoke(this, new EventArgs()); - - public bool LockAspectRatio - { - set - { - this.lockAspectRatio = value; - this.OnLockAspectRatioChanged(); - } - get => this.lockAspectRatio; - } - - public bool AreaClipping - { - set - { - this.areaClipping = value; - this.OnAreaClippingChanged(); - } - get => this.areaClipping; - } - - public bool IgnoreOutsideArea - { - set - { - this.ignoreOutsideArea = value; - this.OnIgnoreOutsideAreaChanged(); - } - get => this.ignoreOutsideArea; - } - - public BindableBinding LockAspectRatioBinding - { - get - { - return new BindableBinding( - this, - c => c.LockAspectRatio, - (c, v) => c.LockAspectRatio = v, - (c, h) => c.LockAspectRatioChanged += h, - (c, h) => c.LockAspectRatioChanged -= h - ); - } - } - - public BindableBinding AreaClippingBinding - { - get - { - return new BindableBinding( - this, - c => c.AreaClipping, - (c, v) => c.AreaClipping = v, - (c, h) => c.AreaClippingChanged += h, - (c, h) => c.AreaClippingChanged -= h - ); - } - } - - public BindableBinding IgnoreOutsideAreaBinding - { - get - { - return new BindableBinding( - this, - c => c.IgnoreOutsideArea, - (c, v) => c.IgnoreOutsideArea = v, - (c, h) => c.IgnoreOutsideAreaChanged += h, - (c, h) => c.IgnoreOutsideAreaChanged -= h - ); - } - } - - protected override void CreateMenu() - { - base.CreateMenu(); - - base.ContextMenu.Items.AddSeparator(); - - lockArCmd = new BooleanCommand - { - MenuText = "Lock aspect ratio" - }; - - areaClippingCmd = new BooleanCommand - { - MenuText = "Clamp input outside area" - }; - - ignoreOutsideAreaCmd = new BooleanCommand - { - MenuText = "Ignore input outside area" - }; - - base.ContextMenu.Items.AddRange( - new Command[] - { - lockArCmd, - areaClippingCmd, - ignoreOutsideAreaCmd - } - ); - - base.ContextMenu.Items.AddSeparator(); - - base.ContextMenu.Items.Add( - new ActionCommand - { - MenuText = "Convert area...", - Action = async () => await ConvertAreaDialog() - } - ); - - lockArCmd.CheckedBinding.Cast().Bind(LockAspectRatioBinding); - areaClippingCmd.CheckedBinding.Cast().Bind(AreaClippingBinding); - ignoreOutsideAreaCmd.CheckedBinding.Cast().Bind(IgnoreOutsideAreaBinding); - } - - private async Task ConvertAreaDialog() - { - var converter = new AreaConverterDialog - { - DataContext = base.Area - }; - await converter.ShowModalAsync(base.ParentWindow); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/Area/AreaControl.cs b/OpenTabletDriver.UX/Controls/Output/Area/AreaControl.cs deleted file mode 100644 index 3b729506e..000000000 --- a/OpenTabletDriver.UX/Controls/Output/Area/AreaControl.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections.Generic; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; - -namespace OpenTabletDriver.UX.Controls.Output.Area -{ - public abstract class AreaControl : Panel - { - private AreaSettings area; - private bool lockToUsableArea; - private string unit, invalidForegroundError, invalidBackgroundError; - protected IEnumerable areaBounds; - private RectangleF fullAreaBounds; - - public event EventHandler AreaChanged; - public event EventHandler LockToUsableAreaChanged; - public event EventHandler UnitChanged; - public event EventHandler AreaBoundsChanged; - public event EventHandler FullAreaBoundsChanged; - public event EventHandler InvalidForegroundErrorChanged; - public event EventHandler InvalidBackgroundErrorChanged; - - protected virtual void OnAreaChanged() => AreaChanged?.Invoke(this, new EventArgs()); - protected virtual void OnLockToUsableAreaChanged() => LockToUsableAreaChanged?.Invoke(this, new EventArgs()); - protected virtual void OnUnitChanged() => UnitChanged?.Invoke(this, new EventArgs()); - protected virtual void OnAreaBoundsChanged() => AreaBoundsChanged?.Invoke(this, new EventArgs()); - protected virtual void OnFullAreaBoundsChanged() => FullAreaBoundsChanged?.Invoke(this, new EventArgs()); - protected virtual void OnInvalidForegroundErrorChanged() => InvalidForegroundErrorChanged?.Invoke(this, new EventArgs()); - protected virtual void OnInvalidBackgroundErrorChanged() => InvalidBackgroundErrorChanged?.Invoke(this, new EventArgs()); - - public AreaSettings Area - { - set - { - this.area = value; - this.OnAreaChanged(); - } - get => this.area; - } - - public bool LockToUsableArea - { - set - { - this.lockToUsableArea = value; - this.OnLockToUsableAreaChanged(); - } - get => this.lockToUsableArea; - } - - public string Unit - { - set - { - this.unit = value; - this.OnUnitChanged(); - } - get => this.unit; - } - - public virtual IEnumerable AreaBounds - { - set - { - this.areaBounds = value; - this.OnAreaBoundsChanged(); - } - get => this.areaBounds; - } - - public RectangleF FullAreaBounds - { - protected set - { - this.fullAreaBounds = value; - this.OnFullAreaBoundsChanged(); - } - get => this.fullAreaBounds; - } - - public string InvalidForegroundError - { - set - { - this.invalidForegroundError = value; - this.OnInvalidForegroundErrorChanged(); - } - get => this.invalidForegroundError; - } - - public string InvalidBackgroundError - { - set - { - this.invalidBackgroundError = value; - this.OnInvalidBackgroundErrorChanged(); - } - get => this.invalidBackgroundError; - } - - public BindableBinding AreaBinding - { - get - { - return new BindableBinding( - this, - c => c.Area, - (c, v) => c.Area = v, - (c, h) => c.AreaChanged += h, - (c, h) => c.AreaChanged -= h - ); - } - } - - public BindableBinding LockToUsableAreaBinding - { - get - { - return new BindableBinding( - this, - c => c.LockToUsableArea, - (c, v) => c.LockToUsableArea = v, - (c, h) => c.LockToUsableAreaChanged += h, - (c, h) => c.LockToUsableAreaChanged -= h - ); - } - } - - public BindableBinding UnitBinding - { - get - { - return new BindableBinding( - this, - c => c.Unit, - (c, v) => c.Unit = v, - (c, h) => c.UnitChanged += h, - (c, h) => c.UnitChanged -= h - ); - } - } - - public BindableBinding> AreaBoundsBinding - { - get - { - return new BindableBinding>( - this, - c => c.AreaBounds, - (c, v) => c.AreaBounds = v, - (c, h) => c.AreaBoundsChanged += h, - (c, h) => c.AreaBoundsChanged -= h - ); - } - } - - public BindableBinding FullAreaBoundsBinding - { - get - { - return new BindableBinding( - this, - c => c.FullAreaBounds, - (c, v) => c.FullAreaBounds = v, - (c, h) => c.FullAreaBoundsChanged += h, - (c, h) => c.FullAreaBoundsChanged -= h - ); - } - } - - public BindableBinding InvalidForegroundErrorBinding - { - get - { - return new BindableBinding( - this, - c => c.InvalidForegroundError, - (c, v) => c.InvalidForegroundError = v, - (c, h) => c.InvalidForegroundErrorChanged += h, - (c, h) => c.InvalidForegroundErrorChanged -= h - ); - } - } - - public BindableBinding InvalidBackgroundErrorBinding - { - get - { - return new BindableBinding( - this, - c => c.InvalidBackgroundError, - (c, v) => c.InvalidBackgroundError = v, - (c, h) => c.InvalidBackgroundErrorChanged += h, - (c, h) => c.InvalidBackgroundErrorChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/Area/AreaDisplay.cs b/OpenTabletDriver.UX/Controls/Output/Area/AreaDisplay.cs deleted file mode 100644 index 1db80946b..000000000 --- a/OpenTabletDriver.UX/Controls/Output/Area/AreaDisplay.cs +++ /dev/null @@ -1,441 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Output.Area -{ - public class AreaDisplay : ScheduledDrawable - { - /// - /// Workaround for memory leaks on macos. - /// Use shared FormattedText to draw text. - /// - private class TextDrawer - { - private readonly FormattedText sharedFormattedText = new(); - - public void DrawText(Graphics graphics, Font font, Brush brush, PointF location, String text) - { - sharedFormattedText.Text = text; - sharedFormattedText.Font = font; - sharedFormattedText.ForegroundBrush = brush; - graphics.DrawText(sharedFormattedText, location); - } - } - - private AreaSettings area; - private bool lockToUsableArea; - private string unit, invalidForegroundError, invalidBackgroundError; - protected IEnumerable areaBounds; - private RectangleF fullAreaBounds; - private readonly TextDrawer textDrawer = new(); - - public event EventHandler AreaChanged; - public event EventHandler LockToUsableAreaChanged; - public event EventHandler UnitChanged; - public event EventHandler AreaBoundsChanged; - public event EventHandler FullAreaBoundsChanged; - public event EventHandler InvalidForegroundErrorChanged; - public event EventHandler InvalidBackgroundErrorChanged; - - protected virtual void OnAreaChanged() => AreaChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnLockToUsableAreaChanged() => LockToUsableAreaChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnUnitChanged() => UnitChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnAreaBoundsChanged() => AreaBoundsChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnFullAreaBoundsChanged() => FullAreaBoundsChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnInvalidForegroundErrorChanged() => InvalidForegroundErrorChanged?.Invoke(this, EventArgs.Empty); - protected virtual void OnInvalidBackgroundErrorChanged() => InvalidBackgroundErrorChanged?.Invoke(this, EventArgs.Empty); - - public AreaSettings Area - { - set - { - this.area = value; - this.OnAreaChanged(); - } - get => this.area; - } - - public bool LockToUsableArea - { - set - { - this.lockToUsableArea = value; - this.OnLockToUsableAreaChanged(); - } - get => this.lockToUsableArea; - } - - public string Unit - { - set - { - this.unit = value; - this.OnUnitChanged(); - } - get => this.unit; - } - - public virtual IEnumerable AreaBounds - { - set - { - this.areaBounds = value; - this.OnAreaBoundsChanged(); - } - get => this.areaBounds; - } - - public RectangleF FullAreaBounds - { - protected set - { - this.fullAreaBounds = value; - this.OnFullAreaBoundsChanged(); - } - get => this.fullAreaBounds; - } - - public string InvalidForegroundError - { - set - { - this.invalidForegroundError = value; - this.OnInvalidForegroundErrorChanged(); - } - get => this.invalidForegroundError; - } - - public string InvalidBackgroundError - { - set - { - this.invalidBackgroundError = value; - this.OnInvalidBackgroundErrorChanged(); - } - get => this.invalidBackgroundError; - } - - public BindableBinding AreaBinding - { - get - { - return new BindableBinding( - this, - c => c.Area, - (c, v) => c.Area = v, - (c, h) => c.AreaChanged += h, - (c, h) => c.AreaChanged -= h - ); - } - } - - public BindableBinding LockToUsableAreaBinding - { - get - { - return new BindableBinding( - this, - c => c.LockToUsableArea, - (c, v) => c.LockToUsableArea = v, - (c, h) => c.LockToUsableAreaChanged += h, - (c, h) => c.LockToUsableAreaChanged -= h - ); - } - } - - public BindableBinding UnitBinding - { - get - { - return new BindableBinding( - this, - c => c.Unit, - (c, v) => c.Unit = v, - (c, h) => c.UnitChanged += h, - (c, h) => c.UnitChanged -= h - ); - } - } - - public BindableBinding> AreaBoundsBinding - { - get - { - return new BindableBinding>( - this, - c => c.AreaBounds, - (c, v) => c.AreaBounds = v, - (c, h) => c.AreaBoundsChanged += h, - (c, h) => c.AreaBoundsChanged -= h - ); - } - } - - public BindableBinding FullAreaBoundsBinding - { - get - { - return new BindableBinding( - this, - c => c.FullAreaBounds, - (c, v) => c.FullAreaBounds = v, - (c, h) => c.FullAreaBoundsChanged += h, - (c, h) => c.FullAreaBoundsChanged -= h - ); - } - } - - public BindableBinding InvalidForegroundErrorBinding - { - get - { - return new BindableBinding( - this, - c => c.InvalidForegroundError, - (c, v) => c.InvalidForegroundError = v, - (c, h) => c.InvalidForegroundErrorChanged += h, - (c, h) => c.InvalidForegroundErrorChanged -= h - ); - } - } - - public BindableBinding InvalidBackgroundErrorBinding - { - get - { - return new BindableBinding( - this, - c => c.InvalidBackgroundError, - (c, v) => c.InvalidBackgroundError = v, - (c, h) => c.InvalidBackgroundErrorChanged += h, - (c, h) => c.InvalidBackgroundErrorChanged -= h - ); - } - } - - private static readonly Font Font = SystemFonts.User(8); - private static readonly Brush TextBrush = new SolidBrush(SystemColors.ControlText); - - private readonly Color AccentColor = new Color(SystemColors.Highlight, 0.5f); - private readonly Color AreaBoundsFillColor = SystemColors.ControlBackground; - private readonly Color AreaBoundsBorderColor = SystemInterop.CurrentPlatform switch - { - PluginPlatform.Windows => new Color(64, 64, 64), - _ => SystemColors.Control - }; - - private bool mouseDragging; - private PointF? mouseOffset; - private PointF? viewModelOffset; - - private RectangleF ForegroundRect => Area == null ? RectangleF.Empty : RectangleF.FromCenter( - new PointF(Area.X, Area.Y), - new SizeF(Area.Width, Area.Height) - ); - - public float PixelScale => CalculateScale(FullAreaBounds); - - protected override void OnMouseDown(MouseEventArgs e) - { - base.OnMouseDown(e); - - switch (e.Buttons) - { - case MouseButtons.Primary: - mouseDragging = true; - break; - default: - mouseDragging = false; - break; - } - } - - protected override void OnMouseUp(MouseEventArgs e) - { - base.OnMouseUp(e); - - switch (e.Buttons) - { - case MouseButtons.Primary: - { - mouseDragging = false; - break; - } - } - } - - protected override void OnMouseMove(MouseEventArgs e) - { - base.OnMouseMove(e); - - if (mouseDragging) - { - if (mouseOffset != null) - { - var delta = e.Location - mouseOffset.Value; - var newX = viewModelOffset.Value.X + (delta.X / PixelScale); - var newY = viewModelOffset.Value.Y + (delta.Y / PixelScale); - - Area.X = newX; - Area.Y = newY; - OnAreaChanged(); - } - else - { - mouseOffset = e.Location; - viewModelOffset = new PointF(Area.X, Area.Y); - } - } - else if (!mouseDragging && mouseOffset != null) - { - mouseOffset = null; - viewModelOffset = null; - } - } - - protected override void OnNextFrame(PaintEventArgs e) - { - var graphics = e.Graphics; - - switch (IsValid(ForegroundRect), IsValid(FullAreaBounds)) - { - case (true, true): - { - using (graphics.SaveTransformState()) - { - var scale = CalculateScale(FullAreaBounds); - - var clientCenter = new PointF(this.ClientSize.Width, this.ClientSize.Height) / 2; - var backgroundCenter = new PointF(FullAreaBounds.Width, FullAreaBounds.Height) / 2 * scale; - var offset = clientCenter - backgroundCenter; - - graphics.TranslateTransform(offset); - - DrawBackground(graphics, scale); - DrawForeground(graphics, scale); - } - break; - } - case (_, false): - { - DrawText(graphics, InvalidBackgroundError); - break; - } - case (false, _): - { - DrawText(graphics, InvalidForegroundError); - break; - } - } - } - - private void DrawBackground(Graphics graphics, float scale) - { - using (graphics.SaveTransformState()) - { - graphics.TranslateTransform(-FullAreaBounds.TopLeft * scale); - foreach (var rect in AreaBounds) - { - var scaledRect = rect * scale; - graphics.FillRectangle(AreaBoundsFillColor, scaledRect); - graphics.DrawRectangle(AreaBoundsBorderColor, scaledRect); - } - } - } - - private void DrawForeground(Graphics graphics, float scale) - { - using (graphics.SaveTransformState()) - { - var area = ForegroundRect * scale; - - graphics.TranslateTransform(area.Center); - graphics.RotateTransform(Area.Rotation); - graphics.TranslateTransform(-area.Center); - - graphics.FillRectangle(AccentColor, area); - graphics.DrawRectangle(SystemColors.ControlText, area); - - var originEllipse = new RectangleF(0, 0, 1, 1); - originEllipse.Offset(area.Center - (originEllipse.Size / 2)); - graphics.DrawEllipse(SystemColors.ControlText, originEllipse); - - DrawRatioText(graphics, area); - DrawWidthText(graphics, area); - DrawHeightText(graphics, area); - } - } - - private void DrawRatioText(Graphics graphics, RectangleF area) - { - string ratio = Math.Round(Area.Width / Area.Height, 4).ToString(); - SizeF ratioMeasure = graphics.MeasureString(Font, ratio); - var offsetY = area.Center.Y + (ratioMeasure.Height / 2); - if (offsetY + ratioMeasure.Height > area.Y + area.Height) - offsetY = area.Y + area.Height; - - var ratioPos = new PointF( - area.Center.X - (ratioMeasure.Width / 2), - offsetY - ); - textDrawer.DrawText(graphics, Font, TextBrush, ratioPos, ratio); - } - - private void DrawWidthText(Graphics graphics, RectangleF area) - { - var minDist = area.Center.Y - 40; - string widthText = $"{MathF.Round(Area.Width, 3)}{Unit}"; - var widthTextSize = graphics.MeasureString(Font, widthText); - var widthTextPos = new PointF( - area.MiddleTop.X - (widthTextSize.Width / 2), - Math.Min(area.MiddleTop.Y, minDist) - ); - textDrawer.DrawText(graphics, Font, TextBrush, widthTextPos, widthText); - } - - private void DrawHeightText(Graphics graphics, RectangleF area) - { - using (graphics.SaveTransformState()) - { - var minDist = area.Center.X - 40; - string heightText = $"{MathF.Round(Area.Height, 3)}{Unit}"; - var heightSize = graphics.MeasureString(Font, heightText) / 2; - var heightPos = new PointF( - -area.MiddleLeft.Y - heightSize.Width, - Math.Min(area.MiddleLeft.X, minDist) - ); - graphics.RotateTransform(-90); - textDrawer.DrawText(graphics, Font, TextBrush, heightPos, heightText); - } - } - - private void DrawText(Graphics graphics, string errorText) - { - var errSize = graphics.MeasureString(Font, errorText); - var errorOffset = new PointF(errSize.Width, errSize.Height) / 2; - var clientOffset = new PointF(this.ClientSize.Width, this.ClientSize.Height) / 2; - var offset = clientOffset - errorOffset; - - textDrawer.DrawText(graphics, Font, TextBrush, offset, errorText); - } - - private float CalculateScale(RectangleF rect) - { - float scaleX = (this.ClientSize.Width - 2) / rect.Width; - float scaleY = (this.ClientSize.Height - 2) / rect.Height; - return scaleX > scaleY ? scaleY : scaleX; - } - - private static bool IsValid(RectangleF rect) - { - return rect.Width > 0 && rect.Height > 0; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/Area/AreaEditor.cs b/OpenTabletDriver.UX/Controls/Output/Area/AreaEditor.cs deleted file mode 100644 index e9de4f8eb..000000000 --- a/OpenTabletDriver.UX/Controls/Output/Area/AreaEditor.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.UX.Controls.Generic.Text; -using OpenTabletDriver.UX.Controls.Utilities; - -namespace OpenTabletDriver.UX.Controls.Output.Area -{ - public class AreaEditor : AreaControl - { - public AreaEditor() - { - this.Content = new StackLayout - { - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - Control = new Panel - { - Padding = new Padding(5), - Content = Display = new AreaDisplay() - } - }, - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Center, - Control = settingsPanel = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Control = widthGroup = new UnitGroup - { - Text = "Width", - Unit = Unit, - ToolTip = $"Area width in {Unit}", - Orientation = Orientation.Horizontal, - Content = width = new FloatNumberBox() - } - }, - new StackLayoutItem - { - Control = heightGroup = new UnitGroup - { - Text = "Height", - Unit = Unit, - ToolTip = $"Area height in {Unit}", - Orientation = Orientation.Horizontal, - Content = height = new FloatNumberBox() - } - }, - new StackLayoutItem - { - Control = xGroup = new UnitGroup - { - Text = "X", - Unit = Unit, - ToolTip = $"Area center X offset in {Unit}", - Orientation = Orientation.Horizontal, - Content = x = new FloatNumberBox() - } - }, - new StackLayoutItem - { - Control = yGroup = new UnitGroup - { - Text = "Y", - Unit = Unit, - ToolTip = $"Area center Y offset in {Unit}", - Orientation = Orientation.Horizontal, - Content = y = new FloatNumberBox() - } - } - } - } - } - } - }; - - CreateMenu(); - - widthGroup.UnitBinding.Bind(UnitBinding); - heightGroup.UnitBinding.Bind(UnitBinding); - xGroup.UnitBinding.Bind(UnitBinding); - yGroup.UnitBinding.Bind(UnitBinding); - - var widthBinding = AreaBinding.Child((AreaSettings s) => s.Width); - var heightBinding = AreaBinding.Child((AreaSettings s) => s.Height); - var xBinding = AreaBinding.Child((AreaSettings s) => s.X); - var yBinding = AreaBinding.Child((AreaSettings s) => s.Y); - - width.ValueBinding.Bind(widthBinding); - height.ValueBinding.Bind(heightBinding); - x.ValueBinding.Bind(xBinding); - y.ValueBinding.Bind(yBinding); - - Display.AreaBinding.Bind(AreaBinding); - Display.LockToUsableAreaBinding.Bind(LockToUsableAreaBinding); - Display.UnitBinding.Bind(UnitBinding); - Display.AreaBoundsBinding.Bind(AreaBoundsBinding); - Display.FullAreaBoundsBinding.Bind(FullAreaBoundsBinding); - Display.InvalidForegroundErrorBinding.Bind(InvalidForegroundErrorBinding); - Display.InvalidBackgroundErrorBinding.Bind(InvalidBackgroundErrorBinding); - } - - private BooleanCommand lockToUsableArea = new BooleanCommand - { - MenuText = "Lock to usable area" - }; - - private UnitGroup widthGroup, heightGroup, xGroup, yGroup; - private MaskedTextBox width, height, x, y; - - protected StackLayout settingsPanel; - - public AreaDisplay Display { get; } - - public override IEnumerable AreaBounds - { - set - { - this.areaBounds = value; - this.OnAreaBoundsChanged(); - if (AreaBounds != null) - { - this.FullAreaBounds = new RectangleF - { - Left = this.AreaBounds.Min(r => r.Left), - Top = this.AreaBounds.Min(r => r.Top), - Right = this.AreaBounds.Max(r => r.Right), - Bottom = this.AreaBounds.Max(r => r.Bottom), - }; - } - else - { - this.FullAreaBounds = RectangleF.Empty; - } - } - get => this.areaBounds; - } - - public Vector2[] GetAreaCorners() - { - var origin = new Vector2(Area.X, Area.Y); - var matrix = Matrix3x2.CreateTranslation(-origin); - matrix *= Matrix3x2.CreateRotation((float)(Area.Rotation * Math.PI / 180)); - matrix *= Matrix3x2.CreateTranslation(origin); - - float halfWidth = Area.Width / 2; - float halfHeight = Area.Height / 2; - - return new Vector2[] - { - Vector2.Transform(new Vector2(Area.X - halfWidth, Area.Y - halfHeight), matrix), - Vector2.Transform(new Vector2(Area.X - halfWidth, Area.Y + halfHeight), matrix), - Vector2.Transform(new Vector2(Area.X + halfWidth, Area.Y + halfHeight), matrix), - Vector2.Transform(new Vector2(Area.X + halfWidth, Area.Y - halfHeight), matrix), - }; - } - - public Vector2 GetAreaCenterOffset() - { - var corners = GetAreaCorners(); - var min = new Vector2( - corners.Min(v => v.X), - corners.Min(v => v.Y) - ); - var max = new Vector2( - corners.Max(v => v.X), - corners.Max(v => v.Y) - ); - return (max - min) / 2; - } - - protected virtual void CreateMenu() - { - this.ContextMenu = new ContextMenu - { - Items = - { - new ButtonMenuItem - { - Text = "Align", - Items = - { - new ActionCommand - { - MenuText = "Left", - Action = () => Area.X = GetAreaCenterOffset().X - }, - new ActionCommand - { - MenuText = "Right", - Action = () => Area.X = FullAreaBounds.Width - GetAreaCenterOffset().X - }, - new ActionCommand - { - MenuText = "Top", - Action = () => Area.Y = GetAreaCenterOffset().Y - }, - new ActionCommand - { - MenuText = "Bottom", - Action = () => Area.Y = FullAreaBounds.Height - GetAreaCenterOffset().Y - }, - new ActionCommand - { - MenuText = "Center", - Action = () => - { - Area.X = FullAreaBounds.Center.X; - Area.Y = FullAreaBounds.Center.Y; - } - } - } - }, - new ButtonMenuItem - { - Text = "Resize", - Items = - { - new ActionCommand - { - MenuText = "Full area", - Action = () => - { - Area.Height = FullAreaBounds.Height; - Area.Width = FullAreaBounds.Width; - Area.Y = FullAreaBounds.Center.Y; - Area.X = FullAreaBounds.Center.X; - } - }, - new ActionCommand - { - MenuText = "Quarter area", - Action = () => - { - Area.Height = FullAreaBounds.Height / 2; - Area.Width = FullAreaBounds.Width / 2; - } - } - } - }, - new ButtonMenuItem - { - Text = "Flip", - Items = - { - new ActionCommand - { - MenuText = "Horizontal", - Action = () => Area.X = FullAreaBounds.Width - Area.X - }, - new ActionCommand - { - MenuText = "Vertical", - Action = () => Area.Y = FullAreaBounds.Height - Area.Y - } - } - }, - lockToUsableArea - } - }; - - lockToUsableArea.CheckedBinding.Cast().Bind(LockToUsableAreaBinding); - } - - protected override void OnMouseDown(MouseEventArgs e) - { - base.OnMouseDown(e); - - switch (e.Buttons) - { - case MouseButtons.Alternate: - { - this.ContextMenu.Show(this); - break; - } - } - } - - protected override void OnLockToUsableAreaChanged() - { - base.OnLockToUsableAreaChanged(); - - if (LockToUsableArea) - OnAreaChanged(); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/Area/RotationAreaEditor.cs b/OpenTabletDriver.UX/Controls/Output/Area/RotationAreaEditor.cs deleted file mode 100644 index 805c324fe..000000000 --- a/OpenTabletDriver.UX/Controls/Output/Area/RotationAreaEditor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic.Text; -using OpenTabletDriver.UX.Controls.Utilities; - -namespace OpenTabletDriver.UX.Controls.Output.Area -{ - public class RotationAreaEditor : AreaEditor - { - public RotationAreaEditor() - : base() - { - settingsPanel.Items.Add( - new StackLayoutItem - { - Control = new UnitGroup - { - Text = "Rotation", - Unit = "°", - ToolTip = "Angle of rotation about the center of the area.", - Orientation = Orientation.Horizontal, - Content = rotation = new FloatNumberBox() - } - } - ); - - var rotationBinding = AreaBinding.Child(c => c.Rotation); - rotation.ValueBinding.Bind(rotationBinding); - } - - private MaskedTextBox rotation; - - protected override void CreateMenu() - { - base.CreateMenu(); - - this.ContextMenu.Items.GetSubmenu("Flip").Items.Add( - new ActionCommand - { - MenuText = "Handedness", - Action = () => - { - Area.Rotation += 180; - Area.Rotation %= 360; - Area.X = FullAreaBounds.Width - Area.X; - Area.Y = FullAreaBounds.Height - Area.Y; - } - } - ); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/Area/UnitGroup.cs b/OpenTabletDriver.UX/Controls/Output/Area/UnitGroup.cs deleted file mode 100644 index 86b50c840..000000000 --- a/OpenTabletDriver.UX/Controls/Output/Area/UnitGroup.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Controls.Output.Area -{ - public class UnitGroup : Group - { - public UnitGroup() - { - unitLabel.TextBinding.Bind(UnitBinding); - } - - private string unit; - public string Unit - { - set - { - this.unit = value; - this.OnUnitChanged(); - } - get => this.unit; - } - - public event EventHandler UnitChanged; - - protected virtual void OnUnitChanged() => UnitChanged?.Invoke(this, new EventArgs()); - - public BindableBinding UnitBinding - { - get - { - return new BindableBinding( - this, - c => c.Unit, - (c, v) => c.Unit = v, - (c, h) => c.UnitChanged += h, - (c, h) => c.UnitChanged -= h - ); - } - } - - private Label unitLabel = new Label(); - - private Control content; - public new Control Content - { - set - { - this.content = value; - base.Content = new StackLayout - { - Spacing = 5, - Orientation = Orientation.Horizontal, - Items = - { - new StackLayoutItem(this.Content, true), - new StackLayoutItem - { - VerticalAlignment = VerticalAlignment.Center, - Control = this.unitLabel - } - } - }; - } - get => this.content; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/OutputModeEditor.cs b/OpenTabletDriver.UX/Controls/Output/OutputModeEditor.cs deleted file mode 100644 index 8bb622a3a..000000000 --- a/OpenTabletDriver.UX/Controls/Output/OutputModeEditor.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Platform.Display; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Reflection; - -namespace OpenTabletDriver.UX.Controls.Output -{ - public class OutputModeEditor : Panel - { - public OutputModeEditor() - { - this.Content = new StackLayout - { - Padding = 5, - Spacing = 5, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new StackLayoutItem(editorContainer, true), - new StackLayoutItem(outputModeSelector, HorizontalAlignment.Left) - } - }; - - absoluteModeEditor.SettingsBinding.Bind(ProfileBinding.Child(p => p.AbsoluteModeSettings)); - relativeModeEditor.SettingsBinding.Bind(ProfileBinding.Child(p => p.RelativeModeSettings)); - - outputModeSelector.SelectedItemBinding.Convert( - c => PluginSettingStore.FromPath(c?.FullName), - v => v?.GetTypeInfo() - ).Bind(ProfileBinding.Child(c => c.OutputMode)); - - outputModeSelector.SelectedValueChanged += (sender, e) => UpdateOutputMode(Profile?.OutputMode); - - App.Driver.TabletsChanged += (sender, e) => UpdateTablet(e); - UpdateTablet(); - } - - private void UpdateTablet(IEnumerable tablets = null) => Application.Instance.AsyncInvoke(async () => - { - tablets ??= await App.Driver.Instance.GetTablets(); - var selectedTablet = tablets.FirstOrDefault(t => t.Properties.Name == Profile?.Tablet); - if (selectedTablet != null) - SetTabletSize(selectedTablet); - }); - - private Profile profile; - public Profile Profile - { - set - { - this.profile = value; - this.OnProfileChanged(); - } - get => this.profile; - } - - public event EventHandler ProfileChanged; - - protected virtual void OnProfileChanged() - { - ProfileChanged?.Invoke(this, new EventArgs()); - UpdateTablet(); - } - - public BindableBinding ProfileBinding - { - get - { - return new BindableBinding( - this, - c => c.Profile, - (c, v) => c.Profile = v, - (c, h) => c.ProfileChanged += h, - (c, h) => c.ProfileChanged -= h - ); - } - } - - private Panel editorContainer = new Panel(); - private AbsoluteModeEditor absoluteModeEditor = new AbsoluteModeEditor(); - private RelativeModeEditor relativeModeEditor = new RelativeModeEditor(); - private TypeDropDown outputModeSelector = new TypeDropDown { Width = 300 }; - - public void SetTabletSize(TabletReference tablet) - { - var tabletAreaEditor = absoluteModeEditor.tabletAreaEditor; - if (tablet?.Properties?.Specifications?.Digitizer is DigitizerSpecifications digitizer) - { - tabletAreaEditor.AreaBounds = new RectangleF[] - { - new RectangleF(0, 0, digitizer.Width, digitizer.Height) - }; - } - else - { - tabletAreaEditor.AreaBounds = null; - } - } - - public void SetDisplaySize(IEnumerable displays) - { - var bgs = from disp in displays - where !(disp is IVirtualScreen) - select new RectangleF(disp.Position.X, disp.Position.Y, disp.Width, disp.Height); - absoluteModeEditor.displayAreaEditor.AreaBounds = bgs; - } - - private void UpdateOutputMode(PluginSettingStore store) - { - bool showAbsolute = false; - bool showRelative = false; - if (store != null) - { - var outputMode = store.GetTypeInfo(); - showAbsolute = outputMode.IsSubclassOf(typeof(AbsoluteOutputMode)); - showRelative = outputMode.IsSubclassOf(typeof(RelativeOutputMode)); - } - - if (showAbsolute) - editorContainer.Content = absoluteModeEditor; - else if (showRelative) - editorContainer.Content = relativeModeEditor; - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Output/RelativeModeEditor.cs b/OpenTabletDriver.UX/Controls/Output/RelativeModeEditor.cs deleted file mode 100644 index 64d0d1ca5..000000000 --- a/OpenTabletDriver.UX/Controls/Output/RelativeModeEditor.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; -using OpenTabletDriver.UX.Controls.Output.Area; - -namespace OpenTabletDriver.UX.Controls.Output -{ - public class RelativeModeEditor : Panel - { - public RelativeModeEditor() - { - this.Content = new Group - { - Text = "Relative", - Content = new StackLayout - { - Orientation = Orientation.Horizontal, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - VerticalContentAlignment = VerticalAlignment.Top, - Spacing = 5, - Items = - { - new StackLayoutItem(null, true), - new UnitGroup - { - Text = "X Sensitivity", - Orientation = Orientation.Horizontal, - Unit = "px/mm", - Content = xSens = new FloatNumberBox() - }, - new UnitGroup - { - Text = "Y Sensitivity", - Orientation = Orientation.Horizontal, - Unit = "px/mm", - Content = ySens = new FloatNumberBox() - }, - new UnitGroup - { - Text = "Rotation", - Orientation = Orientation.Horizontal, - Unit = "°", - Content = rotation = new FloatNumberBox() - }, - new UnitGroup - { - Text = "Reset Time", - Orientation = Orientation.Horizontal, - Unit = "ms", - Content = resetTime = new FloatNumberBox() - }, - new StackLayoutItem(null, true) - } - } - }; - - xSens.ValueBinding.Bind(SettingsBinding.Child(s => s.XSensitivity)); - ySens.ValueBinding.Bind(SettingsBinding.Child(s => s.YSensitivity)); - rotation.ValueBinding.Bind(SettingsBinding.Child(s => s.RelativeRotation)); - resetTime.ValueBinding.Convert( - c => TimeSpan.FromMilliseconds(c), - v => (float)v.TotalMilliseconds - ).Bind(SettingsBinding.Child(s => s.ResetTime)); - } - - private MaskedTextBox xSens, ySens, rotation, resetTime; - - private RelativeModeSettings settings; - public RelativeModeSettings Settings - { - set - { - this.settings = value; - this.OnSettingsChanged(); - } - get => this.settings; - } - - public event EventHandler SettingsChanged; - - protected virtual void OnSettingsChanged() => SettingsChanged?.Invoke(this, new EventArgs()); - - public BindableBinding SettingsBinding - { - get - { - return new BindableBinding( - this, - c => c.Settings, - (c, v) => c.Settings = v, - (c, h) => c.SettingsChanged += h, - (c, h) => c.SettingsChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/OutputModePicker.cs b/OpenTabletDriver.UX/Controls/OutputModePicker.cs new file mode 100644 index 000000000..640f016e7 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/OutputModePicker.cs @@ -0,0 +1,32 @@ +using System.Collections.Immutable; +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Output; + +namespace OpenTabletDriver.UX.Controls +{ + public class OutputModePicker : DropDown + { + private readonly IPluginFactory _pluginFactory; + + public OutputModePicker(App app, IPluginFactory pluginFactory) + { + _pluginFactory = pluginFactory; + + ItemTextBinding = Binding.Property(t => t.GetFriendlyName() ?? t.GetFullyQualifiedName()); + Refresh(); + + app.PluginManager.AssembliesChanged += (_, _) => Refresh(); + } + + protected override IEnumerable CreateDefaultDataStore() + { + return _pluginFactory.GetMatchingTypes(typeof(IOutputMode)).ToImmutableArray(); + } + + private void Refresh() + { + DataStore = CreateDefaultDataStore(); + } + } +} diff --git a/OpenTabletDriver.UX/Controls/PenPanel.cs b/OpenTabletDriver.UX/Controls/PenPanel.cs new file mode 100644 index 000000000..57cc63b0f --- /dev/null +++ b/OpenTabletDriver.UX/Controls/PenPanel.cs @@ -0,0 +1,95 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class PenPanel : BindingPanel + { + public PenPanel(IControlBuilder controlBuilder, App app) : base(controlBuilder) + { + var tipSettings = new GroupBox + { + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = new Padding(5, 5, 5, 0), + Spacing = 5, + Items = + { + ButtonFor(p => p.BindingSettings.TipButton), + SliderFor(p => p.BindingSettings.TipActivationThreshold) + } + } + }; + + var eraserSettings = new GroupBox + { + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = new Padding(5, 5, 5, 0), + Spacing = 5, + Items = + { + ButtonFor(p => p.BindingSettings.EraserButton), + SliderFor(p => p.BindingSettings.EraserActivationThreshold) + } + } + }; + + var tips = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem(tipSettings, true), + new StackLayoutItem(eraserSettings, true) + } + }; + + var buttons = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5 + }; + + DataContextChanged += delegate + { + buttons.Items.Clear(); + + if (DataContext is not Profile profile) + return; + + var tablet = app.Tablets.First(t => t.Name == profile.Tablet); + var penButtonCount = tablet.Specifications.Pen?.ButtonCount ?? 0; + + foreach (var button in ButtonsFor(c => c.BindingSettings.PenButtons, penButtonCount)) + buttons.Items.Add(button); + }; + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + tips, + new StackLayoutItem + { + Expand = true, + Control = new Scrollable + { + Padding = 5, + Content = buttons + } + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Placeholder.cs b/OpenTabletDriver.UX/Controls/Placeholder.cs index 4c265423e..d9a6beb48 100644 --- a/OpenTabletDriver.UX/Controls/Placeholder.cs +++ b/OpenTabletDriver.UX/Controls/Placeholder.cs @@ -1,28 +1,34 @@ -using System; using Eto.Drawing; using Eto.Forms; +using OpenTabletDriver.UX.Components; namespace OpenTabletDriver.UX.Controls { - public class Placeholder : Panel + public sealed class Placeholder : DesktopPanel { + private readonly Panel _extraPanel; + private string? _text; + private Control? _extraContent; + + // TODO: Clean up Placeholder before using extensively, lots of legacy code smell public Placeholder() { - this.Content = new StackLayout + Label label; + Content = new StackLayout { Spacing = 5, HorizontalContentAlignment = HorizontalAlignment.Center, Items = { new StackLayoutItem(null, true), - new Bitmap(App.Logo.WithSize(256, 256)), + new Bitmap(Metadata.Logo.WithSize(256, 256)), new StackLayoutItem { Control = label = new Label() }, new StackLayoutItem { - Control = extraPanel = new Panel() + Control = _extraPanel = new Panel() }, new StackLayoutItem(null, true) } @@ -31,29 +37,25 @@ public Placeholder() label.TextBinding.Bind(TextBinding); } - private Label label; - private Panel extraPanel; - - private string text; - public string Text + public string? Text { set { - this.text = value; - this.OnTextChanged(); + _text = value; + OnTextChanged(); } - get => this.text; + get => _text; } - public event EventHandler TextChanged; + public event EventHandler? TextChanged; - protected virtual void OnTextChanged() => TextChanged?.Invoke(this, new EventArgs()); + private void OnTextChanged() => TextChanged?.Invoke(this, EventArgs.Empty); - public BindableBinding TextBinding + public BindableBinding TextBinding { get { - return new BindableBinding( + return new BindableBinding( this, c => c.Text, (c, v) => c.Text = v, @@ -63,15 +65,14 @@ public BindableBinding TextBinding } } - private Control extraContent; - public Control ExtraContent + public Control? ExtraContent { set { - this.extraContent = value; - extraPanel.Content = value; + _extraContent = value; + _extraPanel.Content = value; } - get => this.extraContent; + get => _extraContent; } } } diff --git a/OpenTabletDriver.UX/Controls/PluginSettingStoreCollectionEditor.cs b/OpenTabletDriver.UX/Controls/PluginSettingStoreCollectionEditor.cs deleted file mode 100644 index 018c1a622..000000000 --- a/OpenTabletDriver.UX/Controls/PluginSettingStoreCollectionEditor.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls.Generic.Reflection; - -namespace OpenTabletDriver.UX.Controls -{ - public class PluginSettingStoreCollectionEditor : Panel where TSource : class - { - public PluginSettingStoreCollectionEditor() - { - this.Content = placeholder = new Placeholder - { - Text = "No plugins containing this type are installed.", - ExtraContent = new Button - { - Text = "Open Plugin Manager", - Command = new Command((s, e) => App.Current.PluginManagerWindow.Show()) - } - }; - - mainContent = new Splitter - { - Panel1MinimumSize = 150, - Panel1 = new Scrollable - { - Border = BorderType.None, - Content = sourceSelector = new TypeListBox() - }, - Panel2 = new Scrollable - { - Content = settingStoreEditor = new ToggleablePluginSettingStoreEditor() - { - Padding = 5 - } - } - }; - - settingStoreEditor.StoreBinding.Bind( - sourceSelector.SelectedItemBinding.Convert(t => StoreCollection?.FromType(t)) - ); - - if (!Platform.IsMac) // Don't do this on macOS, causes poor UI performance. - settingStoreEditor.BackgroundColor = SystemColors.WindowBackground; - - AppInfo.PluginManager.AssembliesChanged += HandleAssembliesChanged; - } - - private Placeholder placeholder; - private Splitter mainContent; - private TypeListBox sourceSelector; - private ToggleablePluginSettingStoreEditor settingStoreEditor; - - private PluginSettingStoreCollection storeCollection; - public PluginSettingStoreCollection StoreCollection - { - set - { - this.storeCollection = value; - this.OnStoreCollectionChanged(); - } - get => this.storeCollection; - } - - public event EventHandler StoreCollectionChanged; - - protected virtual void OnStoreCollectionChanged() - { - StoreCollectionChanged?.Invoke(this, new EventArgs()); - RefreshContent(); - } - - private void HandleAssembliesChanged(object sender, EventArgs e) => Application.Instance.AsyncInvoke(RefreshContent); - - private void RefreshContent() - { - var types = AppInfo.PluginManager.GetChildTypes(); - - // Update DataStore to new types, this refreshes the editor. - var prevIndex = sourceSelector.SelectedIndex; - sourceSelector.SelectedIndex = -1; - sourceSelector.DataStore = types; - sourceSelector.SelectedIndex = prevIndex; - - this.Content = types.Any() ? mainContent : placeholder; - } - - public BindableBinding, PluginSettingStoreCollection> StoreCollectionBinding - { - get - { - return new BindableBinding, PluginSettingStoreCollection>( - this, - c => c.StoreCollection, - (c, v) => c.StoreCollection = v, - (c, h) => c.StoreCollectionChanged += h, - (c, h) => c.StoreCollectionChanged -= h - ); - } - } - - private class ToggleablePluginSettingStoreEditor : PluginSettingStoreEditor - { - protected override IEnumerable GetHeaderControlsForStore(PluginSettingStore store) - { - var enableButton = new CheckBox - { - Text = $"Enable {store.Name ?? store.Path}", - Checked = store.Enable - }; - enableButton.CheckedChanged += (sender, e) => store.Enable = enableButton.Checked ?? false; - yield return enableButton; - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/PluginSettingStoreEditor.cs b/OpenTabletDriver.UX/Controls/PluginSettingStoreEditor.cs deleted file mode 100644 index 0973c801f..000000000 --- a/OpenTabletDriver.UX/Controls/PluginSettingStoreEditor.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Attributes; - -namespace OpenTabletDriver.UX.Controls -{ - public class PluginSettingStoreEditor : Panel - { - public PluginSettingStoreEditor() - { - this.Content = layout = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5 - }; - } - - private StackLayout layout; - - private PluginSettingStore store; - public PluginSettingStore Store - { - set - { - this.store = value; - this.OnStoreChanged(); - } - get => this.store; - } - - public event EventHandler StoreChanged; - - protected virtual void OnStoreChanged() - { - StoreChanged?.Invoke(this, new EventArgs()); - - layout.Items.Clear(); - if (Store != null) - { - foreach (var control in GetHeaderControlsForStore(Store).Concat(GetControlsForStore(Store))) - { - layout.Items.Add(control); - } - } - } - - public BindableBinding, PluginSettingStore> StoreBinding - { - get - { - return new BindableBinding, PluginSettingStore>( - this, - c => c.Store, - (c, v) => c.Store = v, - (c, h) => c.StoreChanged += h, - (c, h) => c.StoreChanged -= h - ); - } - } - - protected virtual IEnumerable GetHeaderControlsForStore(PluginSettingStore store) - { - return Array.Empty(); - } - - private IEnumerable GetControlsForStore(PluginSettingStore store) - { - if (store != null) - { - var type = store.GetTypeInfo(); - return GetControlsForType(store, type); - } - else - { - return Array.Empty(); - } - } - - private IEnumerable GetControlsForType(PluginSettingStore store, Type type) - { - var properties = from property in type.GetProperties() - let attrs = property.GetCustomAttributes(true) - where attrs.Any(a => a is PropertyAttribute) - select property; - - foreach (var property in properties) - yield return GeneratedControls.GetControlForProperty(store, property); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/ProfilePanel.cs b/OpenTabletDriver.UX/Controls/ProfilePanel.cs new file mode 100644 index 000000000..4d12ab9e5 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/ProfilePanel.cs @@ -0,0 +1,101 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.Profiles; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class ProfilePanel : DesktopPanel + { + private readonly IControlBuilder _controlBuilder; + + public ProfilePanel(IDriverDaemon daemon, IControlBuilder controlBuilder, App app) + { + _controlBuilder = controlBuilder; + + var outputPage = CreatePage("Digitizer"); + var filtersPage = CreatePage("Filters"); + var penPage = CreatePage("Pen"); + var auxPage = CreatePage("Auxiliary"); + var mousePage = CreatePage("Mouse"); + var toolsPage = CreatePage("Tools"); + var logPage = CreatePage("Log"); + var placeholderPage = new TabPage + { + Text = "Info", + Padding = 5, + Content = new Placeholder + { + Text = "No tablet is selected. Make sure your tablet is connected." + } + }; + + var tabControl = new TabControl + { + Pages = + { + logPage + } + }; + + daemon.Message += (_, m) => + { + if (m.Level > LogLevel.Info) + tabControl.SelectedPage = logPage; + }; + + Content = new Panel + { + Padding = new Padding(5, 5, 5, 0), + Content = tabControl + }; + + DataContextChanged += delegate + { + var pages = tabControl.Pages; + pages.Clear(); + + if (DataContext is Profile profile && app.Tablets.Any()) + { + var tablet = app.Tablets.First(t => t.Name == profile.Tablet); + var specifications = tablet.Specifications; + + if (specifications.Digitizer != null) + { + pages.Add(outputPage); + pages.Add(filtersPage); + } + + if (specifications.Pen != null) + pages.Add(penPage); + + if (specifications.AuxiliaryButtons != null) + pages.Add(auxPage); + + if (specifications.MouseButtons != null) + pages.Add(mousePage); + + pages.Add(toolsPage); + } + else + { + pages.Add(placeholderPage); + tabControl.SelectedPage = placeholderPage; + } + + pages.Add(logPage); + }; + } + + private TabPage CreatePage(string text) where TControl : Control + { + return new TabPage + { + Text = text, + Padding = 5, + Content = _controlBuilder.Build() + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/ProfilePicker.cs b/OpenTabletDriver.UX/Controls/ProfilePicker.cs new file mode 100644 index 000000000..d6cf6892b --- /dev/null +++ b/OpenTabletDriver.UX/Controls/ProfilePicker.cs @@ -0,0 +1,48 @@ +using System.Collections.Immutable; +using Eto.Forms; +using OpenTabletDriver.Desktop.Profiles; + +namespace OpenTabletDriver.UX.Controls +{ + public class ProfilePicker : DropDown + { + private readonly App _app; + + public ProfilePicker(App app) + { + _app = app; + + ItemTextBinding = Binding.Property(profile => profile.Tablet); + UpdateItems(); + + app.PropertyChanged += (_, args) => + { + switch (args.PropertyName) + { + case nameof(App.Tablets): + case nameof(App.Settings): + UpdateItems(); + break; + } + }; + } + + private void UpdateItems() + { + var profiles = _app.Settings.Profiles; + var tablets = _app.Tablets.Select(t => t.Name); + + var visibleProfiles = profiles.Where(p => tablets.Contains(p.Tablet)).ToImmutableArray(); + + Application.Instance.AsyncInvoke(() => + { + DataStore = visibleProfiles; + + if (SelectedIndex == -1 && visibleProfiles.Any()) + SelectedIndex = 0; + + OnSelectedValueChanged(EventArgs.Empty); + }); + } + } +} diff --git a/OpenTabletDriver.UX/Controls/SettingsPanel.cs b/OpenTabletDriver.UX/Controls/SettingsPanel.cs new file mode 100644 index 000000000..d36261c01 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/SettingsPanel.cs @@ -0,0 +1,65 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Controls +{ + public class SettingsPanel : DesktopPanel + { + public SettingsPanel(App app, IControlBuilder controlBuilder) + { + var profilePicker = controlBuilder.Build(); + + var toolbar = new StackLayout + { + Padding = 5, + Spacing = 5, + Orientation = Orientation.Horizontal, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + new Panel + { + MinimumSize = new Size(250, 0), + Content = profilePicker + }, + new StackLayoutItem(null, true), + new Button((_, _) => app.DiscardSettings().Run()) + { + Text = "Discard" + }, + new Button((_, _) => app.SaveSettings().Run()) + { + Text = "Save" + }, + new Button((_, _) => app.ApplySettings().Run()) + { + Text = "Apply" + } + } + }; + + // TODO: Fix profile switching retaining old settings while updating the profile (such as lock aspect ratio) + var profilePanel = controlBuilder.Build(); + profilePanel.DataContextBinding.Bind(profilePicker.SelectedValueBinding); + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + new StackLayoutItem + { + Expand = true, + Control = profilePanel + }, + new StackLayoutItem + { + Control = toolbar + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/TabletSwitcherPanel.cs b/OpenTabletDriver.UX/Controls/TabletSwitcherPanel.cs deleted file mode 100644 index 41e3f54ef..000000000 --- a/OpenTabletDriver.UX/Controls/TabletSwitcherPanel.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Collections.ObjectModel; -using System.Linq; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.UX.Controls -{ - public class TabletSwitcherPanel : Panel - { - public TabletSwitcherPanel() - { - base.Content = layout = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = controlPanel = new ControlPanel() - }, - new StackLayoutItem - { - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - Padding = new Padding(5, 5, 0, 5), - Spacing = 5, - Items = - { - new StackLayoutItem - { - Control = tabletSwitcher = new TabletSwitcher() - }, - new StackLayoutItem(null, true), - new StackLayoutItem - { - Control = commandsPanel = new Panel() - } - } - } - } - } - }; - - controlPanel.ProfileBinding.Bind(tabletSwitcher.SelectedValueBinding.Cast()); - - tabletSwitcher.ProfilesBinding.BindDataContext(a => a.Settings.Profiles); - - App.Driver.TabletsChanged += HandleTabletsChanged; - Application.Instance.AsyncInvoke(async () => HandleTabletsChanged(this, await App.Driver.Instance.GetTablets())); - } - - private StackLayout layout; - private TabletSwitcher tabletSwitcher; - private ControlPanel controlPanel; - private Panel commandsPanel; - - public Control CommandsControl - { - set => commandsPanel.Content = value; - get => commandsPanel.Content; - } - - private void HandleTabletsChanged(object sender, IEnumerable tablets) - { - tabletSwitcher.HandleTabletsChanged(sender, tablets.ToImmutableArray()); - } - - private class TabletSwitcher : DropDown - { - public TabletSwitcher() - { - this.ItemTextBinding = Binding.Property(t => t.Tablet); - this.DataStore = visibleProfiles; - this.SelectedIndex = 0; - } - - private readonly ObservableCollection visibleProfiles = new ObservableCollection(); - - private ProfileCollection profiles; - public ProfileCollection Profiles - { - set - { - this.profiles = value; - this.OnProfilesChanged(); - } - get => this.profiles; - } - - public event EventHandler ProfilesChanged; - - protected virtual async void OnProfilesChanged() - { - ProfilesChanged?.Invoke(this, new EventArgs()); - var tablets = await App.Driver.Instance.GetTablets(); - HandleTabletsChanged(this, tablets.ToImmutableArray()); - } - - public BindableBinding ProfilesBinding - { - get - { - return new BindableBinding( - this, - c => c.Profiles, - (c, v) => c.Profiles = v, - (c, h) => c.ProfilesChanged += h, - (c, h) => c.ProfilesChanged -= h - ); - } - } - - public void HandleTabletsChanged(object sender, IList tablets) - { - visibleProfiles.Clear(); - if (tablets.Any()) - { - var tabletsWithoutProfile = from tablet in tablets - where !profiles.Any(p => p.Tablet == tablet.Properties.Name) - select tablet; - - foreach (var tablet in tabletsWithoutProfile) - profiles.Generate(tablet); - - foreach (var tablet in tablets) - visibleProfiles.Add(Profiles.FirstOrDefault(p => p.Tablet == tablet.Properties.Name)); - - if (this.SelectedIndex < 0) - { - this.SelectedIndex = 0; - this.OnSelectedValueChanged(EventArgs.Empty); - } - } - else - { - this.SelectedValue = null; - this.OnSelectedValueChanged(EventArgs.Empty); - } - } - } - } -} diff --git a/OpenTabletDriver.UX/Controls/ToolsPanel.cs b/OpenTabletDriver.UX/Controls/ToolsPanel.cs new file mode 100644 index 000000000..925af2585 --- /dev/null +++ b/OpenTabletDriver.UX/Controls/ToolsPanel.cs @@ -0,0 +1,24 @@ +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls.Editors; + +namespace OpenTabletDriver.UX.Controls +{ + public class ToolsPanel : DesktopPanel + { + private readonly PluginSettingsEditorList _editor; + private readonly App _app; + + public ToolsPanel(IControlBuilder controlBuilder, App app) + { + _app = app; + Content = _editor = controlBuilder.Build>(); + } + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + _editor.DataContext = _app.Settings.Tools; + } + } +} diff --git a/OpenTabletDriver.UX/Controls/Utilities/ActionCommand.cs b/OpenTabletDriver.UX/Controls/Utilities/ActionCommand.cs deleted file mode 100644 index d67f29747..000000000 --- a/OpenTabletDriver.UX/Controls/Utilities/ActionCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Utilities -{ - public class ActionCommand : Command - { - public Action Action { set; get; } - - protected override void OnExecuted(EventArgs e) - { - base.OnExecuted(e); - Action?.Invoke(); - } - } -} diff --git a/OpenTabletDriver.UX/Controls/Utilities/BooleanCommand.cs b/OpenTabletDriver.UX/Controls/Utilities/BooleanCommand.cs deleted file mode 100644 index 57d3d5469..000000000 --- a/OpenTabletDriver.UX/Controls/Utilities/BooleanCommand.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Controls.Utilities -{ - public class BooleanCommand : CheckCommand - { - public BooleanCommand() - { - CheckedBinding = new BindableBinding( - this, - (c) => c.Checked, - (c, v) => c.Checked = v ?? false, - (c, h) => c.CheckedChanged += h, - (c, h) => c.CheckedChanged -= h - ); - } - - public BindableBinding CheckedBinding { get; } - } -} diff --git a/OpenTabletDriver.UX/DaemonWatchdog.cs b/OpenTabletDriver.UX/DaemonWatchdog.cs deleted file mode 100644 index fe2c5bdd0..000000000 --- a/OpenTabletDriver.UX/DaemonWatchdog.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.UX -{ - public class DaemonWatchdog : IDisposable - { - public event EventHandler DaemonExited; - - private Process daemonProcess; - - private readonly static ProcessStartInfo startInfo = DesktopInterop.CurrentPlatform switch - { - PluginPlatform.Windows => new ProcessStartInfo - { - FileName = Path.Join(Directory.GetCurrentDirectory(), "OpenTabletDriver.Daemon.exe"), - Arguments = "", - WorkingDirectory = Directory.GetCurrentDirectory(), - CreateNoWindow = true - }, - PluginPlatform.MacOS => new ProcessStartInfo - { - FileName = Path.Join(AppContext.BaseDirectory, "OpenTabletDriver.Daemon"), - Arguments = $"-c {Path.Join(AppContext.BaseDirectory, "Configurations")}" - }, - _ => new ProcessStartInfo - { - FileName = "dotnet", - Arguments = Path.Join(Directory.GetCurrentDirectory(), "OpenTabletDriver.Daemon.dll") - } - }; - - public static bool CanExecute => - File.Exists(startInfo.FileName) || - File.Exists(startInfo.Arguments); - - public void Start() - { - this.daemonProcess = new Process() - { - StartInfo = startInfo, - EnableRaisingEvents = true - }; - this.daemonProcess.Exited += (_, e) => - { - DaemonExited?.Invoke(this, EventArgs.Empty); - }; - this.daemonProcess.Start(); - } - - public void Stop() - { - this.daemonProcess?.Kill(); - } - - public void Dispose() - { - Stop(); - this.daemonProcess?.Dispose(); - } - } -} diff --git a/OpenTabletDriver.UX/DependencyInjectionExtensions.cs b/OpenTabletDriver.UX/DependencyInjectionExtensions.cs new file mode 100644 index 000000000..9c5d144de --- /dev/null +++ b/OpenTabletDriver.UX/DependencyInjectionExtensions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace OpenTabletDriver.UX +{ + public static class DependencyInjectionExtensions + { + public static ServiceDescriptor Singleton() where T : class => ServiceDescriptor.Singleton(); + } +} diff --git a/OpenTabletDriver.UX/DesktopForm.cs b/OpenTabletDriver.UX/DesktopForm.cs deleted file mode 100644 index 331697f40..000000000 --- a/OpenTabletDriver.UX/DesktopForm.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.UX -{ - using static App; - - public abstract class DesktopForm : Form - { - public DesktopForm() - { - Icon = Logo.WithSize(Logo.Size); - } - - public DesktopForm(Window parentWindow) - : this() - { - Owner = parentWindow; - } - - private bool initialized; - - protected virtual void InitializeForm() - { - ToCenter(); - } - - public new void Show() - { - if (!initialized) - { - initialized = true; - if (ClientSize.Width == 0 && ClientSize.Height == 0) - { - base.Show(); - InitializeForm(); - } - else - { - InitializeForm(); - base.Show(); - } - } - else - { - base.Show(); - } - } - - private void ToCenter() - { - if (DesktopInterop.CurrentPlatform == PluginPlatform.Windows) - { - var x = Owner.Location.X + (Owner.Size.Width / 2); - var y = Owner.Location.Y + (Owner.Size.Height / 2); - var center = new PointF(x, y); - - Location = new Point((int)(center.X - (ClientSize.Width / 2)), (int)(center.Y - (ClientSize.Height / 2))); - } - } - } -} diff --git a/OpenTabletDriver.UX/Dialogs/AdvancedBindingDialog.cs b/OpenTabletDriver.UX/Dialogs/AdvancedBindingDialog.cs new file mode 100644 index 000000000..e7fa652db --- /dev/null +++ b/OpenTabletDriver.UX/Dialogs/AdvancedBindingDialog.cs @@ -0,0 +1,115 @@ +using System.Collections.Immutable; +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.ViewModels; + +namespace OpenTabletDriver.UX.Dialogs +{ + public sealed class AdvancedBindingDialog : Dialog> + { + public AdvancedBindingDialog(IControlBuilder controlBuilder, IPluginFactory pluginFactory, DirectBinding binding) + { + Title = "Select a binding..."; + + Width = 500; + Height = 400; + + var initialSettings = binding.DataValue; + + var types = pluginFactory.GetMatchingTypes(typeof(IBinding)).ToImmutableList(); + + var typePicker = new DropDown + { + ItemTextBinding = Binding.Property(t => Format(t)), + DataStore = types + }; + + var index = types.FindIndex(c => c.FullName == initialSettings?.Path); + if (index >= 0) + typePicker.SelectedIndex = index; + + var properties = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5 + }; + + DataContextChanged += delegate + { + properties.Items.Clear(); + + if (DataContext is not PluginSettings settings || typePicker.SelectedValue is not Type type) + return; + + foreach (var control in controlBuilder.Generate(settings, type)) + properties.Items.Add(control); + }; + + DataContext = initialSettings; + + typePicker.SelectedValueChanged += delegate + { + if (typePicker.SelectedValue is Type type) + DataContext = new PluginSettings(type); + else + DataContext = null; + }; + + var editorLayout = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + typePicker, + new StackLayoutItem(properties, true) + } + }; + + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5, + Items = + { + new StackLayoutItem(editorLayout, true), + new StackLayout + { + Orientation = Orientation.Horizontal, + Spacing = 5, + Items = + { + new StackLayoutItem(null, true), + new Button(Clear) + { + Text = "Clear" + }, + new Button(Apply) + { + Text = "Apply" + } + } + } + } + }; + } + + private void Clear(object? sender, EventArgs e) + { + Close(new DialogModel(DialogResult.Ok)); + } + + private void Apply(object? sender, EventArgs e) + { + Close(new DialogModel(DialogResult.Ok, DataContext as PluginSettings)); + } + + private static string Format(Type? type) + { + return type?.GetFriendlyName() ?? type?.GetFullyQualifiedName() ?? "None"; + } + } +} diff --git a/OpenTabletDriver.UX/Dialogs/BindingDialog.cs b/OpenTabletDriver.UX/Dialogs/BindingDialog.cs new file mode 100644 index 000000000..a779b36b6 --- /dev/null +++ b/OpenTabletDriver.UX/Dialogs/BindingDialog.cs @@ -0,0 +1,149 @@ +using System.Text; +using Eto.Forms; +using OpenTabletDriver.Desktop.Binding; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.UX.ViewModels; + +namespace OpenTabletDriver.UX.Dialogs +{ + public sealed class BindingDialog : Dialog> + { + private readonly IPluginFactory _pluginFactory; + private const string NULL_PROMPT = "Press a key, combination of keys, or a mouse button."; + + public BindingDialog(IPluginFactory pluginFactory, DirectBinding binding) + { + _pluginFactory = pluginFactory; + DataContext = binding.DataValue; + + Title = "Select a binding..."; + + Width = 300; + Height = 200; + + var tb = new TextArea + { + TextAlignment = TextAlignment.Center, + Text = NULL_PROMPT, + ReadOnly = true + }; + tb.TextBinding.BindDataContext((PluginSettings? s) => Format(s)); + + tb.MouseDown += MouseHandler; + tb.KeyDown += KeyHandler; + + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5, + Items = + { + new StackLayoutItem(tb, true), + new StackLayout + { + Orientation = Orientation.Horizontal, + Spacing = 5, + Items = + { + new StackLayoutItem(null, true), + new Button(Clear) + { + Text = "Clear" + }, + new Button(Apply) + { + Text = "Apply" + } + } + } + } + }; + } + + private void Clear(object? sender, EventArgs e) + { + Close(new DialogModel(DialogResult.Ok)); + } + + private void Apply(object? sender, EventArgs e) + { + Close(new DialogModel(DialogResult.Ok, DataContext as PluginSettings)); + } + + private string Format(PluginSettings? settings) + { + return settings?.Format(_pluginFactory) ?? NULL_PROMPT; + } + + private void MouseHandler(object? sender, MouseEventArgs args) + { + var button = args.Buttons switch + { + MouseButtons.Primary => MouseButton.Left, + MouseButtons.Alternate => MouseButton.Right, + MouseButtons.Middle => MouseButton.Middle, + _ => MouseButton.None + }; + + var settings = new PluginSettings(typeof(MouseBinding)); + settings.Set((MouseBinding b) => b.Button, Enum.GetName(button)); + + DataContext = settings; + } + + private void KeyHandler(object? sender, KeyEventArgs args) + { + var keys = args.KeyData; + + if (keys.HasFlag(Keys.Alt | Keys.LeftAlt) || keys.HasFlag(Keys.Alt | Keys.RightAlt)) + keys &= ~Keys.Alt; + else if (keys.HasFlag(Keys.Control | Keys.LeftControl) || keys.HasFlag(Keys.Control | Keys.RightControl)) + keys &= ~Keys.Control; + else if (keys.HasFlag(Keys.Shift | Keys.LeftShift) || keys.HasFlag(Keys.Shift | Keys.RightShift)) + keys &= ~Keys.Shift; + else if (keys.HasFlag(Keys.Application | Keys.LeftApplication) || keys.HasFlag(Keys.Application | Keys.RightApplication)) + keys &= ~Keys.Application; + + if ((keys & Keys.ModifierMask) == 0) + { + var settings = new PluginSettings(typeof(KeyBinding)); + settings.Set((KeyBinding k) => k.Key, keys.ToString()); + DataContext = settings; + } + else + { + var settings = new PluginSettings(typeof(MultiKeyBinding)); + settings.Set((MultiKeyBinding k) => k.Keys, CreateShortcutString(keys)); + DataContext = settings; + } + } + + private static string CreateShortcutString(Keys keys) + { + var sb = new StringBuilder(); + + if (keys.HasFlag(Keys.Application)) + AppendSeparator(sb, "+", nameof(Keys.Application)); + if (keys.HasFlag(Keys.Control)) + AppendSeparator(sb, "+", nameof(Keys.Control)); + if (keys.HasFlag(Keys.Shift)) + AppendSeparator(sb, "+", nameof(Keys.Shift)); + if (keys.HasFlag(Keys.Alt)) + AppendSeparator(sb, "+", nameof(Keys.Alt)); + + var mainKey = keys & Keys.KeyMask; + AppendSeparator(sb, "+", mainKey.ToString()); + + return sb.ToString(); + } + + private static void AppendSeparator(StringBuilder sb, string separator, string text) + { + if (sb.Length > 0) + sb.Append(separator); + sb.Append(text); + } + } +} diff --git a/OpenTabletDriver.UX/Dialogs/ExceptionDialog.cs b/OpenTabletDriver.UX/Dialogs/ExceptionDialog.cs new file mode 100644 index 000000000..bf311da2a --- /dev/null +++ b/OpenTabletDriver.UX/Dialogs/ExceptionDialog.cs @@ -0,0 +1,64 @@ +using Eto.Forms; + +namespace OpenTabletDriver.UX.Dialogs +{ + public sealed class ExceptionDialog : Dialog + { + public ExceptionDialog(Exception exception) + { + Title = "Application Error"; + Width = 600; + Height = 400; + + var headerLabel = new Panel + { + Padding = 10, + Content = new Label + { + Text = "An application error has occured. Report this to the developers!" + } + }; + + var stackTrace = new TextArea + { + ReadOnly = true, + Wrap = false, + Text = exception.ToString() + }; + + var copyButton = new Button((_, _) => Clipboard.Instance.Text = stackTrace.Text) + { + Text = "Copy" + }; + + var okButton = new Button((_, _) => Close()) + { + Text = "OK" + }; + + Content = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5, + Items = + { + new StackLayoutItem(headerLabel, HorizontalAlignment.Center), + new StackLayoutItem(stackTrace, HorizontalAlignment.Stretch, true), + new StackLayout + { + Orientation = Orientation.Horizontal, + Spacing = 5, + Items = + { + new StackLayoutItem(null, true), + copyButton, + okButton + } + } + } + }; + } + } +} diff --git a/OpenTabletDriver.UX/Dialogs/OfflineAboutDialog.cs b/OpenTabletDriver.UX/Dialogs/OfflineAboutDialog.cs new file mode 100644 index 000000000..a44f2cd92 --- /dev/null +++ b/OpenTabletDriver.UX/Dialogs/OfflineAboutDialog.cs @@ -0,0 +1,23 @@ +using Eto.Forms; + +namespace OpenTabletDriver.UX.Dialogs +{ + public class OfflineAboutDialog : AboutDialog + { + public OfflineAboutDialog() + { + Title = "OpenTabletDriver"; + ProgramName = "OpenTabletDriver"; + ProgramDescription = "Open source, cross-platform tablet configurator"; + WebsiteLabel = "OpenTabletDriver"; + Website = new Uri(@"https://opentabletdriver.net"); + Version = $"v{Metadata.Version}"; + Developers = new[] { "InfinityGhost" }; + Designers = new[] { "InfinityGhost" }; + Documenters = new[] { "InfinityGhost" }; + License = "LGPLv3"; + Copyright = string.Empty; + Logo = Metadata.Logo.WithSize(256, 256); + } + } +} diff --git a/OpenTabletDriver.UX/Dialogs/RepositoryDialog.cs b/OpenTabletDriver.UX/Dialogs/RepositoryDialog.cs deleted file mode 100644 index e76b2b2fa..000000000 --- a/OpenTabletDriver.UX/Dialogs/RepositoryDialog.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Dialogs -{ - public class RepositoryDialog : Dialog - { - public RepositoryDialog() - { - this.Size = new Size(350, 250); - } - - public RepositoryDialog(string title) - : this() - { - this.Title = title; - } - - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - - var owner = new TextBoxGroup("Owner") - { - DefaultInputText = PluginMetadataCollection.REPOSITORY_OWNER - }; - - var repo = new TextBoxGroup("Name") - { - DefaultInputText = PluginMetadataCollection.REPOSITORY_NAME - }; - - var gitRef = new TextBoxGroup("Ref") - { - DefaultInputText = "master" - }; - - var actions = new StackLayout - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Center, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new Button((sender, e) => Close(null)) - { - Text = "Cancel" - } - }, - new StackLayoutItem - { - Expand = true, - Control = new Button((sender, e) => Return(owner, repo, gitRef)) - { - Text = "Apply" - } - } - } - }; - - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Padding = 5, - Spacing = 5, - Items = - { - new StackLayoutItem(owner), - new StackLayoutItem(repo), - new StackLayoutItem(gitRef), - new StackLayoutItem(null, true), - new StackLayoutItem(actions) - } - }; - } - - protected async void Return(TextBoxGroup owner, TextBoxGroup repo, TextBoxGroup gitRef) - { - try - { - var collection = await PluginMetadataCollection.DownloadAsync(owner.InputText, repo.InputText, gitRef.InputText); - Close(collection); - } - catch (HttpRequestException httpEx) when (httpEx.StatusCode == HttpStatusCode.NotFound) - { - MessageBox.Show( - "Unable to find the repository source that was requested.", - "Error switching repository", - MessageBoxButtons.OK, - MessageBoxType.Error - ); - } - catch (Exception ex) - { - ex.ShowMessageBox(); - } - } - - protected class TextBoxGroup : Group - { - public TextBoxGroup(string text) - { - base.Text = text; - base.Orientation = Orientation.Horizontal; - } - - private string inputText; - public string InputText - { - protected set => this.inputText = value; - get => this.inputText ?? DefaultInputText; - } - - public string DefaultInputText { set; get; } - - protected const int TEXTBOX_WIDTH = 200; - - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - - var textbox = new TextBox - { - Width = TEXTBOX_WIDTH, - PlaceholderText = DefaultInputText - }; - textbox.TextChanged += (sender, e) => InputText = textbox.Text; - - base.Content = new StackLayout - { - Orientation = Orientation.Horizontal, - Items = - { - new StackLayoutItem(null, true), - new StackLayoutItem(textbox) - } - }; - } - } - } -} diff --git a/OpenTabletDriver.UX/EtoExtensions.cs b/OpenTabletDriver.UX/EtoExtensions.cs new file mode 100644 index 000000000..a780aa833 --- /dev/null +++ b/OpenTabletDriver.UX/EtoExtensions.cs @@ -0,0 +1,194 @@ +using System.Linq.Expressions; +using System.Reflection; +using Eto.Forms; +using OpenTabletDriver.Desktop.Reflection; +using OpenTabletDriver.UX.Dialogs; + +namespace OpenTabletDriver.UX +{ + public static class EtoExtensions + { + /// + /// Shows an exception with a MessageBox dialog. + /// + /// The exception to show. + public static void Show(this Exception ex) + { + Application.Instance.Invoke(() => + { + try + { + // Grab innermost exception + while (ex.InnerException != null) + ex = ex.InnerException; + + var dialog = new ExceptionDialog(ex); + dialog.ShowModal(Application.Instance.MainForm); + } + catch + { + Console.WriteLine(ex.ToString()); + } + }); + } + + /// + /// Starts a task on the default task scheduler. + /// + /// The task to start. + public static void Run(this Task task) + { + _ = task.ContinueWith(t => t.Exception?.Show(), TaskContinuationOptions.OnlyOnFaulted); + } + + /// + /// Returns a for a plugin setting. + /// + /// The settings source. + /// The expression pointing to the setting. + /// The settings type. + /// The setting target type. + public static Func GetterFor( + this PluginSettings settings, + Expression> expression + ) + { + var body = (MemberExpression) expression.Body; + + var property = (PropertyInfo) body.Member; + var propertyName = property.Name; + var propertyType = property.PropertyType; + + return () => (T)settings[propertyName].GetValue(propertyType); + } + + /// + /// Converts a PluginSetting binding into a specific type. + /// + /// The binding to convert + /// The target value type + public static DirectBinding ValueSetting(this DirectBinding binding) + { + return binding.Convert( + c => c.GetValue(), + v => binding.DataValue.SetValue(v) + ); + } + + /// + /// Pretty prints to a human readable string. + /// + /// The settings to format + /// Plugin factory + public static string Format(this PluginSettings settings, IPluginFactory factory) + { + var friendlyName = factory.GetFriendlyName(settings.Path) ?? settings.Path; + + if (!settings.Settings.Any()) + return friendlyName; + + return friendlyName + ": [ " + string.Join(", ", settings.Settings) + " ]"; + } + + /// + /// Returns a friendly name from an expression. + /// + /// The expression pointing to the target member. + /// The source type. + /// The target member type. + /// A human-readable string or the member name. + public static string GetFriendlyName(this Expression> expression) + { + var memberExpression = expression.Body switch + { + UnaryExpression unEx => (MemberExpression) unEx.Operand, + object m => (MemberExpression)m + }; + + var member = memberExpression.Member; + return member.GetCustomAttribute()?.DisplayName ?? member.Name; + } + + /// + /// Modifies a in with a new value based on an . + /// + /// The settings source. + /// The target property. + /// The value to set. + /// The source type. + /// The target value type. + public static void Set(this PluginSettings settings, Expression> expression, TValue value) + { + var name = MemberNameFor(expression); + settings[name].SetValue(value); + } + + /// + /// Returns from a based on . + /// + /// The settings source. + /// The target property. + /// The source type. + /// The target value type. + public static TValue Get(this PluginSettings settings, Expression> expression) + { + var name = MemberNameFor(expression); + return settings[name].GetValue(); + } + + /// + /// Returns from a child property based on . + /// + /// The settings source. + /// The target child property. + /// The source type. + /// The target value type. + public static TValue GetChild(this PluginSettings settings, Expression> expression) + { + var propertyExpression = (MemberExpression) expression.Body; + var property = (PropertyInfo) propertyExpression.Member; + + var parentExpression = (MemberExpression) propertyExpression.Expression!; + var parentName = parentExpression.Member.Name; + var parentObj = settings[parentName].GetValue(parentExpression.Type); + + return (TValue) property.GetValue(parentObj)!; + } + + /// + /// Modifies a 's child property in with a new value based on an . + /// + /// The settings source. + /// The target child property. + /// The value to set within the child property. + /// The source type. + /// The target value type. + public static void SetChild(this PluginSettings settings, Expression> expression, TValue value) + { + var propertyExpression = (MemberExpression) expression.Body; + var property = (PropertyInfo) propertyExpression.Member; + + var parentExpression = (MemberExpression) propertyExpression.Expression!; + var parentName = parentExpression.Member.Name; + var parentObj = settings[parentName].GetValue(parentExpression.Type); + + property.SetValue(parentObj, value); + settings[parentName].SetValue(parentObj); // TODO: Verify this notifies for MVVM! + } + + /// + /// Returns the name of a target member. + /// + /// An expression to the target member. + /// The source type. + /// The target value type. + private static string MemberNameFor(Expression> expression) + { + var body = (MemberExpression) expression.Body; + + var member = body.Member; + var memberName = member.Name; + return memberName; + } + } +} diff --git a/OpenTabletDriver.UX/Extensions.cs b/OpenTabletDriver.UX/Extensions.cs deleted file mode 100644 index 36e12dc70..000000000 --- a/OpenTabletDriver.UX/Extensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Tablet; -using StreamJsonRpc.Protocol; - -namespace OpenTabletDriver.UX -{ - public static class Extensions - { - public static void ShowMessageBox(this Exception exception) - { - Log.Exception(exception); - MessageBox.Show( - exception.Message + Environment.NewLine + exception.StackTrace, - $"Error: {exception.GetType().Name}", - MessageBoxButtons.OK, - MessageBoxType.Error - ); - } - - public static void ShowMessageBox(this CommonErrorData errorData) - { - string message = errorData.Message + Environment.NewLine + errorData.StackTrace; - Log.Write( - errorData.TypeName, - message, - LogLevel.Error - ); - MessageBox.Show( - message, - errorData.TypeName, - MessageBoxButtons.OK, - MessageBoxType.Error - ); - } - - public static BindableBinding GetEnabledBinding(this TControl control) where TControl : Control - { - return new BindableBinding( - control, - (c) => c.Enabled, - (c, v) => c.Enabled = v, - (c, e) => c.EnabledChanged += e, - (c, e) => c.EnabledChanged -= e - ); - } - - public static async Task GetTabletReference(this Profile profile) - { - var tablets = await App.Driver.Instance.GetTablets(); - return tablets.FirstOrDefault(t => t.Properties.Name == profile.Tablet); - } - } -} diff --git a/OpenTabletDriver.UX/IViewModelRoot.cs b/OpenTabletDriver.UX/IViewModelRoot.cs deleted file mode 100644 index 8b19c558a..000000000 --- a/OpenTabletDriver.UX/IViewModelRoot.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.ComponentModel; - -namespace OpenTabletDriver.UX -{ - public interface IViewModelRoot where T : INotifyPropertyChanged - { - T ViewModel { set; get; } - } -} diff --git a/OpenTabletDriver.UX/MainForm.cs b/OpenTabletDriver.UX/MainForm.cs index c56423d4e..fa4d8c341 100644 --- a/OpenTabletDriver.UX/MainForm.cs +++ b/OpenTabletDriver.UX/MainForm.cs @@ -1,645 +1,257 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Eto.Drawing; +using System.Diagnostics; +using Eto; using Eto.Forms; -using Newtonsoft.Json.Linq; +using Microsoft.Extensions.DependencyInjection; using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Diagnostics; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Logging; -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.RPC; +using OpenTabletDriver.UX.Components; using OpenTabletDriver.UX.Controls; +using OpenTabletDriver.UX.Windows; namespace OpenTabletDriver.UX { - using static App; - - public class MainForm : DesktopForm + public sealed class MainForm : DesktopForm { - public MainForm() - : base() - { - this.DataContext = App.Current; + private readonly App _app; + private readonly RpcClient _rpc; + private readonly Placeholder _placeholder; + private readonly IControlBuilder _controlBuilder; - // Call InitializeForm on ctor since DesktopForm.Show() won't be called on binary launch - InitializeForm(); - InitializePlatform(); + public MainForm(App app, RpcClient rpc, IControlBuilder controlBuilder, IServiceProvider serviceProvider) + { + DataContext = _app = app; + _rpc = rpc; + _controlBuilder = controlBuilder; - SetTitle(); - Menu = ConstructMenu(); + Width = 1100; + Height = 800; - base.Content = placeholder = new Placeholder + Content = _placeholder = new Placeholder { Text = "Connecting to OpenTabletDriver Daemon..." }; - trayIcon?.Indicator?.Show(); + TitleBinding.BindDataContext((App a) => a.MainFormTitle); - Driver.Connected += HandleDaemonConnected; - Driver.Disconnected += HandleDaemonDisconnected; + var modifier = Application.Instance.CommonModifier; - Application.Instance.AsyncInvoke(async () => + var loadSettings = new AppCommand("Load settings...", LoadSettingsDialog, modifier | Keys.O); + var saveSettings = new AppCommand("Save settings", _app.SaveSettings, modifier | Keys.S); + var saveSettingsAs = new AppCommand("Save settings as...", SaveSettingsDialog, modifier | Keys.Shift | Keys.S); + var applySettings = new AppCommand("Apply settings", _app.ApplySettings, modifier | Keys.Enter); + var discardSettings = new AppCommand("Discard settings", _app.DiscardSettings); + var resetDefaults = new AppCommand("Reset to defaults", _app.ResetSettings, modifier | Keys.Shift | Keys.R); + + // TODO: Presets menu items + var presetsMenu = new ButtonMenuItem { - try - { - var timeout = Task.Delay(TimeSpan.FromSeconds(15)); - var result = await Task.WhenAny(Driver.Connect(), timeout); - if (result == timeout) - { - MessageBox.Show("Daemon connection timed out after some time. Verify that the daemon is running.", "Daemon Connection Timed Out"); - Environment.Exit(1); - } - - await CheckForUpdates(); - } - catch (Exception ex) + Text = "Presets" + }; + + var fileMenu = new ButtonMenuItem + { + Text = "&File", + Items = { - ex.ShowMessageBox(); - Environment.Exit(2); + loadSettings, + saveSettings, + saveSettingsAs, + applySettings, + discardSettings, + resetDefaults, + new SeparatorMenuItem(), + presetsMenu } - }); - } - - private const int DEFAULT_CLIENT_WIDTH = 960; - private const int DEFAULT_CLIENT_HEIGHT = 760; + }; - private TabletSwitcherPanel mainPanel; - private MenuBar menu; - private Placeholder placeholder; - private TrayIcon trayIcon; + var detectTablet = new AppCommand("Detect tablet", DetectTablets, modifier | Keys.D); + var tabletDebugger = new AppCommand("Tablet debugger...", _app.ShowWindow, modifier | Keys.Shift | Keys.D); + var configurationEditor = new AppCommand("Configuration editor...", _app.ShowWindow, modifier | Keys.Shift | Keys.E); - protected override void InitializeForm() - { - var bounds = Screen.FromPoint(Mouse.Position).Bounds; - - if (this.WindowState != WindowState.Maximized) + var tabletsMenu = new ButtonMenuItem { - var minWidth = Math.Min(DEFAULT_CLIENT_WIDTH, bounds.Width * 0.95); - var minHeight = Math.Min(DEFAULT_CLIENT_HEIGHT, bounds.Height * 0.95); - - this.Size = new Size((int)minWidth, (int)minHeight); - - if (DesktopInterop.CurrentPlatform == PluginPlatform.Windows) + Text = "Tablets", + Items = { - var x = Screen.WorkingArea.Center.X - (minWidth / 2); - var y = Screen.WorkingArea.Center.Y - (minHeight / 2); - this.Location = new Point((int)x, (int)y); + detectTablet, + tabletDebugger, + configurationEditor, } - } - } + }; - protected void InitializePlatform() - { - switch (DesktopInterop.CurrentPlatform) - { - case PluginPlatform.MacOS: - this.Padding = 10; - break; - } + var pluginManager = new AppCommand("Plugin manager...", _app.ShowWindow, modifier | Keys.P); - bool enableDaemonWatchdog = DesktopInterop.CurrentPlatform switch + var pluginsMenu = new ButtonMenuItem { - PluginPlatform.Windows => true, - PluginPlatform.MacOS => true, - _ => false, + Text = "Plugins", + Items = + { + pluginManager + } }; - if (App.EnableTrayIcon) + var exportDiagnostics = new AppCommand("Export diagnostics...", ExportDiagnosticsDialog, modifier | Keys.E); + var showWiki = new AppCommand("OpenTabletDriver wiki...", () => _app.Open(Metadata.WIKI_URL)); + + var helpMenu = new ButtonMenuItem { - trayIcon = new TrayIcon(this); - if (WindowState == WindowState.Minimized) + Text = "&Help", + Items = { - this.Visible = false; - this.ShowInTaskbar = false; + exportDiagnostics, + showWiki } - this.WindowStateChanged += (sender, e) => - { - switch (this.WindowState) - { - case WindowState.Normal: - case WindowState.Maximized: - this.Visible = true; - this.ShowInTaskbar = true; - break; - case WindowState.Minimized: - this.Visible = false; - this.ShowInTaskbar = false; - break; - } - }; - Application.Instance.Terminating += (sender, e) => trayIcon.Dispose(); - } + }; - if (App.EnableDaemonWatchdog) + var reconnect = new AppCommand("Reconnect to daemon", Reconnect); + var debugBreak = new AppCommand("Debugger break", Debugger.Break); + + var debugMenu = new ButtonMenuItem { - // Check if daemon is already active, if not then start it as a subprocess if it exists in the local path. - if (!Instance.Exists("OpenTabletDriver.Daemon") && DaemonWatchdog.CanExecute) + Text = "Debug", + Visible = Debugger.IsAttached, + Items = { - var watchdog = new DaemonWatchdog(); - watchdog.Start(); - watchdog.DaemonExited += (sender, e) => - { - Application.Instance.AsyncInvoke(() => - { - var dialogResult = MessageBox.Show( - this, - "Fatal: The OpenTabletDriver Daemon has exited. Do you want to restart it?", - "OpenTabletDriver Fatal Error", - MessageBoxButtons.YesNo, - MessageBoxType.Error - ); - switch (dialogResult) - { - case DialogResult.Yes: - watchdog.Dispose(); - watchdog.Start(); - break; - case DialogResult.No: - default: - Environment.Exit(0); - break; - } - }); - }; - this.Closing += (sender, e) => - { - watchdog.Dispose(); - }; + reconnect, + debugBreak } - } - } - - private MenuBar ConstructMenu() - { - var quitCommand = new Command { MenuText = "Quit", Shortcut = Application.Instance.CommonModifier | Keys.Q }; - quitCommand.Executed += (sender, e) => Application.Instance.Quit(); - - var aboutCommand = new Command { MenuText = "About...", Shortcut = Keys.F1 }; - aboutCommand.Executed += (sender, e) => AboutDialog.ShowDialog(this); - - var resetSettings = new Command { MenuText = "Reset to defaults" }; - resetSettings.Executed += async (sender, e) => await ResetSettingsDialog(); - - var loadSettings = new Command { MenuText = "Load settings...", Shortcut = Application.Instance.CommonModifier | Keys.O }; - loadSettings.Executed += async (sender, e) => await LoadSettingsDialog(); - - var saveSettingsAs = new Command { MenuText = "Save settings as...", Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.S }; - saveSettingsAs.Executed += async (sender, e) => await SaveSettingsDialog(); - - var saveSettings = new Command { MenuText = "Save settings", Shortcut = Application.Instance.CommonModifier | Keys.S }; - saveSettings.Executed += async (sender, e) => await SaveSettings(); - - var applySettings = new Command { MenuText = "Apply settings", Shortcut = Application.Instance.CommonModifier | Keys.Enter }; - applySettings.Executed += async (sender, e) => await ApplySettings(); - - var refreshPresets = new Command { MenuText = "Refresh presets" }; - refreshPresets.Executed += async (sender, e) => await RefreshPresets(); - - var savePreset = new Command { MenuText = "Save as preset..." }; - savePreset.Executed += async (sender, e) => await SavePresetDialog(); - - var detectTablet = new Command { MenuText = "Detect tablet", Shortcut = Application.Instance.CommonModifier | Keys.D }; - detectTablet.Executed += async (sender, e) => await DetectTablet(); - - var showTabletDebugger = new Command { MenuText = "Tablet debugger..." }; - showTabletDebugger.Executed += (sender, e) => App.Current.DebuggerWindow.Show(); - - var deviceStringReader = new Command { MenuText = "Device string reader..." }; - deviceStringReader.Executed += (sender, e) => App.Current.StringReaderWindow.Show(); - - var configurationEditor = new Command { MenuText = "Open Configuration Editor...", Shortcut = Application.Instance.CommonModifier | Keys.E }; - configurationEditor.Executed += (sender, e) => App.Current.ConfigEditorWindow.Show(); - - var pluginManager = new Command { MenuText = "Open Plugin Manager..." }; - pluginManager.Executed += (sender, e) => App.Current.PluginManagerWindow.Show(); - - var wikiUrl = new Command { MenuText = "Open Wiki..." }; - wikiUrl.Executed += (sender, e) => DesktopInterop.Open(WikiUrl); - - var showGuide = new Command { MenuText = "Show guide..." }; - showGuide.Executed += (sender, e) => App.Current.StartupGreeterWindow.Show(); - - var exportDiagnostics = new Command { MenuText = "Export diagnostics..." }; - exportDiagnostics.Executed += async (sender, e) => await ExportDiagnostics(); + }; - var updater = new Command { MenuText = "Check for updates..." }; - updater.Executed += (sender, e) => Current.UpdaterWindow.Show(); + var quitCommand = new AppCommand("Quit", () => App.Exit(), modifier | Keys.Q); + var aboutCommand = new AppCommand("About...", () => serviceProvider.GetRequiredService().ShowDialog(this), Keys.F1); - var menuBar = new MenuBar + Menu = new MenuBar { Items = { - // File submenu - new ButtonMenuItem - { - Text = "&File", - Items = - { - loadSettings, - saveSettings, - saveSettingsAs, - resetSettings, - applySettings, - new SeparatorMenuItem(), - refreshPresets, - savePreset, - new ButtonMenuItem - { - Text = "Presets", - Items = - { - new ButtonMenuItem - { - Text = "No presets loaded", - Enabled = false - } - } - } - } - }, - // Tablets submenu - new ButtonMenuItem - { - Text = "Tablets", - Items = - { - detectTablet, - showTabletDebugger, - deviceStringReader, - configurationEditor - } - }, - // Plugins submenu - new ButtonMenuItem - { - Text = "Plugins", - Items = - { - pluginManager - } - }, - new ButtonMenuItem - { - Text = "&Help", - Items = - { - wikiUrl, - exportDiagnostics, - showGuide - } - } - }, - ApplicationItems = - { - // application (OS X) or file menu (others) + fileMenu, + tabletsMenu, + pluginsMenu, + helpMenu, + debugMenu }, QuitItem = quitCommand, AboutItem = aboutCommand }; - switch (SystemInterop.CurrentPlatform) - { - case PluginPlatform.Windows: - case PluginPlatform.MacOS: - { - menuBar.Items.GetSubmenu("&Help").Items.Add(updater); - break; - } - } - - return menuBar; + InitializeAsync().Run(); } - private void SetTitle(IEnumerable tablets = null) + /// + /// Initialize asynchronous components. + /// + private async Task InitializeAsync() { - string prefix = $"OpenTabletDriver v{App.Version} - "; - if (tablets?.Any() ?? false) - { - // Limit to 3 tablets in the title - int numTablets = Math.Min(tablets.Count(), 3); - this.Title = prefix + string.Join(", ", tablets.Take(numTablets).Select(t => t.Properties.Name)); - } - else - { - this.Title = prefix + "No tablets detected."; - } + _rpc.Connected += (_, _) => OnConnected().Run(); + _rpc.Disconnected += (_, _) => Application.Instance.AsyncInvoke(OnDisconnected); + + _app.StartDaemon(); + await _rpc.Connect(); } - private void HandleDaemonConnected(object sender, EventArgs e) => Application.Instance.AsyncInvoke(async () => + /// + /// The event handler for . + /// This is called when RPC connects to the OpenTabletDriver daemon and builds all dependent UI. + /// + private async Task OnConnected() { - // Hook events after the instance is (re)instantiated - Log.Output += async (sender, message) => { if (Driver.IsConnected) await Driver.Instance?.WriteMessage(message); }; - Driver.TabletsChanged += (sender, tablet) => SetTitle(tablet); - - // Load the application information from the daemon - AppInfo.Current = await Driver.Instance.GetApplicationInfo(); - - // Load any new plugins - AppInfo.PluginManager.Load(); - - // Show the startup greeter - if (!File.Exists(AppInfo.Current.SettingsFile) && this.WindowState != WindowState.Minimized) - App.Current.StartupGreeterWindow.Show(); - - // Synchronize settings - await SyncSettings(); + // Synchronize before building the main panel, this will avoid flickering + await _app.Synchronize(); - // Set window content - base.Menu = menu ??= ConstructMenu(); - base.Content = mainPanel ??= new TabletSwitcherPanel - { - CommandsControl = new StackLayout - { - Orientation = Orientation.Horizontal, - HorizontalContentAlignment = HorizontalAlignment.Right, - Spacing = 5, - Items = - { - new Button(async (s, e) => await SaveSettings()) - { - Text = "Save" - }, - new Button(async (s, e) => await ApplySettings()) - { - Text = "Apply" - } - } - } - }; - - // Update preset options in File menu and tray icon - await RefreshPresets(); - - // Update title to new instance - if (await Driver.Instance.GetTablets() is IEnumerable tablets) - SetTitle(tablets); - }); - - private void HandleDaemonDisconnected(object sender, EventArgs e) => Application.Instance.AsyncInvoke(async () => - { - // Hide all controls until reconnected - base.Content = placeholder; - base.Menu = null; - // Attempt to reconnect - await Driver.Connect(); - }); - - private async Task ResetSettings() - { - await Driver.Instance.ResetSettings(); - App.Current.Settings = await Driver.Instance.GetSettings(); + await Application.Instance.InvokeAsync(() => Content = _controlBuilder.Build()); } - private async Task ResetSettingsDialog() + /// + /// The event handler for . + /// This is called when RPC disconnects from the OpenTabletDriver daemon and returns to the placeholder UI. + /// + private void OnDisconnected() { - if (MessageBox.Show("Reset settings to default?", "Reset to defaults", MessageBoxButtons.OKCancel, MessageBoxType.Question) == DialogResult.Ok) - await ResetSettings(); - } + Content = _placeholder; + _app.Desynchronize(); - private async Task SyncSettings() - { - App.Current.Settings = await Driver.Instance.GetSettings(); + _rpc.Connect().Run(); } + /// + /// Prompts for a file to load settings from and applies it if valid. + /// private async Task LoadSettingsDialog() { - var fileDialog = new OpenFileDialog + var dialog = new OpenFileDialog { Title = "Load OpenTabletDriver settings...", - Directory = new Uri(Eto.EtoEnvironment.GetFolderPath(Eto.EtoSpecialFolder.Documents)), + Directory = new Uri(EtoEnvironment.GetFolderPath(EtoSpecialFolder.Documents)), Filters = { - new FileFilter("OpenTabletDriver Settings (*.json)", ".json") + new FileFilter("Settings (*.json)", ".json") } }; - switch (fileDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - var file = new FileInfo(fileDialog.FileName); - if (file.Exists) - { - App.Current.Settings = Settings.Deserialize(file); - await Driver.Instance.SetSettings(App.Current.Settings); - } - break; - } + + if (dialog.ShowDialog(this) == DialogResult.Ok) + await _app.LoadSettings(dialog.FileName); } + /// + /// Prompts for a file to save settings. + /// private async Task SaveSettingsDialog() { - var fileDialog = new SaveFileDialog + var dialog = new SaveFileDialog { Title = "Save OpenTabletDriver settings...", - Directory = new Uri(Eto.EtoEnvironment.GetFolderPath(Eto.EtoSpecialFolder.Documents)), + Directory = new Uri(EtoEnvironment.GetFolderPath(EtoSpecialFolder.Documents)), Filters = { - new FileFilter("OpenTabletDriver Settings (*.json)", ".json") + new FileFilter("Settings (*.json)", ".json") } }; - switch (fileDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - var file = new FileInfo(fileDialog.FileName); - if (App.Current.Settings is Settings settings) - { - settings.Serialize(file); - await ApplySettings(); - } - break; - } - } - - private async Task SaveSettings() - { - if (App.Current.Settings is Settings settings) - { - if (settings.Profiles.Any(p => p.AbsoluteModeSettings.Tablet.Width + p.AbsoluteModeSettings.Tablet.Height == 0)) - { - var result = MessageBox.Show( - "Warning: Your tablet area is invalid. Saving this configuration may cause problems." + Environment.NewLine + - "Are you sure you want to save your configuration?", - MessageBoxButtons.YesNo, - MessageBoxType.Warning - ); - if (result != DialogResult.Yes) - return; - } - var appInfo = await Driver.Instance.GetApplicationInfo(); - settings.Serialize(new FileInfo(appInfo.SettingsFile)); - await ApplySettings(); - } - } - - private async Task ApplySettings() - { - try - { - if (App.Current.Settings is Settings settings) - await Driver.Instance.SetSettings(settings); - } - catch (StreamJsonRpc.RemoteInvocationException riex) when (riex.ErrorData is JObject err) - { - var type = (string)err["type"]; - var message = (string)err["message"]; - var stack = (string)err["stack"]; - var logMessage = new LogMessage - { - Group = type, - Message = message, - StackTrace = stack - }; - Log.Write(logMessage); - } + if (dialog.ShowDialog(this) == DialogResult.Ok) + await _rpc.Instance!.SaveSettings(_app.Settings); } - private Task LoadPresets() + /// + /// Prompts to export a diagnostics file. + /// + private async Task ExportDiagnosticsDialog() { - var presetDir = new DirectoryInfo(AppInfo.Current.PresetDirectory); - - if (!presetDir.Exists) + var dialog = new SaveFileDialog { - presetDir.Create(); - Log.Write("Settings", $"The preset directory '{presetDir.FullName}' has been created"); - } - - AppInfo.PresetManager.Refresh(); - return Task.CompletedTask; - } - - private Task RefreshPresets() - { - LoadPresets(); - - if (trayIcon != null) // Check non-Linux - trayIcon.RefreshMenuItems(); - - // Update File submenu - var presets = AppInfo.PresetManager.GetPresets(); - var presetsMenu = menu.Items.GetSubmenu("&File").Items.GetSubmenu("Presets") as ButtonMenuItem; - presetsMenu.Items.Clear(); - - if (presets.Count != 0) - { - foreach (var preset in presets) - { - var presetItem = new ButtonMenuItem - { - Text = preset.Name - }; - presetItem.Click += PresetButtonHandler; - - presetsMenu.Items.Add(presetItem); - } - } - else - { - var emptyPresetsItem = new ButtonMenuItem - { - Text = "No presets loaded", - Enabled = false - }; - - presetsMenu.Items.Add(emptyPresetsItem); - } - - return Task.CompletedTask; - } - - private async Task SavePresetDialog() - { - var fileDialog = new SaveFileDialog - { - Title = "Save OpenTabletDriver settings as preset...", - Directory = new Uri(AppInfo.Current.PresetDirectory), + Title = "Exporting OpenTabletDriver diagnostics...", + Directory = new Uri(EtoEnvironment.GetFolderPath(EtoSpecialFolder.Documents)), Filters = { - new FileFilter("OpenTabletDriver Settings (*.json)", ".json") + new FileFilter("Diagnostics (*.txt)", ".txt") } }; - switch (fileDialog.ShowDialog(this)) + + if (dialog.ShowDialog(this) == DialogResult.Ok) { - case DialogResult.Ok: - case DialogResult.Yes: - var file = new FileInfo(fileDialog.FileName + (fileDialog.FileName.EndsWith(".json") ? "" : ".json")); - if (App.Current.Settings is Settings settings) - settings.Serialize(file); - await RefreshPresets(); - break; - } - } + var path = dialog.FileName; + if (Path.GetExtension(path) != ".txt") + path = Path.GetFileNameWithoutExtension(path) + ".txt"; - public static void PresetButtonHandler(object sender, EventArgs e) - { - var presetName = (sender as ButtonMenuItem).Text; - var preset = AppInfo.PresetManager.FindPreset(presetName); - App.Current.Settings = preset.GetSettings(); - App.Driver.Instance.SetSettings(App.Current.Settings); - Log.Write("Settings", $"Applied preset '{preset.Name}'"); + var diagnostics = await _rpc.Instance!.GetDiagnostics(); + Serialization.Serialize(new FileInfo(path), diagnostics); + } } - private async Task DetectTablet() + /// + /// Invokes a device scan for all connected tablets. + /// + private async Task DetectTablets() { - await Driver.Instance.DetectTablets(); - await Driver.Instance.SetSettings(await Driver.Instance.GetSettings()); + await _rpc.Instance!.DetectTablets(); } - private async Task ExportDiagnostics() + /// + /// Forces RPC to reconnect. + /// + private async Task Reconnect() { - try - { - var log = await Driver.Instance.GetCurrentLog(); - var diagnosticDump = new DiagnosticInfo(log, await Driver.Instance.GetDevices()); - var fileDialog = new SaveFileDialog - { - Title = "Save diagnostic information to...", - Directory = new Uri(Eto.EtoEnvironment.GetFolderPath(Eto.EtoSpecialFolder.Documents)), - Filters = - { - new FileFilter("Diagnostic information", ".json") - } - }; - switch (fileDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - string[] options = { ".json", ".txt", ".log" }; - var file = new FileInfo(fileDialog.FileName + (options.Any(fileDialog.FileName.EndsWith) ? "" : ".json")); - if (file.Exists) - file.Delete(); - using (var fs = file.OpenWrite()) - using (var sw = new StreamWriter(fs)) - await sw.WriteLineAsync(diagnosticDump.ToString()); - break; - } - } - catch (Exception ex) - { - Log.Exception(ex); - ex.ShowMessageBox(); - } - } - - private async Task CheckForUpdates() - { - if (await Driver.Instance.HasUpdate()) - { - var result = MessageBox.Show( - "An update to OpenTabletDriver is available. Do you wish to install it?", - "Update", - MessageBoxButtons.YesNo - ); - - if (result == DialogResult.Yes) - Current.UpdaterWindow.Show(); - } + await _rpc.Reconnect(); } } } diff --git a/OpenTabletDriver.UX/Metadata.cs b/OpenTabletDriver.UX/Metadata.cs new file mode 100644 index 000000000..cb25ffe1f --- /dev/null +++ b/OpenTabletDriver.UX/Metadata.cs @@ -0,0 +1,16 @@ +using System.Reflection; +using Eto.Drawing; + +namespace OpenTabletDriver.UX +{ + public static class Metadata + { + public const string WIKI_URL = "https://opentabletdriver.net/Wiki"; + + public static string Version { get; } = + Assembly.GetEntryAssembly()!.GetCustomAttribute()! + .InformationalVersion; + + public static Bitmap Logo => new Bitmap(typeof(Metadata).Assembly.GetManifestResourceStream("OpenTabletDriver.UX.Assets.otd.png")); + } +} diff --git a/OpenTabletDriver.UX/OpenTabletDriver.UX.csproj b/OpenTabletDriver.UX/OpenTabletDriver.UX.csproj index f5fd28323..f14053a42 100644 --- a/OpenTabletDriver.UX/OpenTabletDriver.UX.csproj +++ b/OpenTabletDriver.UX/OpenTabletDriver.UX.csproj @@ -1,21 +1,29 @@  - + $(FrameworkBase) - VSTHRD100; VSTHRD101; VSTHRD110; VSTHRD200 + enable + enable - - - - + + + + VSTHRD200, + VSTHRD105 + + + + + - + - - - + + + + - + diff --git a/OpenTabletDriver.UX/RPC/DaemonRpcClient.cs b/OpenTabletDriver.UX/RPC/DaemonRpcClient.cs deleted file mode 100644 index a83ee3574..000000000 --- a/OpenTabletDriver.UX/RPC/DaemonRpcClient.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using Eto.Forms; -using OpenTabletDriver.Desktop.Contracts; -using OpenTabletDriver.Desktop.RPC; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Logging; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.UX.RPC -{ - public class DaemonRpcClient : RpcClient - { - public DaemonRpcClient(string pipeName) : base(pipeName) - { - } - - public event EventHandler Message; - public event EventHandler DeviceReport; - public event EventHandler> TabletsChanged; - - protected override void OnConnected() - { - base.OnConnected(); - - Instance.Message += (sender, e) => - Application.Instance.AsyncInvoke(() => Message?.Invoke(sender, e)); - Instance.DeviceReport += (sender, e) => - Application.Instance.AsyncInvoke(() => DeviceReport?.Invoke(sender, e)); - Instance.TabletsChanged += (sender, e) => - Application.Instance.AsyncInvoke(() => TabletsChanged?.Invoke(sender, e)); - } - } -} diff --git a/OpenTabletDriver.UX/Services/EtoKeysProvider.cs b/OpenTabletDriver.UX/Services/EtoKeysProvider.cs new file mode 100644 index 000000000..4f5da4786 --- /dev/null +++ b/OpenTabletDriver.UX/Services/EtoKeysProvider.cs @@ -0,0 +1,17 @@ +using Eto.Forms; +using OpenTabletDriver.Platform.Keyboard; + +namespace OpenTabletDriver.UX.Services +{ + public class EtoKeysProvider : IKeysProvider + { + public EtoKeysProvider() + { + var keys = Enum.GetNames(); + var pairs = keys.Select(k => new KeyValuePair(k, k)); + EtoToNative = new Dictionary(pairs); + } + + public IDictionary EtoToNative { get; } + } +} diff --git a/OpenTabletDriver.UX/Tools/CompositionScheduler.cs b/OpenTabletDriver.UX/Tools/CompositionScheduler.cs deleted file mode 100644 index 9b3792691..000000000 --- a/OpenTabletDriver.UX/Tools/CompositionScheduler.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Threading; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Tools -{ - /// - /// A UX composition scheduler that fires events at a maximum of - /// and stops firing when there are no event handlers registered - /// - public static class CompositionScheduler - { - private static Timer scheduler = new Timer(_ => Application.Instance.AsyncInvoke(OnCompose)); - private const int MAX_FRAMES_PER_SEC = 60; - - public static void Register(EventHandler handler) - { - Compose += handler; - Refresh(); - } - - public static void Unregister(EventHandler handler) - { - Compose -= handler; - Refresh(); - } - - private static void Refresh() - { - if (Compose == null || Compose.GetInvocationList().Length == 0) - Stop(); - else - Start(); - } - - private static void Start() - { - if (!running) - { - running = true; - scheduler.Change(0, 1000 / MAX_FRAMES_PER_SEC); - } - } - - private static void Stop() - { - if (running) - { - running = false; - scheduler.Change(0, Timeout.Infinite); - } - } - - private static void OnCompose() - { - Compose?.Invoke(null, null); - } - - private static bool running; - private static event EventHandler Compose; - } -} diff --git a/OpenTabletDriver.UX/Tools/LogDataStore.cs b/OpenTabletDriver.UX/Tools/LogDataStore.cs deleted file mode 100644 index a08026620..000000000 --- a/OpenTabletDriver.UX/Tools/LogDataStore.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Logging; - -namespace OpenTabletDriver.UX.Tools -{ - public class LogDataStore : INotifyCollectionChanged, IEnumerable, IList - { - public LogDataStore(IEnumerable currentMessages) - { - messages = new Queue(currentMessages.TakeLast(MAX_NUM_MESSAGES)); - filteredMessages = new Queue(GetFilteredMessages()); - } - - private const int MAX_NUM_MESSAGES = 250; - - private readonly Queue messages; - private Queue filteredMessages; - - private LogLevel filter = LogLevel.Info; - public LogLevel Filter - { - set - { - this.filter = value; - filteredMessages = new Queue(GetFilteredMessages()); - OnCollectionChanged(); - } - get => this.filter; - } - - public int Count => filteredMessages.Count; - - public bool IsReadOnly => false; - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - public void Add(LogMessage message) - { - this.messages.Enqueue(message); - while (messages.Count > MAX_NUM_MESSAGES) - messages.TryDequeue(out _); - - if (message.Level >= this.Filter) - { - this.filteredMessages.Enqueue(message); - while (this.filteredMessages.Count > MAX_NUM_MESSAGES) - this.filteredMessages.TryDequeue(out _); - - OnCollectionChanged(NotifyCollectionChangedAction.Add, message); - } - } - - protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action = NotifyCollectionChangedAction.Reset) - { - var args = new NotifyCollectionChangedEventArgs(action); - CollectionChanged?.Invoke(this, args); - } - - protected void OnCollectionChanged(NotifyCollectionChangedAction action, object obj) - { - var args = new NotifyCollectionChangedEventArgs(action, obj); - CollectionChanged?.Invoke(this, args); - } - - private IEnumerable GetFilteredMessages() - { - return from message in this.messages - where message.Level >= Filter - select message; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this.filteredMessages.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return (this.filteredMessages as IEnumerable).GetEnumerator(); - } - - void ICollection.Clear() - { - this.messages.Clear(); - this.filteredMessages.Clear(); - } - - bool ICollection.Contains(LogMessage item) - { - return this.messages.Contains(item); - } - - void ICollection.CopyTo(LogMessage[] array, int arrayIndex) - { - messages.CopyTo(array, arrayIndex); - } - - int IList.IndexOf(LogMessage item) - { - return (filteredMessages as IList).IndexOf(item); - } - - void IList.Insert(int index, LogMessage item) - { - throw new NotSupportedException(); - } - - bool ICollection.Remove(LogMessage item) - { - throw new NotSupportedException(); - } - - void IList.RemoveAt(int index) - { - throw new NotSupportedException(); - } - - LogMessage IList.this[int index] - { - get => (filteredMessages as IList)[index]; - set => (filteredMessages as IList)[index] = value; - } - } -} diff --git a/OpenTabletDriver.UX/Tools/ParseTools.cs b/OpenTabletDriver.UX/Tools/ParseTools.cs deleted file mode 100644 index 409e4959e..000000000 --- a/OpenTabletDriver.UX/Tools/ParseTools.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Globalization; - -namespace OpenTabletDriver.UX.Tools -{ - public static class ParseTools - { - public static float? ToNullableFloat(string str) => float.TryParse(str, out var val) ? val : (float?)null; - public static float ToFloat(string str) => ToNullableFloat(str) ?? 0f; - - public static int? ToNullableInt(string str) => int.TryParse(str, out var val) ? val : (int?)null; - public static int ToInt(string str) => ToNullableInt(str) ?? 0; - - public static uint? ToNullableUInt(string str) => uint.TryParse(str, out var val) ? val : (uint?)null; - public static uint ToUInt(string str) => ToNullableUInt(str) ?? 0; - - public static bool TryGetHexValue(string str, out byte value) => byte.TryParse(str.Replace("0x", string.Empty), NumberStyles.HexNumber, null, out value); - - public static string ToHexString(byte[] value) - { - if (value is byte[] array) - return "0x" + BitConverter.ToString(array).Replace("-", " 0x") ?? string.Empty; - else - return string.Empty; - } - - public static byte[] ToByteArray(string hex) - { - var raw = hex.Split(' '); - byte[] buffer = new byte[raw.Length]; - for (int i = 0; i < raw.Length; i++) - { - if (TryGetHexValue(raw[i], out var val)) - buffer[i] = val; - else - return null; - } - return buffer; - } - } -} diff --git a/OpenTabletDriver.UX/TrayIcon.cs b/OpenTabletDriver.UX/TrayIcon.cs deleted file mode 100644 index 6fbf741dd..000000000 --- a/OpenTabletDriver.UX/TrayIcon.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver.UX -{ - public class TrayIcon : IDisposable - { - public TrayIcon(MainForm window) - { - this.window = window; - - Indicator = new TrayIndicator - { - Title = "OpenTabletDriver", - Image = App.Logo - }; - - RefreshMenuItems(); - - Indicator.Activated += (object sender, System.EventArgs e) => - { - window.Show(); - window.BringToFront(); - }; - } - - public TrayIndicator Indicator { get; } - private MainForm window; - - public void Dispose() - { - Indicator.Hide(); - Indicator.Dispose(); - } - - public void RefreshMenuItems() - { - var showWindow = new ButtonMenuItem - { - Text = "Show Window" - }; - showWindow.Click += (sender, e) => - { - window.Show(); - window.BringToFront(); - }; - - var close = new ButtonMenuItem - { - Text = "Close" - }; - close.Click += (sender, e) => window.Close(); - - var items = new List(); - var presets = AppInfo.PresetManager.GetPresets(); - - if (presets.Count != 0) - { - foreach (var preset in presets) - { - var presetItem = new ButtonMenuItem - { - Text = preset.Name - }; - presetItem.Click += MainForm.PresetButtonHandler; - - items.Add(presetItem); - } - - items.Add(new SeparatorMenuItem()); - } - - items.Add(showWindow); - items.Add(close); - - Indicator.Menu = new ContextMenu(items); - } - } -} diff --git a/OpenTabletDriver.UX/UXServiceCollection.cs b/OpenTabletDriver.UX/UXServiceCollection.cs new file mode 100644 index 000000000..95a07398d --- /dev/null +++ b/OpenTabletDriver.UX/UXServiceCollection.cs @@ -0,0 +1,33 @@ +using Eto.Forms; +using Microsoft.Extensions.DependencyInjection; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Platform.Keyboard; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Dialogs; +using OpenTabletDriver.UX.Services; + +namespace OpenTabletDriver.UX +{ + using static ServiceDescriptor; + using static DependencyInjectionExtensions; + + public class UXServiceCollection : ClientServiceCollection + { + private static readonly IEnumerable RequiredServices = new[] + { + Transient(), + Transient(p => p.GetRequiredService().PluginManager), + Transient(p => p.GetRequiredService().PluginFactory), + Singleton(), + Singleton(), + Singleton(), + Transient() + }; + + public UXServiceCollection(App app) + { + this.AddServices(RequiredServices); + this.AddSingleton(app); + } + } +} diff --git a/OpenTabletDriver.UX/Utilities/ExpressionMemberAccessor.cs b/OpenTabletDriver.UX/Utilities/ExpressionMemberAccessor.cs new file mode 100644 index 000000000..4bc529a66 --- /dev/null +++ b/OpenTabletDriver.UX/Utilities/ExpressionMemberAccessor.cs @@ -0,0 +1,31 @@ +using System.Linq.Expressions; + +namespace OpenTabletDriver.UX.Utilities +{ + public class ExpressionMemberAccessor : ExpressionVisitor + { + private readonly Stack _expressions = new Stack(); + + /// + /// Accesses a member in an expression from another expression. + /// + /// The source expression in which to access the child member. + /// The child member to access. + public Expression AccessMember(Expression parent, Expression child) + { + Visit(child); + + var final = parent; + while (_expressions.TryPop(out var next)) + final = Expression.MakeMemberAccess(final, next.Member); + + return final; + } + + protected override Expression VisitMember(MemberExpression node) + { + _expressions.Push(node); + return base.VisitMember(node); + } + } +} diff --git a/OpenTabletDriver.UX/ViewModels/DialogModel.cs b/OpenTabletDriver.UX/ViewModels/DialogModel.cs new file mode 100644 index 000000000..1c8fbcc27 --- /dev/null +++ b/OpenTabletDriver.UX/ViewModels/DialogModel.cs @@ -0,0 +1,16 @@ +using Eto.Forms; + +namespace OpenTabletDriver.UX.ViewModels +{ + public class DialogModel where T : class + { + public DialogModel(DialogResult result, T? model = null) + { + Result = result; + Model = model; + } + + public DialogResult Result { get; } + public T? Model { get; } + } +} diff --git a/OpenTabletDriver.UX/ViewModels/SettingsViewModel.cs b/OpenTabletDriver.UX/ViewModels/SettingsViewModel.cs new file mode 100644 index 000000000..584d28bb3 --- /dev/null +++ b/OpenTabletDriver.UX/ViewModels/SettingsViewModel.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using OpenTabletDriver.Desktop.Reflection; + +namespace OpenTabletDriver.UX.ViewModels +{ + public class SettingsViewModel : NotifyPropertyChanged + { + public SettingsViewModel(PluginSettings settings, TypeInfo type) + { + Settings = settings; + Type = type; + } + + public PluginSettings Settings { get; } + public TypeInfo Type { get; } + } +} diff --git a/OpenTabletDriver.UX/Windows/AreaConverterDialog.cs b/OpenTabletDriver.UX/Windows/AreaConverterDialog.cs deleted file mode 100644 index ea57a57e7..000000000 --- a/OpenTabletDriver.UX/Windows/AreaConverterDialog.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Reflection; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows -{ - public class AreaConverterDialog : ChildDialog - { - public AreaConverterDialog() - : base(Application.Instance.MainForm) - { - base.Title = "Convert Area..."; - - topGroup = new Group - { - Content = top = new FloatNumberBox() - { - PlaceholderText = "0" - } - }; - leftGroup = new Group - { - Content = left = new FloatNumberBox() - { - PlaceholderText = "0" - } - }; - bottomGroup = new Group - { - Content = bottom = new FloatNumberBox() - { - PlaceholderText = "0" - } - }; - rightGroup = new Group - { - Content = right = new FloatNumberBox() - { - PlaceholderText = "0" - } - }; - - this.Content = new StackLayout - { - Orientation = Orientation.Vertical, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Padding = 5, - Spacing = 5, - Items = - { - new Group - { - Text = "Converter", - Content = converterList, - Orientation = Orientation.Horizontal - }, - new StackLayoutItem - { - Expand = true, - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - topGroup, - leftGroup - } - } - }, - new StackLayoutItem - { - Expand = true, - Control = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - bottomGroup, - rightGroup - } - } - } - } - } - }, - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Right, - Control = applyButton = new Button((sender, e) => ConvertArea()) - { - Text = "Apply", - Enabled = false - } - } - } - }; - - converterList.SelectedIndexChanged += (sender, e) => OnSelectionChanged(); - - Application.Instance.AsyncInvoke(async () => - { - var tablets = await App.Driver.Instance.GetTablets(); - var targetProfile = App.Current.Settings.Profiles.FirstOrDefault(p => p.AbsoluteModeSettings.Tablet == this.DataContext); - var tablet = tablets.FirstOrDefault(t => t.Properties.Name == targetProfile.Tablet); - Select(tablet); - }); - } - - private readonly TypeDropDown converterList = new TypeDropDown(); - private Group topGroup, leftGroup, bottomGroup, rightGroup; - private FloatNumberBox top, left, bottom, right; - private Button applyButton; - private TabletReference selectedTablet; - - protected void OnSelectionChanged() - { - // Simpler than binding with MVVM since .ctor() is being called. - if (converterList.ConstructSelectedType() is IAreaConverter converter) - { - topGroup.Text = converter.Top; - leftGroup.Text = converter.Left; - bottomGroup.Text = converter.Bottom; - rightGroup.Text = converter.Right; - applyButton.Enabled = true; - } - else - { - topGroup.Text = string.Empty; - leftGroup.Text = string.Empty; - bottomGroup.Text = string.Empty; - rightGroup.Text = string.Empty; - applyButton.Enabled = false; - } - } - - protected void ConvertArea() - { - var converter = this.converterList.ConstructSelectedType(); - var convertedArea = converter.Convert(selectedTablet, top.Value, left.Value, bottom.Value, right.Value); - - (this.DataContext as AreaSettings).Area = convertedArea; - this.Close(); - } - - private void Select(TabletReference tablet) - { - if (tablet.Identifiers?.FirstOrDefault()?.VendorID is int vendorId) - { - var vendor = (DeviceVendor)vendorId; - converterList.Select(t => t.Vendor.HasFlag(vendor)); - applyButton.Enabled = true; - selectedTablet = tablet; - } - else - { - // Deselect if no tablet is detected - converterList.SelectedIndex = -1; - selectedTablet = null; - - MessageBox.Show("No tablet detected.", MessageBoxType.Error); - this.Close(); - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Bindings/AdvancedBindingEditorDialog.cs b/OpenTabletDriver.UX/Windows/Bindings/AdvancedBindingEditorDialog.cs deleted file mode 100644 index 64456a9e0..000000000 --- a/OpenTabletDriver.UX/Windows/Bindings/AdvancedBindingEditorDialog.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.UX.Controls; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Reflection; -using IBinding = OpenTabletDriver.Plugin.IBinding; - -namespace OpenTabletDriver.UX.Windows.Bindings -{ - public class AdvancedBindingEditorDialog : Dialog - { - public AdvancedBindingEditorDialog(PluginSettingStore currentBinding = null) - { - Title = "Advanced Binding Editor"; - Result = currentBinding; - Padding = 5; - - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Width = 500, - Height = 450, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new Group - { - Text = "Type", - Content = bindingTypeDropDown = new TypeDropDown() - }, - settingStoreEditor - } - } - }, - new StackLayoutItem - { - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new Button(ClearBinding) - { - Text = "Clear" - } - }, - new StackLayoutItem - { - Expand = true, - Control = new Button(ApplyBinding) - { - Text = "Apply" - } - } - } - } - } - } - }; - - bindingTypeDropDown.SelectedItemBinding.Convert(t => new PluginSettingStore(t)).Bind(settingStoreEditor.StoreBinding); - bindingTypeDropDown.SelectedItem = currentBinding?.GetTypeInfo(); - } - - private TypeDropDown bindingTypeDropDown; - private PluginSettingStoreEditor settingStoreEditor = new PluginSettingStoreEditor(); - - private void ClearBinding(object sender, EventArgs e) - { - Close(null); - } - - private void ApplyBinding(object sender, EventArgs e) - { - if (bindingTypeDropDown.SelectedItem == null) - { - Close(null); - return; - } - - Close(settingStoreEditor.Store); - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Bindings/BindingEditorDialog.cs b/OpenTabletDriver.UX/Windows/Bindings/BindingEditorDialog.cs deleted file mode 100644 index b054df990..000000000 --- a/OpenTabletDriver.UX/Windows/Bindings/BindingEditorDialog.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Text; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Binding; -using OpenTabletDriver.Desktop.Reflection; -using OpenTabletDriver.Plugin.Platform.Pointer; - -namespace OpenTabletDriver.UX.Windows.Bindings -{ - public class BindingEditorDialog : Dialog - { - public BindingEditorDialog(PluginSettingStore currentBinding = null) - { - Title = "Binding Editor"; - Result = currentBinding; - - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Padding = new Padding(5), - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = bindingController = new BindingController - { - Store = currentBinding, - Width = 300, - Height = 150 - } - }, - new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new Button(ClearBinding) - { - Text = "Clear" - } - }, - new StackLayoutItem - { - Expand = true, - Control = new Button(ApplyBinding) - { - Text = "Apply" - } - } - } - } - }, - }; - } - - private BindingController bindingController; - - private void ClearBinding(object sender, EventArgs e) - { - Close(null); - } - - private void ApplyBinding(object sender, EventArgs e) - { - Close(bindingController.Store); - } - - private static string ParseMouseButton(MouseEventArgs e) - { - switch (e.Buttons) - { - case MouseButtons.Primary: - return nameof(MouseButton.Left); - case MouseButtons.Middle: - return nameof(MouseButton.Middle); - case MouseButtons.Alternate: - return nameof(MouseButton.Right); - default: - return nameof(MouseButton.None); - } - } - - private class BindingController : TextArea - { - public BindingController() - { - TextAlignment = TextAlignment.Center; - ReadOnly = true; - ToolTip = TOOLTIP; - Text = TOOLTIP; - } - - private const string TOOLTIP = "Press a key, combination of keys, or a mouse button."; - - private PluginSettingStore store; - public PluginSettingStore Store - { - set - { - this.store = value; - Refresh(); - } - get => this.store; - } - - public void Refresh() - { - this.Text = Store?.GetHumanReadableString() ?? TOOLTIP; - } - - protected override void OnKeyDown(KeyEventArgs e) - { - PluginSettingStore store; - Keys keys = e.KeyData; - - if (keys == Keys.None) - return; - - if (keys.HasFlag(Keys.Alt | Keys.LeftAlt) || keys.HasFlag(Keys.Alt | Keys.RightAlt)) - keys &= ~Keys.Alt; - else if (keys.HasFlag(Keys.Control | Keys.LeftControl) || keys.HasFlag(Keys.Control | Keys.RightControl)) - keys &= ~Keys.Control; - else if (keys.HasFlag(Keys.Shift | Keys.LeftShift) || keys.HasFlag(Keys.Shift | Keys.RightShift)) - keys &= ~Keys.Shift; - else if (keys.HasFlag(Keys.Application | Keys.LeftApplication) || keys.HasFlag(Keys.Application | Keys.RightApplication)) - keys &= ~Keys.Application; - - if ((keys & Keys.ModifierMask) == 0) - { - store = new PluginSettingStore(typeof(KeyBinding)); - store[nameof(KeyBinding.Key)].SetValue(keys.ToString()); - } - else - { - store = new PluginSettingStore(typeof(MultiKeyBinding)); - store[nameof(MultiKeyBinding.Keys)].SetValue(CreateShortcutString(keys)); - } - this.Store = store; - } - - protected override void OnMouseDown(MouseEventArgs e) - { - var store = new PluginSettingStore(typeof(MouseBinding)); - store[nameof(MouseBinding.Button)].SetValue(ParseMouseButton(e)); - this.Store = store; - } - - private static void AppendSeparator(StringBuilder sb, string separator, string text) - { - if (sb.Length > 0) - sb.Append(separator); - sb.Append(text); - } - - private static string CreateShortcutString(Keys keys) - { - var sb = new StringBuilder(); - - if (keys.HasFlag(Keys.Application)) - AppendSeparator(sb, "+", nameof(Keys.Application)); - if (keys.HasFlag(Keys.Control)) - AppendSeparator(sb, "+", nameof(Keys.Control)); - if (keys.HasFlag(Keys.Shift)) - AppendSeparator(sb, "+", nameof(Keys.Shift)); - if (keys.HasFlag(Keys.Alt)) - AppendSeparator(sb, "+", nameof(Keys.Alt)); - - var mainKey = keys & Keys.KeyMask; - AppendSeparator(sb, "+", mainKey.ToString()); - - return sb.ToString(); - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/ConfigurationEditor.cs b/OpenTabletDriver.UX/Windows/ConfigurationEditor.cs new file mode 100644 index 000000000..ae77eb44b --- /dev/null +++ b/OpenTabletDriver.UX/Windows/ConfigurationEditor.cs @@ -0,0 +1,654 @@ +using System.Collections.Immutable; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reflection; +using Eto; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Tablet; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls; +using OpenTabletDriver.UX.Controls.Numeric; +using OpenTabletDriver.UX.Utilities; +using Container = Eto.Forms.Container; + +namespace OpenTabletDriver.UX.Windows +{ + public sealed class ConfigurationEditor : DesktopForm + { + private readonly IDriverDaemon _driverDaemon; + private readonly ListBox _configsList; + + // TODO: Add, Remove, Generate buttons + public ConfigurationEditor(IDriverDaemon driverDaemon) + { + _driverDaemon = driverDaemon; + + Title = "Configuration Editor"; + + var placeholder = new Placeholder + { + Text = "No configuration selected." + }; + + var splitter = new Splitter + { + Panel1MinimumSize = 250, + Panel1 = _configsList = new ListBox(), + Panel2 = placeholder + }; + + _configsList.ItemTextBinding = Binding.Property(c => c.Name); + Refresh().Run(); + + Content = splitter; + + var digitizerEditors = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Items = + { + TextBoxFor((TabletConfiguration c) => c.Specifications.Digitizer!.Width), + TextBoxFor((TabletConfiguration c) => c.Specifications.Digitizer!.Height), + TextBoxFor((TabletConfiguration c) => c.Specifications.Digitizer!.MaxX), + TextBoxFor((TabletConfiguration c) => c.Specifications.Digitizer!.MaxY) + } + }; + + var digitizerSpecifications = SpecificationEditorsFor(c => c.Specifications.Digitizer); + var penSpecifications = SpecificationEditorsFor(c => c.Specifications.Pen); + var auxiliarySpecifications = SpecificationEditorsFor(c => c.Specifications.AuxiliaryButtons); + var mouseSpecifications = SpecificationEditorsFor(c => c.Specifications.MouseButtons); + var touchSpecifications = SpecificationEditorsFor(c => c.Specifications.Touch); + + var digitizerIdentifiers = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5 + }; + + var auxiliaryIdentifiers = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5 + }; + + var attributes = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5 + }; + + DataContextChanged += delegate + { + AssignIdentifierEditors(digitizerIdentifiers, c => c.DigitizerIdentifiers); + AssignIdentifierEditors(auxiliaryIdentifiers, c => c.AuxiliaryDeviceIdentifiers); + AssignDictionaryEditor(attributes, c => c.Attributes); + }; + + var editorPanel = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5, + Items = + { + TextBoxFor((TabletConfiguration c) => c.Name), + new Expander + { + Header = LabelFor((TabletConfiguration c) => c.Specifications), + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5, + Items = + { + digitizerSpecifications, + penSpecifications, + auxiliarySpecifications, + mouseSpecifications, + touchSpecifications + } + } + }, + new Expander + { + Header = LabelFor((TabletConfiguration c) => c.DigitizerIdentifiers), + Content = digitizerIdentifiers + }, + new Expander + { + Header = LabelFor((TabletConfiguration c) => c.AuxiliaryDeviceIdentifiers), + Content = auxiliaryIdentifiers + }, + new Expander + { + Header = LabelFor((TabletConfiguration c) => c.Attributes), + Content = attributes + } + } + }; + + DataContextBinding.Bind(_configsList.SelectedValueBinding); + DataContextChanged += (_, _) => splitter.Panel2 = DataContext is TabletConfiguration ? editorPanel : placeholder; + + var modifier = Application.Instance.CommonModifier; + + var fileMenu = new ButtonMenuItem + { + Text = "&File", + Items = + { + new AppCommand("Discard", Refresh), + new AppCommand("Load...", LoadDialog, modifier | Keys.O), + new AppCommand("Save...", SaveDialog, modifier | Keys.S) + } + }; + + Menu = new MenuBar + { + Items = + { + fileMenu + }, + QuitItem = new AppCommand("Close", Close, Keys.Escape) + }; + } + + private static BindableBinding GetEnabledBinding(T control) where T : Control + { + return new BindableBinding( + control, + c => c.Enabled, + (c, v) => c.Enabled = v.GetValueOrDefault(), + (c, h) => c.EnabledChanged += h, + (c, h) => c.EnabledChanged -= h + ); + } + + /// + /// Refreshes the configuration editor from the configuration directory. + /// + private async Task Refresh() + { + var appInfo = await _driverDaemon.GetApplicationInfo(); + LoadConfigurations(appInfo.ConfigurationDirectory); + } + + private Container SpecificationEditorsFor(Expression> expression) where T : class, new() + { + var type = typeof(T); + + var editors = new StackLayout + { + Orientation = Orientation.Vertical, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5 + }; + + // this.((TabletConfiguration)DataContext). + var thisExpr = Expression.Constant(this); + var dataContextExpr = Expression.Convert(Expression.Property(thisExpr, nameof(DataContext)), typeof(TabletConfiguration)); + var baseExpr = new ExpressionMemberAccessor().AccessMember(dataContextExpr, expression); + var getExpr = Expression.Lambda>(baseExpr); + var get = getExpr.Compile(); + + foreach (var property in type.GetProperties()) + { + var binding = new DelegateBinding( + () => + { + if (DataContext != null && get() is T t) + return property.GetValue(t)?.ToString(); + return string.Empty; + }, + v => + { + if (get() is T t) + property.SetValue(t, Convert.ChangeType(v, property.PropertyType)); + }, + h => DataContextChanged += h, + h => DataContextChanged -= h + ); + + var textBox = new TextBox(); + textBox.TextBinding.Bind(binding); + + var name = property.GetCustomAttribute()?.DisplayName ?? property.Name; + var control = new LabeledGroup(name, textBox); + + editors.Items.Add(control); + } + + return new Expander + { + Header = LabelFor(expression), + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Spacing = 5, + Padding = 5, + Items = + { + ToggleFor(expression, editors), + editors + } + } + }; + } + + /// + /// Updates all items in a to target an expression with editor controls. + /// + /// + /// + private void AssignIdentifierEditors(StackLayout layout, Expression>> expression) + { + // Keeps items open whenever this function is re-invoked (remove button invocation) + var openItemsQuery = from item in layout.Items + let container = item.Control as Container + where container is not null + let expander = container.FindChild() + where expander.Expanded + select ((Label) expander.Header).Text; + + var openItems = openItemsQuery.ToImmutableArray(); + + layout.Items.Clear(); + + if (DataContext is not TabletConfiguration configuration) + return; + + var get = expression.Compile(); + for (var i = 0; i < get(configuration).Count; i++) + { + var control = IdentifierEditorForIndex(i, expression); + var expander = control.FindChild(); + if (expander.Header is Label label) + expander.Expanded = openItems.Any(t => t == label.Text); + + layout.Items.Add(control); + } + + var addButton = new Button + { + Text = "+" + }; + addButton.Click += delegate + { + var instance = get((TabletConfiguration) DataContext); + var identifier = new DeviceIdentifier(); + instance.Add(identifier); + + var index = instance.IndexOf(identifier); + var control = IdentifierEditorForIndex(index, expression); + + layout.Items.Insert(index, control); + }; + layout.Items.Add(new StackLayoutItem(addButton, HorizontalAlignment.Right)); + } + + /// + /// Creates an identifier editor for the target index + /// + /// The target index + /// + /// An expression pointing within the + /// to a list of s. + /// + private Container IdentifierEditorForIndex(int i, Expression>> expression) + { + var vendorID = HexEditorFor((DeviceIdentifier d) => d.VendorID); + var productID = HexEditorFor((DeviceIdentifier d) => d.ProductID); + var inputReportLength = TextBoxFor((DeviceIdentifier d) => d.InputReportLength); + var outputReportLength = TextBoxFor((DeviceIdentifier d) => d.OutputReportLength); + var reportParser = TextBoxFor((DeviceIdentifier d) => d.ReportParser); + + var expander = new Expander + { + Header = $"Identifier {i}", + Content = new StackLayout + { + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = 5, + Spacing = 5, + Items = + { + vendorID, + productID, + inputReportLength, + outputReportLength, + reportParser + } + } + }; + + var removeButton = new Button + { + Text = "-" + }; + + var control = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Stretch, + Items = + { + new StackLayoutItem(expander, true), + new StackLayoutItem(removeButton, VerticalAlignment.Bottom) + } + }; + + var get = expression.Compile(); + control.BindDataContext(c => c.DataContext, (TabletConfiguration c) => get(c)[i]); + + removeButton.Click += delegate + { + var parentLayout = control.FindParent()!; + parentLayout.Items.RemoveAt(i); + get((TabletConfiguration)DataContext).RemoveAt(i); + + // Regenerate layout to fix bindings + AssignIdentifierEditors(parentLayout, expression); + }; + + return control; + } + + private void AssignDictionaryEditor(StackLayout layout, Expression>> expression) + { + layout.Items.Clear(); + + if (DataContext is not TabletConfiguration config) + return; + + var get = expression.Compile(); + + foreach (var pair in get(config)) + { + var control = DictionaryItemForKey(pair.Key, expression); + layout.Items.Add(control); + } + + var addButton = new Button + { + Text = "+" + }; + addButton.Click += delegate + { + var dict = get((TabletConfiguration) DataContext); + if (dict.TryAdd(string.Empty, string.Empty)) + { + var control = DictionaryItemForKey(string.Empty, expression); + layout.Items.Insert(layout.Items.Count - 1, control); + } + }; + layout.Items.Add(new StackLayoutItem(addButton, HorizontalAlignment.Right)); + } + + private Container DictionaryItemForKey(string key, Expression>> expression) + { + var get = expression.Compile(); + + var keyBinding = new DelegateBinding( + () => key, + newKey => + { + var dict = get((TabletConfiguration) DataContext); + var value = string.Empty; + + if (dict.ContainsKey(key)) + { + value = dict[key]; + dict.Remove(key); + } + + dict.Add(newKey, value); + key = newKey; + } + ); + + var valueBinding = new DelegateBinding( + () => get((TabletConfiguration) DataContext)[key], + v => + { + var dict = get((TabletConfiguration) DataContext); + if (dict.ContainsKey(key)) + dict[key] = v; + else + dict.Add(key, v); + } + ); + + var keyBox = new TextBox(); + keyBox.TextBinding.Bind(keyBinding); + keyBox.TextChanging += (sender, args) => + { + var dict = get((TabletConfiguration) DataContext); + if (dict.ContainsKey(args.NewText)) + args.Cancel = true; + }; + + var valueBox = new TextBox(); + valueBox.TextBinding.Bind(valueBinding); + + var removeButton = new Button + { + Text = "-" + }; + + var control = new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem(keyBox, true), + new StackLayoutItem(valueBox, true), + removeButton + } + }; + + removeButton.Click += delegate + { + var layout = control.FindParent(); + var instance = get((TabletConfiguration) DataContext); + instance.Remove(keyBinding.DataValue); + layout.Items.Remove(control); + + // Regenerate layout to fix bindings + AssignDictionaryEditor(layout, expression); + }; + + return control; + } + + /// + /// Creates a label for an expression, using friendly names where possible. + /// + /// Expression pointing to the target member. + /// The source type. + /// The target value type. + private static Control LabelFor(Expression> expression) + { + return new Panel + { + Padding = new Padding(3, 0, 0, 0), + Content = new Label + { + Text = expression.GetFriendlyName() + } + }; + } + + /// + /// Creates a toggle bound to an expression. + /// + /// + /// When the checked state is changed, it will update the binding with a new value. + /// When unchecked, the bound value is set to null. + /// When checked, the bound value is set to a new instance of + /// + /// The expression to bind to. + /// The control to enable when checked. + /// The expected type. + /// The target member type in the expression. + private CheckBox ToggleFor(Expression> expression, Control control) where TValue : class, new() + { + var toggle = new CheckBox + { + Text = "Enable" + }; + + // this.((T)DataContext). + var thisExpr = Expression.Constant(this); + var dataContextExpr = Expression.Convert(Expression.Property(thisExpr, nameof(DataContext)), typeof(T)); + var baseExpr = new ExpressionMemberAccessor().AccessMember(dataContextExpr, expression); + + // v => this.((T)DataContext). = v + var parameter = Expression.Parameter(typeof(TValue)); + var setExpr = Expression.Assign(baseExpr, parameter); + + var set = Expression.Lambda>(setExpr, parameter).Compile(); + var get = Expression.Lambda>(baseExpr).Compile(); + + toggle.CheckedBinding.Convert( + b => + { + if (DataContext is null) + return default; + set(b.GetValueOrDefault() ? new TValue() : null); + return get(); + }, + v => v != null + ).BindDataContext(expression); + + toggle.CheckedBinding.Bind(GetEnabledBinding(control)); + toggle.CheckedChanged += (_, _) => control.UpdateBindings(); + + return toggle; + } + + /// + /// Creates a TextBox bound to an expression. + /// + /// An expression pointing to a member. + /// The type. + private Control TextBoxFor(Expression> expression) + { + var textBox = new TextBox(); + textBox.TextBinding.Convert(s => s, (object? o) => o?.ToString()).BindDataContext(expression); + return new LabeledGroup(expression.GetFriendlyName(), textBox); + } + + /// + /// Creates a hexadecimal number editor bound to an expression. + /// + /// An expression pointing to a member. + /// The type. + private Control HexEditorFor(Expression> expression) + { + var hex = new HexNumberBox(); + hex.ValueBinding.BindDataContext(expression); + return new LabeledGroup(expression.GetFriendlyName(), hex); + } + + /// + /// Load dialog for loading tablet configurations. + /// + private void LoadDialog() + { + var dialog = new SelectFolderDialog + { + Title = "Load tablet configurations...", + Directory = EtoEnvironment.GetFolderPath(EtoSpecialFolder.Documents) + }; + + if (dialog.ShowDialog(this) == DialogResult.Ok) + LoadConfigurations(dialog.Directory); + } + + /// + /// Save dialog for saving tablet configurations. + /// + private void SaveDialog() + { + var dialog = new SelectFolderDialog + { + Title = "Save tablet configurations...", + Directory = EtoEnvironment.GetFolderPath(EtoSpecialFolder.Documents) + }; + + if (dialog.ShowDialog(this) == DialogResult.Ok) + SaveConfigurations(dialog.Directory); + } + + /// + /// Internal method for loading configurations into the data store. + /// + /// The source directory. + private void LoadConfigurations(string path) + { + var oldValue = _configsList.SelectedValue; + + _configsList.Enabled = false; + + var configs = EnumerateConfigurations(path).ToList(); + _configsList.DataStore = configs; + + _configsList.Enabled = true; + if (oldValue is TabletConfiguration config) + _configsList.SelectedIndex = configs.FindIndex(c => c?.Name == config.Name); + } + + /// + /// Internal method for saving configurations into a directory. + /// + /// The directory to save to. + /// + private void SaveConfigurations(string path) + { + if (_configsList.DataStore is not IEnumerable configs) + throw new InvalidOperationException(); + + foreach (var config in configs) + { + var spaceIndex = config.Name.IndexOf(' '); + var manufacturer = config.Name[..spaceIndex]; + var tabletName = config.Name[spaceIndex..].TrimStart(); + + var configPath = Path.Join(path, manufacturer, $"{tabletName}.json"); + var file = new FileInfo(configPath); + + if (!file.Directory!.Exists) + file.Directory.Create(); + + Serialization.Serialize(file, config); + } + } + + /// + /// Enumerates configurations from a directory path. + /// + /// The directory to enumerate for s. + private static IEnumerable EnumerateConfigurations(string path) + { + var dir = new DirectoryInfo(path); + if (!dir.Exists) + return Array.Empty(); + + return from file in dir.EnumerateFiles("*.json", SearchOption.AllDirectories) + let config = Serialization.Deserialize(file) + orderby config.Name + select config; + } + } +} diff --git a/OpenTabletDriver.UX/Windows/Configurations/ConfigurationEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/ConfigurationEditor.cs deleted file mode 100644 index b6b96c409..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/ConfigurationEditor.cs +++ /dev/null @@ -1,361 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Dictionary; -using OpenTabletDriver.UX.Windows.Configurations.Controls; -using OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications; - -namespace OpenTabletDriver.UX.Windows.Configurations -{ - public class ConfigurationEditor : DesktopForm - { - public ConfigurationEditor() - : base(Application.Instance.MainForm) - { - base.Title = "Configuration Editor"; - base.ClientSize = new Size(910, 680); - - base.Content = new Splitter - { - Orientation = Orientation.Horizontal, - Panel1MinimumSize = 200, - Panel1 = this.configList, - Panel2 = new Scrollable - { - Content = this.configurationSettings, - Padding = new Padding(5) - }, - }; - - // MenuBar Commands - var quitCommand = new Command { MenuText = "Close", Shortcut = Application.Instance.CommonModifier | Keys.W }; - quitCommand.Executed += (sender, e) => Close(); - - var loadDirectory = new Command { MenuText = "Load configurations...", Shortcut = Application.Instance.CommonModifier | Keys.O }; - loadDirectory.Executed += (sender, e) => LoadConfigurationsDialog(); - - var saveDirectory = new Command { MenuText = "Save configurations", Shortcut = Application.Instance.CommonModifier | Keys.S }; - saveDirectory.Executed += (sender, e) => SaveConfigurationsDialog(new DirectoryInfo(AppInfo.Current.ConfigurationDirectory)); - - var saveToDirectory = new Command { MenuText = "Save configurations to...", Shortcut = Application.Instance.CommonModifier | Application.Instance.AlternateModifier | Keys.S }; - saveToDirectory.Executed += (sender, e) => SaveConfigurationsDialog(); - - var newConfiguration = new Command { ToolBarText = "New configuration", Shortcut = Application.Instance.CommonModifier | Keys.N }; - newConfiguration.Executed += (sender, e) => configList.CreateConfiguration(); - - var deleteConfiguration = new Command { ToolBarText = "Delete configuration" }; - deleteConfiguration.Executed += (sender, e) => configList.DeleteConfiguration(); - - var generateConfiguration = new Command { ToolBarText = "Generate configuration..." }; - generateConfiguration.Executed += async (sender, e) => await configList.GenerateConfiguration(); - - // Menu - base.Menu = new MenuBar - { - Items = - { - // File submenu - new ButtonMenuItem - { - Text = "&File", - Items = - { - loadDirectory, - saveDirectory, - saveToDirectory - } - } - }, - QuitItem = quitCommand - }; - - base.ToolBar = new ToolBar - { - Items = - { - newConfiguration, - deleteConfiguration, - generateConfiguration - } - }; - - this.configList.SelectedValueChanged += (sender, e) => configurationSettings.Configuration = SelectedConfiguration; - - Refresh(); - } - - public void Refresh() - { - var configDir = new DirectoryInfo(AppInfo.Current.ConfigurationDirectory); - var sortedConfigs = from config in ReadConfigurations(configDir) - orderby config.Name - select config; - - Configurations = new ObservableCollection(sortedConfigs); - SelectedIndex = 0; - } - - protected ObservableCollection Configurations - { - set => this.configList.Source = value; - get => this.configList.Source as ObservableCollection; - } - - protected int SelectedIndex - { - set => this.configList.SelectedIndex = value; - get => this.configList.SelectedIndex; - } - - protected TabletConfiguration SelectedConfiguration - { - set => this.configList.SelectedItem = value; - get => this.configList.SelectedItem; - } - - private ConfigurationList configList = new ConfigurationList(); - private ConfigurationSettings configurationSettings = new ConfigurationSettings(); - - private static readonly System.Text.RegularExpressions.Regex NameRegex = new System.Text.RegularExpressions.Regex("(?.+?) (?.+?)$"); - - private ObservableCollection ReadConfigurations(DirectoryInfo dir) - { - dir.Refresh(); - if (dir.Exists) - { - var configs = from file in dir.GetFiles("*.json", SearchOption.AllDirectories) - select Serialization.Deserialize(file); - return new ObservableCollection(configs); - } - else - { - return new ObservableCollection(); - } - } - - private void WriteConfigurations(IEnumerable configs, DirectoryInfo dir) - { - foreach (var config in configs) - { - var match = NameRegex.Match(config.Name); - var manufacturer = match.Groups["Manufacturer"].Value; - var tabletName = match.Groups["TabletName"].Value; - - var path = Path.Join(dir.FullName, manufacturer, string.Format("{0}.json", tabletName)); - var file = new FileInfo(path); - try - { - if (!file.Directory.Exists) - file.Directory.Create(); - Serialization.Serialize(file, config); - } - catch (UnauthorizedAccessException) - { - Log.Write("Configuration", $"OpenTabletDriver doesn't have permission to save persistent tablet config to {path}.", LogLevel.Error); - } - } - } - - private void LoadConfigurationsDialog() - { - var folderDialog = new SelectFolderDialog - { - Title = "Open configuration folder..." - }; - switch (folderDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - var dir = new DirectoryInfo(folderDialog.Directory); - Configurations = ReadConfigurations(dir); - break; - } - } - - /// - /// Save configurations to `dir` if present, else show a folder selection dialog. - /// - /// The directory to save to, can be null. - private void SaveConfigurationsDialog(DirectoryInfo dir = null) - { - if (Configurations == null) - { - MessageBox.Show(this, "There are no configurations to save.", MessageBoxType.Information); - return; - } - - if (dir == null) - { - var folderDialog = new SelectFolderDialog - { - Title = "Save configurations to..." - }; - switch (folderDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - dir = new DirectoryInfo(folderDialog.Directory); - break; - } - } - - WriteConfigurations(Configurations, dir); - } - - private class ConfigurationList : ListBox - { - public ConfigurationList() - { - this.ItemTextBinding = Binding.Property(t => t.Name); - this.SelectedIndex = 0; - } - - public void CreateConfiguration() - { - var config = new TabletConfiguration - { - Name = "New Tablet" - }; - AddConfiguration(config); - } - - public void AddConfiguration(TabletConfiguration config) - { - base.Source.Add(config); - base.SelectedIndex = base.Source.IndexOf(config); - } - - public void DeleteConfiguration() => DeleteConfiguration(this.SelectedItem); - - public void DeleteConfiguration(TabletConfiguration config) - { - base.SelectedIndex--; - base.Source.Remove(config); - } - - public async Task GenerateConfiguration() - { - var dialog = new DeviceListDialog(); - await dialog.InitializeAsync(); - if (await dialog.ShowModalAsync(this) is IDeviceEndpoint device) - { - try - { - var generatedConfig = new TabletConfiguration - { - Name = device.Manufacturer + " " + device.ProductName, - DigitizerIdentifiers = - { - new DeviceIdentifier - { - VendorID = device.VendorID, - ProductID = device.ProductID, - InputReportLength = (uint)device.InputReportLength, - OutputReportLength = (uint)device.OutputReportLength - } - } - }; - AddConfiguration(generatedConfig); - } - catch (Exception ex) - { - Log.Exception(ex); - } - } - } - } - - private class ConfigurationSettings : Panel - { - public ConfigurationSettings() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Items = - { - new Group - { - Text = "Name", - Orientation = Orientation.Horizontal, - Content = name = new TextBox() - }, - new Expander - { - Header = "Specifications", - Content = specificationEditor = new TabletSpecificationsEditor() - }, - new Expander - { - Header = "Digitizer Identifiers", - Content = digitizerIdentifierEditor = new DeviceIdentifierEditor() - }, - new Expander - { - Header = "Auxiliary Identifiers", - Content = auxiliaryIdentifierEditor = new DeviceIdentifierEditor() - }, - new Expander - { - Header = "Attributes", - Padding = new Padding(5, 5, 0, 5), - Content = attributeEditor = new StringDictionaryEditor() - } - } - }; - - name.TextBinding.Bind(ConfigurationBinding.Child(c => c.Name)); - specificationEditor.SpecificationsBinding.Bind(ConfigurationBinding.Child(c => c.Specifications)); - digitizerIdentifierEditor.ItemSourceBinding.Bind(ConfigurationBinding.Child>(c => c.DigitizerIdentifiers)); - auxiliaryIdentifierEditor.ItemSourceBinding.Bind(ConfigurationBinding.Child>(c => c.AuxilaryDeviceIdentifiers)); - attributeEditor.ItemSourceBinding.Bind(ConfigurationBinding.Child>(c => c.Attributes)); - } - - private TextBox name; - private TabletSpecificationsEditor specificationEditor; - private DeviceIdentifierEditor digitizerIdentifierEditor; - private DeviceIdentifierEditor auxiliaryIdentifierEditor; - private StringDictionaryEditor attributeEditor; - - private TabletConfiguration configuration; - public TabletConfiguration Configuration - { - set - { - this.configuration = value; - this.OnConfigurationChanged(); - } - get => this.configuration; - } - - public event EventHandler ConfigurationChanged; - - protected virtual void OnConfigurationChanged() => ConfigurationChanged?.Invoke(this, new EventArgs()); - - public BindableBinding ConfigurationBinding - { - get - { - return new BindableBinding( - this, - c => c.Configuration, - (c, v) => c.Configuration = v, - (c, h) => c.ConfigurationChanged += h, - (c, h) => c.ConfigurationChanged -= h - ); - } - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/DeviceIdentifierEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/DeviceIdentifierEditor.cs deleted file mode 100644 index e2420409d..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/DeviceIdentifierEditor.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Dictionary; -using OpenTabletDriver.UX.Controls.Generic.Reflection; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls -{ - public class DeviceIdentifierEditor : ModifiableConstructableItemList - { - protected override Control CreateControl(int index, DirectBinding itemBinding) - { - var entry = new DeviceIdentifierEntry(); - entry.EntryBinding.Bind(itemBinding); - - return new Panel - { - Padding = 5, - Content = new Expander - { - Header = $"Identifier {index + 1}", - Content = entry - } - }; - } - - private class DeviceIdentifierEntry : Panel where T : DeviceIdentifier - { - public DeviceIdentifierEntry() - { - this.Content = layout = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Padding = 5, - Spacing = 5, - Items = - { - new Group - { - Text = "Vendor ID", - Orientation = Orientation.Horizontal, - Content = vendorId = new HexNumberBox() - }, - new Group - { - Text = "Product ID", - Orientation = Orientation.Horizontal, - Content = productId = new HexNumberBox() - }, - new Group - { - Text = "Input Report Length", - Orientation = Orientation.Horizontal, - Content = inputReportLength = new UnsignedIntegerNumberBox() - }, - new Group - { - Text = "Output Report Length", - Orientation = Orientation.Horizontal, - Content = outputReportLength = new UnsignedIntegerNumberBox() - }, - new Group - { - Text = "Report Parser", - Orientation = Orientation.Horizontal, - Content = reportParser = new TypeDropDown>() - }, - new Expander - { - Header = "Feature Initialization Report", - Padding = 5, - Content = featureInitReport = new ReportEditor() - }, - new Expander - { - Header = "Output Initialization Report", - Padding = 5, - Content = outputInitReport = new ReportEditor() - }, - new Expander - { - Header = "Device Strings", - Padding = 5, - Content = deviceStrings = new DeviceStringEditor() - }, - new Expander - { - Header = "Initialization String Indexes", - Padding = 5, - Content = initializationStrings = new ByteListEditor() - } - } - }; - - vendorId.ValueBinding.Bind(EntryBinding.Child(e => e.VendorID)); - productId.ValueBinding.Bind(EntryBinding.Child(e => e.ProductID)); - - inputReportLength.ValueBinding.Bind( - EntryBinding.Child(e => e.InputReportLength).Convert( - c => c ?? 0, - v => v - ) - ); - - outputReportLength.ValueBinding.Bind( - EntryBinding.Child(e => e.OutputReportLength).Convert( - c => c ?? 0, - v => v - ) - ); - - reportParser.SelectedKeyBinding.Bind(EntryBinding.Child(e => e.ReportParser)); - - featureInitReport.ItemSourceBinding.Bind(EntryBinding.Child>(e => e.FeatureInitReport)); - outputInitReport.ItemSourceBinding.Bind(EntryBinding.Child>(e => e.OutputInitReport)); - deviceStrings.ItemSourceBinding.Bind(EntryBinding.Child>(e => e.DeviceStrings)); - initializationStrings.ItemSourceBinding.Bind(EntryBinding.Child>(e => e.InitializationStrings)); - } - - protected StackLayout layout; - - private HexNumberBox vendorId, productId; - private UnsignedIntegerNumberBox inputReportLength, outputReportLength; - private ReportEditor featureInitReport, outputInitReport; - private TypeDropDown> reportParser; - private DeviceStringEditor deviceStrings; - private ByteListEditor initializationStrings; - - private T entry; - public T Entry - { - set - { - this.entry = value; - this.OnEntryChanged(); - } - get => this.entry; - } - - public event EventHandler EntryChanged; - - protected virtual void OnEntryChanged() => EntryChanged?.Invoke(this, new EventArgs()); - - public BindableBinding, T> EntryBinding - { - get - { - return new BindableBinding, T>( - this, - c => c.Entry, - (c, v) => c.Entry = v, - (c, h) => c.EntryChanged += h, - (c, h) => c.EntryChanged -= h - ); - } - } - - private class ReportEditor : ModifiableItemList - { - protected override void AddNew() => Add(ItemSource.Count, new byte[0]); - - protected override Control CreateControl(int index, DirectBinding itemBinding) - { - MaskedTextBox arrayEditor = new HexByteArrayBox(); - arrayEditor.ValueBinding.Bind(itemBinding); - - return new Panel - { - Padding = new Padding(0, 0, 5, 0), - Content = arrayEditor - }; - } - } - - private class DeviceStringEditor : DictionaryEditor - { - protected override Control CreateControl(DirectBinding keyBinding, DirectBinding valueBinding) - { - var keyBox = new IntegerNumberBox(); - keyBox.ValueBinding.Bind(keyBinding.Convert( - b => (int)b, - i => (byte)i - )); - keyBox.TextChanging += (sender, e) => e.Cancel = byte.TryParse(e.NewText, out byte newByte) && ItemSource.Keys.Contains(newByte); - - var valueBox = new TextBox(); - valueBox.TextBinding.Bind(valueBinding); - - return new StackLayout - { - Orientation = Orientation.Horizontal, - Padding = new Padding(0, 0, 5, 0), - Spacing = 5, - Items = - { - keyBox, - new StackLayoutItem(valueBox, true) - } - }; - } - - protected override void AddNew() => Add(0, string.Empty); - } - - private class ByteListEditor : ModifiableConstructableItemList - { - protected override Control CreateControl(int index, DirectBinding itemBinding) - { - MaskedTextBox intBox = new IntegerNumberBox(); - intBox.ValueBinding.Bind( - itemBinding.Convert( - c => (int)c, - v => (byte)v - ) - ); - - return new Panel - { - Padding = new Padding(0, 0, 5, 0), - Content = intBox - }; - } - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/ButtonSpecificationsEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/ButtonSpecificationsEditor.cs deleted file mode 100644 index d9b338972..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/ButtonSpecificationsEditor.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications -{ - public class ButtonSpecificationsEditor : SpecificationsEditor - { - public ButtonSpecificationsEditor() - { - this.Content = new StackLayout - { - Spacing = 5, - Items = - { - new StackLayoutItem - { - Control = enable = new CheckBox - { - Text = "Enable", - } - }, - new Group - { - Text = "Button Count", - Orientation = Orientation.Horizontal, - Content = buttonCount = new UnsignedIntegerNumberBox() - } - } - }; - - enable.CheckedBinding.Cast().Bind( - SpecificationsBinding.Convert( - c => c != null, - v => v ? new ButtonSpecifications() : null - ) - ); - - enable.CheckedBinding.Bind(buttonCount, b => b.Enabled); - - buttonCount.ValueBinding.Bind(SpecificationsBinding.Child(b => b.ButtonCount)); - } - - private CheckBox enable; - private MaskedTextBox buttonCount; - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/DigitizerSpecificationsEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/DigitizerSpecificationsEditor.cs deleted file mode 100644 index f9b345817..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/DigitizerSpecificationsEditor.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications -{ - public class DigitizerSpecificationsEditor : SpecificationsEditor - { - public DigitizerSpecificationsEditor() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Padding = 5, - Items = - { - new StackLayoutItem - { - Control = enable = new CheckBox - { - Text = "Enable" - } - }, - new Group - { - Text = "Width (mm)", - Orientation = Orientation.Horizontal, - Content = width = new FloatNumberBox() - }, - new Group - { - Text = "Height (mm)", - Orientation = Orientation.Horizontal, - Content = height = new FloatNumberBox() - }, - new Group - { - Text = "Horizontal Resolution", - Orientation = Orientation.Horizontal, - Content = maxX = new FloatNumberBox() - }, - new Group - { - Text = "Vertical Resolution", - Orientation = Orientation.Horizontal, - Content = maxY = new FloatNumberBox() - } - } - }; - - enable.CheckedBinding.Cast().Bind( - SpecificationsBinding.Convert( - c => c != null, - v => v ? new DigitizerSpecifications() : null - ) - ); - enable.CheckedBinding.Bind(width, c => c.Enabled); - enable.CheckedBinding.Bind(height, c => c.Enabled); - enable.CheckedBinding.Bind(maxX, c => c.Enabled); - enable.CheckedBinding.Bind(maxY, c => c.Enabled); - - width.ValueBinding.Bind(SpecificationsBinding.Child(c => c.Width)); - height.ValueBinding.Bind(SpecificationsBinding.Child(c => c.Height)); - maxX.ValueBinding.Bind(SpecificationsBinding.Child(c => c.MaxX)); - maxY.ValueBinding.Bind(SpecificationsBinding.Child(c => c.MaxY)); - } - - private CheckBox enable; - private MaskedTextBox width, height, maxX, maxY; - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/PenSpecificationsEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/PenSpecificationsEditor.cs deleted file mode 100644 index 2622dcaa8..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/PenSpecificationsEditor.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Eto.Forms; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications -{ - public class PenSpecificationsEditor : SpecificationsEditor - { - public PenSpecificationsEditor() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Padding = 5, - Items = - { - new StackLayoutItem - { - Control = enable = new CheckBox - { - Text = "Enable" - } - }, - new Group - { - Text = "Max Pressure", - Orientation = Orientation.Horizontal, - Content = maxPressure = new UnsignedIntegerNumberBox() - }, - new Group - { - Text = "Button Count", - Orientation = Orientation.Horizontal, - Content = buttonCount = new UnsignedIntegerNumberBox() - } - } - }; - - enable.CheckedBinding.Cast().Bind( - SpecificationsBinding.Convert( - c => c != null, - v => v ? new PenSpecifications() : null - ) - ); - enable.CheckedBinding.Bind(maxPressure, c => c.Enabled); - enable.CheckedBinding.Bind(buttonCount, c => c.Enabled); - - maxPressure.ValueBinding.Bind(SpecificationsBinding.Child(c => c.MaxPressure)); - buttonCount.ValueBinding.Bind(SpecificationsBinding.Child(c => c.Buttons.ButtonCount)); - } - - private CheckBox enable; - private MaskedTextBox maxPressure; - private UnsignedIntegerNumberBox buttonCount; - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/SpecificationsEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/SpecificationsEditor.cs deleted file mode 100644 index f73541082..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/SpecificationsEditor.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications -{ - public abstract class SpecificationsEditor : Panel where T : class - { - private T specification; - public T Specifications - { - set - { - this.specification = value; - this.OnSpecificationsChanged(); - } - get => this.specification; - } - - public event EventHandler SpecificationsChanged; - - protected virtual void OnSpecificationsChanged() => SpecificationsChanged?.Invoke(this, new EventArgs()); - - public BindableBinding, T> SpecificationsBinding - { - get - { - return new BindableBinding, T>( - this, - c => c.Specifications, - (c, v) => c.Specifications = v, - (c, h) => c.SpecificationsChanged += h, - (c, h) => c.SpecificationsChanged -= h - ); - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/TabletSpecificationsEditor.cs b/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/TabletSpecificationsEditor.cs deleted file mode 100644 index 0b156e8bd..000000000 --- a/OpenTabletDriver.UX/Windows/Configurations/Controls/Specifications/TabletSpecificationsEditor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using Eto.Forms; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Configurations.Controls.Specifications -{ - public class TabletSpecificationsEditor : SpecificationsEditor - { - public TabletSpecificationsEditor() - { - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Spacing = 5, - Padding = 5, - Items = - { - new Expander - { - Header = "Digitizer", - Content = digitizer = new DigitizerSpecificationsEditor() - }, - new Expander - { - Header = "Pen", - Content = pen = new PenSpecificationsEditor() - }, - new Expander - { - Header = "Auxiliary Buttons", - Padding = 5, - Content = auxButtons = new ButtonSpecificationsEditor() - }, - new Expander - { - Header = "Touch", - Content = touch = new DigitizerSpecificationsEditor() - }, - new Expander - { - Header = "Mouse", - Padding = 5, - Content = mouseButtons = new ButtonSpecificationsEditor() - } - } - }; - - digitizer.SpecificationsBinding.Bind(SpecificationsBinding.Child(c => c.Digitizer)); - pen.SpecificationsBinding.Bind(SpecificationsBinding.Child(c => c.Pen)); - auxButtons.SpecificationsBinding.Bind(SpecificationsBinding.Child(c => c.AuxiliaryButtons)); - touch.SpecificationsBinding.Bind(SpecificationsBinding.Child(c => c.Touch)); - mouseButtons.SpecificationsBinding.Bind(SpecificationsBinding.Child(c => c.MouseButtons)); - } - - private DigitizerSpecificationsEditor digitizer, touch; - private PenSpecificationsEditor pen; - private ButtonSpecificationsEditor auxButtons; - private ButtonSpecificationsEditor mouseButtons; - } -} diff --git a/OpenTabletDriver.UX/Windows/DeviceListDialog.cs b/OpenTabletDriver.UX/Windows/DeviceListDialog.cs deleted file mode 100644 index c1f8e6d29..000000000 --- a/OpenTabletDriver.UX/Windows/DeviceListDialog.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Devices; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Generic.Text; - -namespace OpenTabletDriver.UX.Windows -{ - public class DeviceListDialog : Dialog - { - public DeviceListDialog() - { - Title = "Device List"; - ClientSize = new Size(960 - 100, 730 - 100); - MinimumSize = new Size(960 - 100, 730 - 100); - Icon = App.Logo.WithSize(App.Logo.Size); - - Content = new Splitter - { - Orientation = Orientation.Horizontal, - Panel1MinimumSize = 200, - Panel1 = deviceList = new ListBox(), - Panel2 = new Scrollable - { - Content = new StackLayout - { - Padding = new Padding(5), - Spacing = 5, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new Group - { - Text = "Friendly Name", - Orientation = Orientation.Horizontal, - Content = this.friendlyName = new TextBox() - }, - new Group - { - Text = "Manufacturer", - Orientation = Orientation.Horizontal, - Content = this.manufacturer = new TextBox() - }, - new Group - { - Text = "Product Name", - Orientation = Orientation.Horizontal, - Content = this.productName = new TextBox() - }, - new Group - { - Text = "Serial Number", - Orientation = Orientation.Horizontal, - Content = this.serialNumber = new TextBox() - }, - new Group - { - Text = "Vendor ID", - Orientation = Orientation.Horizontal, - Content = this.vendorId = new HexNumberBox() - }, - new Group - { - Text = "Product ID", - Orientation = Orientation.Horizontal, - Content = this.productId = new HexNumberBox() - }, - new Group - { - Text = "Input Report Length", - Orientation = Orientation.Horizontal, - Content = this.inputReportLength = new IntegerNumberBox() - }, - new Group - { - Text = "Output Report Length", - Orientation = Orientation.Horizontal, - Content = this.outputReportLength = new IntegerNumberBox() - }, - new Group - { - Text = "Feature Report Length", - Orientation = Orientation.Horizontal, - Content = this.featureReportLength = new IntegerNumberBox() - }, - new Group - { - Text = "Device Path", - Orientation = Orientation.Horizontal, - Content = this.devicePath = new TextBox() - } - } - } - } - }; - - deviceList.ItemTextBinding = Binding.Property(d => d.FriendlyName); - - var selectedItemBinding = deviceList.SelectedItemBinding; - this.friendlyName.TextBinding.Bind(selectedItemBinding.Child(c => c.FriendlyName)); - this.manufacturer.TextBinding.Bind(selectedItemBinding.Child(c => c.Manufacturer)); - this.productName.TextBinding.Bind(selectedItemBinding.Child(c => c.ProductName)); - this.serialNumber.TextBinding.Bind(selectedItemBinding.Child(c => c.SerialNumber)); - this.devicePath.TextBinding.Bind(selectedItemBinding.Child(c => c.DevicePath)); - this.vendorId.ValueBinding.Bind(selectedItemBinding.Child(c => c.VendorID)); - this.productId.ValueBinding.Bind(selectedItemBinding.Child(c => c.ProductID)); - this.inputReportLength.ValueBinding.Bind(selectedItemBinding.Child(c => c.InputReportLength)); - this.outputReportLength.ValueBinding.Bind(selectedItemBinding.Child(c => c.OutputReportLength)); - this.featureReportLength.ValueBinding.Bind(selectedItemBinding.Child(c => c.FeatureReportLength)); - - var select = new Command { ToolBarText = "Select" }; - select.Executed += (sender, e) => Close(deviceList.SelectedItem); - - ToolBar = new ToolBar - { - Items = - { - select - } - }; - } - - public async Task InitializeAsync() - { - deviceList.Source = (await App.Driver.Instance.GetDevices()) - .Where(d => d.CanOpen) - .ToList(); - } - - private ListBox deviceList; - private TextBox friendlyName, manufacturer, productName, serialNumber, devicePath; - private MaskedTextBox vendorId, productId, inputReportLength, outputReportLength, featureReportLength; - } -} diff --git a/OpenTabletDriver.UX/Windows/DeviceStringReader.cs b/OpenTabletDriver.UX/Windows/DeviceStringReader.cs deleted file mode 100644 index c58862c2a..000000000 --- a/OpenTabletDriver.UX/Windows/DeviceStringReader.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows -{ - public class DeviceStringReader : DesktopForm - { - public DeviceStringReader() - : base(Application.Instance.MainForm) - { - this.Title = "Device String Reader"; - this.Icon = App.Logo.WithSize(App.Logo.Size); - this.ClientSize = new Size(300, 250); - - var sendRequestButton = new Button - { - Text = "Send Request", - }; - - sendRequestButton.Click += async (_, _) => await SendRequestWithTimeout(stringIndexText.Text, - (s) => deviceStringText.Text = s, - (e) => MessageBox.Show($"Error: {e.Message}", MessageBoxType.Error), - () => MessageBox.Show(OperationTimedOut) - ); - - var sendRequestAllStringsButton = new Button - { - Text = "Dump All" - }; - - sendRequestAllStringsButton.Click += SendRequestAllStrings; - - this.vendorIdText = new NumericMaskedTextBox - { - PlaceholderText = DecimalStyle, - Width = NUMERICBOX_WIDTH - }; - this.productIdText = new NumericMaskedTextBox - { - PlaceholderText = DecimalStyle, - Width = NUMERICBOX_WIDTH - }; - this.stringIndexText = new NumericMaskedTextBox - { - PlaceholderText = "[1..255]", - Width = NUMERICBOX_WIDTH - }; - this.deviceStringText = new TextBox - { - PlaceholderText = "Device String", - ReadOnly = true - }; - - this.vendorIdCtrl = new Group("VendorID", vendorIdText, Orientation.Horizontal, false); - this.productIdCtrl = new Group("ProductID", productIdText, Orientation.Horizontal, false); - this.stringIndexCtrl = new Group("String Index", stringIndexText, Orientation.Horizontal, false); - - this.Content = new StackLayout - { - Padding = 5, - Spacing = 5, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - vendorIdCtrl, - productIdCtrl, - stringIndexCtrl, - new StackLayoutItem( - new StackLayout - { - Orientation = Orientation.Horizontal, - Items = - { - new Panel { Content = sendRequestButton, Padding = new Padding(5, 5) }, - new Panel { Content = sendRequestAllStringsButton, Padding = new Padding(5, 5) } - } - }, - HorizontalAlignment.Center - ), - new StackLayoutItem(deviceStringText, true) - } - }; - } - - private const int NUMERICBOX_WIDTH = 150; - private const string DecimalStyle = "Decimal Value"; - private const string StringIndex = "Index"; - private const string RequestTabletReplug = "Please replug the tablet, and then press OK to continue"; - private const string DisconnectionIndex = "Device disconnected"; - private const string OperationTimedOut = "Operation timed-out"; - - private async void SendRequestAllStrings(object sender, EventArgs args) - { - var stringDump = new StringBuilder(); - - for (int i = 1; i < 256; i++) - { - bool shouldRead = true; - await SendRequestWithTimeout($"{i}", - (str) => stringDump.AppendLine($"{StringIndex} {i}: {str}"), - (e) => shouldRead = AskReconnection(stringDump, i), - () => stringDump.AppendLine($"{StringIndex} {i}: {{ OTD: {OperationTimedOut} }}") - ); - - // If user pressed "Cancel" return immediately - if (!shouldRead) - return; - } - - var fileDialog = new SaveFileDialog - { - Title = "Save string dump to...", - Directory = new Uri(Eto.EtoEnvironment.GetFolderPath(Eto.EtoSpecialFolder.Documents)), - Filters = - { - new FileFilter("String dump", ".txt") - } - }; - - switch (fileDialog.ShowDialog(this)) - { - case DialogResult.Ok: - case DialogResult.Yes: - var file = new FileInfo(fileDialog.FileName); - if (file.Exists) - file.Delete(); - using (var fs = file.OpenWrite()) - using (var sw = new StreamWriter(fs)) - await sw.WriteAsync(stringDump); - break; - } - } - - private async Task SendRequestWithTimeout(string strIndex, Action action, Action error, Action timeoutAction) - { - var strVid = vendorIdText.Text; - var strPid = productIdText.Text; - var request = SendRequest(strIndex, strVid, strPid); - var timeout = Task.Delay(TimeSpan.FromSeconds(5)); - var completed = await Task.WhenAny(request, timeout); - if (completed == timeout) - { - timeoutAction(); - } - else - { - try - { - var str = await request; - action(str); - } - catch (Exception e) - { - error(e); - } - } - } - - private async Task SendRequest(string strIndex, string strVid, string strPid) - { - if (int.TryParse(strIndex, out var index) && index < 256 && index > 0) - { - if (int.TryParse(strVid, out var vid) && int.TryParse(strPid, out var pid)) - return await App.Driver.Instance.RequestDeviceString(vid, pid, index); - } - throw new ArgumentException("Invalid index"); - } - - private bool AskReconnection(StringBuilder stringDump, int i) - { - stringDump.AppendLine($"{StringIndex} {i}: {{ OTD: {DisconnectionIndex} }}"); - var result = MessageBox.Show(RequestTabletReplug, MessageBoxButtons.OKCancel); - return result == DialogResult.Ok; - } - - private readonly NumericMaskedTextBox vendorIdText, productIdText, stringIndexText; - private readonly TextBox deviceStringText; - private readonly Group vendorIdCtrl, productIdCtrl, stringIndexCtrl; - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/AreaEditorPage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/AreaEditorPage.cs deleted file mode 100644 index 49063a3cb..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/AreaEditorPage.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop.Profiles; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Controls.Output.Area; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("Area Editor")] - public class AreaEditorPage : StylizedPage - { - public AreaEditorPage() - { - this.Content = new StackedContent - { - new StackLayoutItem - { - Expand = true, - Control = new Group - { - Text = "Demo Area Editor", - Content = new RotationAreaEditor - { - Area = new AreaSettings - { - Width = 75, - Height = 75, - X = 75, - Y = 75, - Rotation = 15, - }, - Unit = "mm", - AreaBounds = new RectangleF[] - { - new RectangleF(0, 0, 150, 150) - } - } - } - }, - new StylizedText("This is the area editor.", SystemFonts.Bold(9), new Padding(0, 0, 0, 4)), - "You can right click the absolute output mode area editor for more options.", - "Aligning, resizing, and flipping your area can be done within this context menu.", - "Other options such as locking aspect ratio, locking input inside of the usable area are also found here.", - }; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/BindingPage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/BindingPage.cs deleted file mode 100644 index 7c24b4eb4..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/BindingPage.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("Bindings")] - public class BindingPage : StylizedPage - { - public BindingPage() - { - this.Content = new StackedContent - { - new PaddingSpacerItem(), - new StackLayoutItem - { - Expand = true, - Control = new Group - { - Text = "Demo", - Content = new StackedContent - { - new PaddingSpacerItem(), - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Stretch, - Control = new Group - { - Text = "Demo Binding", - Orientation = Orientation.Horizontal, - Content = new StackLayout - { - Orientation = Orientation.Horizontal, - Items = - { - new Panel - { - Width = 50 - }, - new DemoBindingDisplay - { - Width = 250 - } - } - } - } - }, - new PaddingSpacerItem() - } - } - }, - new StylizedText("This is the binding editor.", SystemFonts.Bold(9), new Padding(0, 0, 0, 4)), - "It allows you to set specific actions that OpenTabletDriver will perform when, for example, a tablet button is pressed.", - "Click on the left button to capture a mouse or keyboard binding.", - "Click on the right button to open the advanced binding editor, which allows you to use plugin bindings.", - new PaddingSpacerItem(), - }; - } - - private class DemoBindingDisplay : BindingDisplay - { - public DemoBindingDisplay() - { - Width = 512; - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/FAQPage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/FAQPage.cs deleted file mode 100644 index d5ebbaf84..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/FAQPage.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Eto.Drawing; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("Wiki")] - public class WikiPage : StylizedPage - { - public WikiPage() - { - this.Content = new StackedContent - { - new PaddingSpacerItem(), - new Bitmap(App.Logo.WithSize(150, 150)), - new StylizedText("Wiki", SystemFonts.Bold(12), new Padding(0, 0, 0, 8)), - "If you have any issues, check out the Wiki.", - "This can be found under the Help menu in the main window.", - new PaddingSpacerItem(), - }; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/PluginPage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/PluginPage.cs deleted file mode 100644 index 712e537a8..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/PluginPage.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Eto.Drawing; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("Plugins")] - public class PluginPage : StylizedPage - { - public PluginPage() - { - this.Content = new StackedContent - { - new PaddingSpacerItem(), - new Bitmap(App.Logo.WithSize(150, 150)), - new StylizedText("Plugins", SystemFonts.Bold(12), new Padding(0, 0, 0, 8)), - "Plugins can be downloaded from the plugin manager at your own risk.", - "The plugin manager can be found in the Plugins menu in the main window.", - "Filters modify the output of the tablet, an example of this is smoothing.", - "Using multiple filters at once can cause unexpected effects.", - "Tools don't directly interfere with or modify the output; they instead add new functionality to OpenTabletDriver.", - new PaddingSpacerItem() - }; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/SystemTrayPage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/SystemTrayPage.cs deleted file mode 100644 index 6796437fc..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/SystemTrayPage.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Eto.Drawing; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("System Tray")] - public class SystemTrayPage : StylizedPage - { - public SystemTrayPage() - { - this.Content = new StackedContent - { - new PaddingSpacerItem(), - new Bitmap(App.Logo.WithSize(150, 150)), - new StylizedText("System Tray", SystemFonts.Bold(12), new Padding(0, 0, 0, 8)), - "When minimized, OpenTabletDriver will run in the background.", - "The window can be restored from the system tray.", - new PaddingSpacerItem(), - }; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/Pages/WelcomePage.cs b/OpenTabletDriver.UX/Windows/Greeter/Pages/WelcomePage.cs deleted file mode 100644 index 79fd857a4..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/Pages/WelcomePage.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Eto.Drawing; -using OpenTabletDriver.UX.Attributes; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Greeter.Pages -{ - [PageName("Intro")] - public class WelcomePage : StylizedPage - { - public WelcomePage() - { - this.Content = new StackedContent - { - new PaddingSpacerItem(), - new Bitmap(App.Logo.WithSize(256, 256)), - new StylizedText("OpenTabletDriver Guide", SystemFonts.Bold(12), new Padding(0, 0, 0, 10)), - "Welcome to OpenTabletDriver!", - "OpenTabletDriver is an open source, cross platform, user mode tablet driver.", - new PaddingSpacerItem(), - }; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Greeter/StartupGreeterWindow.cs b/OpenTabletDriver.UX/Windows/Greeter/StartupGreeterWindow.cs deleted file mode 100644 index 480eee12f..000000000 --- a/OpenTabletDriver.UX/Windows/Greeter/StartupGreeterWindow.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Windows.Greeter.Pages; - -namespace OpenTabletDriver.UX.Windows.Greeter -{ - public class StartupGreeterWindow : ChildDialog - { - public StartupGreeterWindow() - : base(Application.Instance.MainForm) - { - base.Title = "OpenTabletDriver Guide"; - - var bounds = Application.Instance.MainForm.ClientSize; - var minWidth = Math.Min(895, bounds.Width * 0.95); - var minHeight = Math.Min(680, bounds.Height * 0.95); - ClientSize = new Size((int)minWidth, (int)minHeight); - } - - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - - var pageViewer = new StartupGreeterPageViewer(); - foreach (var page in GetGreeterPages()) - pageViewer.Pages.Add(page); - - var nextButton = new Button((sender, e) => pageViewer.NextPage()) - { - Text = "Next" - }; - - var prevButton = new Button((sender, e) => pageViewer.PreviousPage()) - { - Text = "Previous" - }; - - pageViewer.SelectedIndexChanged += (sender, e) => - { - prevButton.Enabled = pageViewer.SelectedIndex > 0; - nextButton.Enabled = pageViewer.SelectedIndex <= pageViewer.Pages.Count; - nextButton.Text = pageViewer.SelectedIndex == pageViewer.Pages.Count - 1 ? "Close" : "Next"; - }; - - base.Content = new StackedContent - { - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Stretch, - Expand = true, - Control = pageViewer - }, - new StackLayoutItem - { - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - prevButton, - nextButton - } - } - } - }; - } - - private IEnumerable GetGreeterPages() - { - yield return new WelcomePage(); - yield return new AreaEditorPage(); - yield return new BindingPage(); - yield return new PluginPage(); - if (App.EnableTrayIcon) - yield return new SystemTrayPage(); - yield return new WikiPage(); - } - - private class StartupGreeterPageViewer : DocumentControl - { - public void PreviousPage() - { - if (SelectedIndex > 0) - SelectedIndex--; - } - - public void NextPage() - { - if (SelectedIndex < Pages.Count - 1) - SelectedIndex++; - else if (SelectedIndex == Pages.Count - 1) - base.ParentWindow.Close(); - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/PluginManager.cs b/OpenTabletDriver.UX/Windows/PluginManager.cs new file mode 100644 index 000000000..b3ef82761 --- /dev/null +++ b/OpenTabletDriver.UX/Windows/PluginManager.cs @@ -0,0 +1,228 @@ +using System.Collections.Immutable; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq.Expressions; +using System.Reflection; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.Reflection.Metadata; +using OpenTabletDriver.UX.Components; +using OpenTabletDriver.UX.Controls; + +namespace OpenTabletDriver.UX.Windows +{ + public sealed class PluginManager : DesktopForm + { + private readonly IDriverDaemon _daemon; + private readonly App _app; + private readonly ListBox _pluginsList; + private ImmutableArray _remotePlugins; + private ImmutableArray _installedPlugins; + + public PluginManager(IDriverDaemon daemon, App app) + { + _daemon = daemon; + _app = app; + + Title = "Plugin Manager"; + + var placeholder = new Placeholder + { + Text = "No plugin is selected." + }; + + var splitter = new Splitter + { + Panel1MinimumSize = 250, + Panel1 = _pluginsList = new ListBox(), + Panel2 = placeholder + }; + + _pluginsList.ItemTextBinding = Binding.Property(p => p.Name); + Refresh().Run(); + + Content = splitter; + + var installButton = new Button((_, _) => Install().Run()) + { + Text = "Install" + }; + + var uninstallButton = new Button((_, _) => Uninstall().Run()) + { + Text = "Uninstall" + }; + + var details = new StackLayout + { + Padding = 5, + Spacing = 5, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + ControlFor(m => m.Name), + ControlFor(m => m.Owner), + ControlFor(m => m.Description), + ControlFor(m => m.PluginVersion), + ControlFor(m => m.RepositoryUrl), + ControlFor(m => m.SupportedDriverVersion), + ControlFor(m => m.WikiUrl), + ControlFor(m => m.LicenseIdentifier), + new StackLayoutItem(null, true), + new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Bottom, + Spacing = 5, + Items = + { + new StackLayoutItem(uninstallButton, true), + new StackLayoutItem(installButton, true) + } + } + } + }; + + DataContextBinding.Bind(_pluginsList.SelectedValueBinding); + + DataContextChanged += (_, _) => + { + splitter.Panel2 = DataContext is PluginMetadata ? details : placeholder; + if (DataContext is not PluginMetadata meta) + return; + + uninstallButton.Enabled = _installedPlugins.Any(meta.Match); + + if (uninstallButton.Enabled && _remotePlugins.FirstOrDefault(meta.Match) is PluginMetadata remoteMeta) + installButton.Enabled = remoteMeta.PluginVersion > meta.PluginVersion; + else + installButton.Enabled = !uninstallButton.Enabled; + + installButton.Text = uninstallButton.Enabled ? "Update" : "Install"; + }; + + var fileMenu = new ButtonMenuItem + { + Text = "&File", + Items = + { + new AppCommand("Refresh", Refresh, Application.Instance.CommonModifier | Keys.R) + } + }; + + Menu = new MenuBar + { + Items = + { + fileMenu + }, + QuitItem = new AppCommand("Close", Close, Keys.Escape) + }; + } + + /// + /// Refreshes the plugin list from the default remote source. + /// + private async Task Refresh() + { + _pluginsList.Enabled = false; + var selectedItem = _pluginsList.SelectedValue as PluginMetadata; + + var appVersion = Assembly.GetEntryAssembly()!.GetName().Version!; + + var installedQuery = await _daemon.GetInstalledPlugins(); + _installedPlugins = installedQuery.ToImmutableArray(); + + var remoteQuery = await _daemon.GetRemotePlugins(); + _remotePlugins = remoteQuery.ToImmutableArray(); + + var remote = from meta in _remotePlugins + where meta.IsSupportedBy(appVersion) + where !_installedPlugins.Any(meta.Match) + orderby meta.Name + select meta; + + var plugins = from meta in _installedPlugins.Concat(remote) + orderby meta.PluginVersion descending + group meta by (meta.Name, meta.Owner, meta.RepositoryUrl); + + var query = from plugin in plugins + let meta = plugin.FirstOrDefault() + orderby meta.Name, _installedPlugins.Any(m => m.Match(meta)) descending, _installedPlugins.Any(meta.Match) + select meta; + + var store = query.ToImmutableList(); + + _pluginsList.DataStore = store; + _pluginsList.Enabled = true; + + if (selectedItem != null) + _pluginsList.SelectedIndex = store.FindIndex(m => m.Match(selectedItem)); + } + + /// + /// Installs the currently selected plugin. + /// + private async Task Install() + { + Content.Enabled = false; + + var plugin = (PluginMetadata) DataContext; + await _daemon.DownloadPlugin(plugin); + + Content.Enabled = true; + await Refresh(); + } + + /// + /// Uninstalls the currently selected plugin. + /// + private async Task Uninstall() + { + Content.Enabled = false; + + var plugin = (PluginMetadata) DataContext; + await _daemon.UninstallPlugin(plugin); + + Content.Enabled = true; + await Refresh(); + } + + /// + /// Creates a control from an expression. + /// + /// The expression pointing to the target member. + private Control ControlFor(Expression> expression) + { + var memberExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberExpression.Member; + var title = property.GetCustomAttribute()?.DisplayName ?? property.Name; + + TextControl? control = null; + + if (property.GetCustomAttribute() is not null) + { + var url = DataContext != null ? property.GetValue(DataContext)?.ToString() : null; + var linkButton = new LinkButton(); + linkButton.Click += (_, _) => + { + if (url != null) + _app.Open(url); + }; + + control = linkButton; + } + + control ??= new Label + { + Wrap = WrapMode.Word + }; + control.TextBinding.Convert(t => t, (object? o) => o?.ToString()).BindDataContext(expression); + return new LabeledGroup(title, null, control) + { + MinimumSize = new Size(0, 46) + }; + } + } +} diff --git a/OpenTabletDriver.UX/Windows/Plugins/MetadataViewer.cs b/OpenTabletDriver.UX/Windows/Plugins/MetadataViewer.cs deleted file mode 100644 index fe2bafa82..000000000 --- a/OpenTabletDriver.UX/Windows/Plugins/MetadataViewer.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.UX.Controls; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Plugins -{ - public class MetadataViewer : Panel - { - public MetadataViewer() - { - actions = new StackLayout - { - Orientation = Orientation.Horizontal, - Spacing = 5, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = uninstallButton = new Button(UninstallHandler) - { - Text = "Uninstall" - } - }, - new StackLayoutItem - { - Expand = true, - Control = installButton = new Button(InstallHandler) - } - } - }; - - var installedBinding = new DelegateBinding( - () => - { - var contexts = AppInfo.PluginManager.GetLoadedPlugins(); - return contexts.Any(t => PluginMetadata.Match(t.GetMetadata(), Metadata)); - }, - addChangeEvent: (e) => MetadataChanged += e, - removeChangeEvent: (e) => MetadataChanged -= e - ); - - var updateableBinding = new DelegateBinding( - () => - { - var repo = PluginMetadataList.Repository; - if (repo == null) - return false; - - var updatableFromRepository = from meta in repo - where PluginMetadata.Match(meta, Metadata) - where meta.PluginVersion > Metadata.PluginVersion - where CurrentDriverVersion >= meta.SupportedDriverVersion - orderby meta.PluginVersion descending - select meta; - - return updatableFromRepository.Any(); - }, - addChangeEvent: (e) => MetadataChanged += e, - removeChangeEvent: (e) => MetadataChanged -= e - ); - - var installableBinding = new DelegateBinding( - () => updateableBinding.GetValue() || !installedBinding.GetValue(), - addChangeEvent: (e) => MetadataChanged += e, - removeChangeEvent: (e) => MetadataChanged += e - ); - - uninstallButton.GetEnabledBinding().Bind(installedBinding); - installButton.TextBinding.Bind(updateableBinding.Convert(c => c ? "Update" : "Install")); - installButton.GetEnabledBinding().Bind(installableBinding); - - content = new Scrollable - { - Content = new StackLayout - { - Padding = 5, - Spacing = 5, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Items = - { - new AlignedGroup - { - Text = "Name", - Content = name = new Label() - }, - new AlignedGroup - { - Text = "Owner", - Content = owner = new Label() - }, - new AlignedGroup - { - Text = "Description", - Content = description = new Label - { - Wrap = WrapMode.Word - } - }, - new AlignedGroup - { - Text = "Driver Version", - Content = driverVersion = new Label() - }, - new AlignedGroup - { - Text = "Plugin Version", - Content = pluginVersion = new Label() - }, - new AlignedGroup - { - Text = "Source Code Repository", - Content = sourceCode = new Button - { - Width = 175, - Text = "Show source code" - } - }, - new AlignedGroup - { - Text = "Wiki", - Content = wiki = new Button - { - Width = 175, - Text = "Show plugin wiki" - } - }, - new AlignedGroup - { - Text = "License", - Content = license = new Label() - }, - new StackLayoutItem(null, true), - actions - } - } - }; - - name.TextBinding.Bind(MetadataBinding.Child(c => c.Name)); - owner.TextBinding.Bind(MetadataBinding.Child(c => c.Owner)); - description.TextBinding.Bind(MetadataBinding.Child(c => c.Description)); - driverVersion.TextBinding.Bind(MetadataBinding.Child(c => c.SupportedDriverVersion).Convert(v => v?.ToString())); - pluginVersion.TextBinding.Bind(MetadataBinding.Child(c => c.PluginVersion).Convert(v => v?.ToString())); - license.TextBinding.Bind(MetadataBinding.Child(c => c.LicenseIdentifier)); - - sourceCode.GetEnabledBinding().Bind(MetadataBinding.Child(c => c.RepositoryUrl).Convert(c => c != null)); - sourceCode.Click += (sender, e) => DesktopInterop.Open(Metadata.RepositoryUrl); - - wiki.GetEnabledBinding().Bind(MetadataBinding.Child(c => c.WikiUrl).Convert(c => c != null)); - wiki.Click += (sender, e) => DesktopInterop.Open(Metadata.WikiUrl); - - AppInfo.PluginManager.AssembliesChanged += HandleAssembliesChanged; - } - - private Control content; - private StackLayout actions; - private Placeholder placeholder; - - private Label name, owner, description, driverVersion, pluginVersion, license; - private Button sourceCode, wiki; - - private Version CurrentDriverVersion = Assembly.GetExecutingAssembly().GetName().Version; - private Button uninstallButton, installButton; - - public event Func> RequestPluginInstall; - public event Func> RequestPluginUninstall; - - private PluginMetadata metadata; - public PluginMetadata Metadata - { - set - { - this.metadata = value; - this.OnMetadataChanged(); - } - get => this.metadata; - } - - public event EventHandler MetadataChanged; - - protected virtual void OnMetadataChanged() - { - MetadataChanged?.Invoke(this, new EventArgs()); - - var contexts = AppInfo.PluginManager.GetLoadedPlugins(); - - bool isInstalled = contexts.Any(t => PluginMetadata.Match(t.GetMetadata(), metadata)); - - this.Content = Metadata != null ? content : placeholder ??= new Placeholder - { - Text = "No plugin selected." - }; - } - - public BindableBinding MetadataBinding - { - get - { - return new BindableBinding( - this, - c => c.Metadata, - (c, v) => c.Metadata = v, - (c, h) => c.MetadataChanged += h, - (c, h) => c.MetadataChanged -= h - ); - } - } - - private void HandleAssembliesChanged(object sender, EventArgs e) => Application.Instance.AsyncInvoke(() => - { - MetadataBinding.Update(); - }); - - private async void InstallHandler(object sender, EventArgs e) - { - this.ParentWindow.Enabled = false; - - await RequestPluginInstall?.Invoke(Metadata); - - this.ParentWindow.Enabled = true; - } - - private async void UninstallHandler(object sender, EventArgs e) - { - this.ParentWindow.Enabled = false; - - await RequestPluginUninstall?.Invoke(Metadata); - - this.ParentWindow.Enabled = true; - } - - private class AlignedGroup : Group - { - public AlignedGroup() - { - base.Content = panel = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Right, - Padding = 5, - Items = - { - new StackLayoutItem - { - Control = container = new Panel() - } - } - }; - this.Orientation = Orientation.Horizontal; - } - - private StackLayout panel; - private Panel container; - - public new Control Content - { - set => container.Content = value; - get => container.Content; - } - } - - private class LinkButtonGroup : Group - { - public LinkButtonGroup(string header, string link, string text = null) - { - var linkButton = new Button - { - Text = text ?? header, - Width = 175, - Enabled = !string.IsNullOrEmpty(link) - }; - linkButton.Click += (sender, e) => DesktopInterop.Open(link); - - this.Text = header; - this.Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Right, - Items = - { - linkButton - } - }; - this.Orientation = Orientation.Horizontal; - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Plugins/PluginDropPanel.cs b/OpenTabletDriver.UX/Windows/Plugins/PluginDropPanel.cs deleted file mode 100644 index 55f3b07a3..000000000 --- a/OpenTabletDriver.UX/Windows/Plugins/PluginDropPanel.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Windows.Plugins -{ - public class PluginDropPanel : Panel - { - public PluginDropPanel() - { - AllowDrop = true; - - DropContent = new StackLayout - { - Items = - { - new StackLayoutItem(null, true), - new StackLayoutItem - { - Control = dropTextLabel, - HorizontalAlignment = HorizontalAlignment.Center - }, - new StackLayoutItem(null, true) - } - }; - } - - private const string DRAG_DROP_SUPPORTED = "Drop plugin here..."; - private const string DRAG_DROP_UNSUPPORTED = "Drag and drop is not supported on this platform."; - - public event Func RequestPluginInstall; - - private Control content; - public new Control Content - { - set - { - this.content = value; - base.Content = this.Content; - } - get => this.content; - } - - private readonly Label dropTextLabel = new Label - { - Text = DRAG_DROP_SUPPORTED - }; - - protected StackLayout DropContent { get; } - - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); - base.Content = this.Content; - } - - protected override void OnDragEnter(DragEventArgs args) - { - base.OnDragEnter(args); - try - { - if (args.Data.ContainsUris) - { - // Skip if running on bugged platform - // https://github.com/picoe/Eto/issues/1812 - if (args.Data.Uris != null && args.Data.Uris?.Length > 0) - { - var uriList = args.Data.Uris; - var supportedType = uriList.All(uri => - { - if (uri.IsFile && File.Exists(uri.LocalPath)) - { - var fileInfo = new FileInfo(uri.LocalPath); - return fileInfo.Extension == ".zip"; - } - return false; - }); - if (supportedType) - { - dropTextLabel.Text = DRAG_DROP_SUPPORTED; - args.Effects = DragEffects.Copy; - } - } - else - { - dropTextLabel.Text = DRAG_DROP_UNSUPPORTED; - args.Effects = DragEffects.None; - } - base.Content = DropContent; - } - } - catch (Exception ex) - { - ex.ShowMessageBox(); - } - } - - protected override void OnDragLeave(DragEventArgs args) - { - base.OnDragLeave(args); - base.Content = this.Content; - } - - protected override async void OnDragDrop(DragEventArgs args) - { - base.OnDragDrop(args); - try - { - if (args.Data.ContainsUris && args.Data.Uris != null && args.Data.Uris.Length > 0) - { - var uriList = args.Data.Uris; - foreach (var uri in uriList) - { - if (uri.IsFile && File.Exists(uri.LocalPath)) - { - await RequestPluginInstall?.Invoke(uri.LocalPath); - } - } - } - } - catch (Exception ex) - { - ex.ShowMessageBox(); - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Plugins/PluginManagerWindow.cs b/OpenTabletDriver.UX/Windows/Plugins/PluginManagerWindow.cs deleted file mode 100644 index c41910f8e..000000000 --- a/OpenTabletDriver.UX/Windows/Plugins/PluginManagerWindow.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Linq; -using System.Security.Cryptography; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Interop; -using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.UX.Dialogs; -using StreamJsonRpc; -using StreamJsonRpc.Protocol; - -namespace OpenTabletDriver.UX.Windows.Plugins -{ - public class PluginManagerWindow : DesktopForm - { - public PluginManagerWindow() - : base(Application.Instance.MainForm) - { - this.Title = "Plugin Manager"; - this.ClientSize = new Size(1000, 750); - this.AllowDrop = true; - - this.Menu = ConstructMenu(); - this.Content = dropPanel = new PluginDropPanel - { - Content = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Orientation = Orientation.Vertical, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new Splitter - { - Panel1MinimumSize = 300, - Panel1 = pluginList = new PluginMetadataList(), - Panel2 = metadataViewer = new MetadataViewer() - } - }, - new StackLayoutItem - { - HorizontalAlignment = HorizontalAlignment.Center, - Control = new Panel - { - Padding = 5, - Content = new Label - { - Text = "Drag and drop plugins here to install.", - VerticalAlignment = VerticalAlignment.Center - } - } - } - } - } - }; - - metadataViewer.MetadataBinding.Bind(pluginList.SelectedItemBinding, DualBindingMode.OneWay); - - dropPanel.RequestPluginInstall += Install; - metadataViewer.RequestPluginInstall += DownloadAndInstall; - metadataViewer.RequestPluginUninstall += Uninstall; - } - - private readonly PluginDropPanel dropPanel; - private readonly PluginMetadataList pluginList; - private readonly MetadataViewer metadataViewer; - - protected async Task SwitchRepositorySource() - { - var dialog = new RepositoryDialog("Switch Repository Source"); - if (await dialog.ShowModalAsync() is PluginMetadataCollection repository) - pluginList.SetRepository(repository); - } - - protected async Task DownloadAndInstall(PluginMetadata metadata) - { - try - { - if (await App.Driver.Instance.DownloadPlugin(metadata)) - { - pluginList.SelectFirstOrDefault((m => PluginMetadata.Match(m, metadata))); - AppInfo.PluginManager.Load(); - } - return true; - } - catch (RemoteInvocationException ex) - { - var data = ex.DeserializedErrorData as CommonErrorData; - if (data.TypeName == typeof(CryptographicException).FullName) - { - MessageBox.Show( - data.Message + Environment.NewLine + "Report this incident to the developers!", - "Cryptographic Verification Error", - MessageBoxButtons.OK, - MessageBoxType.Error - ); - } - else - { - data.ShowMessageBox(); - } - return false; - } - } - - protected async Task Install(string path) - { - if (await App.Driver.Instance.InstallPlugin(path)) - { - AppInfo.PluginManager.Load(); - } - else - { - MessageBox.Show(this, $"Failed to install plugin from '{path}'", "Plugin Manager", MessageBoxType.Error); - } - } - - protected async Task Uninstall(PluginMetadata metadata) - { - var context = AppInfo.PluginManager.GetLoadedPlugins().First( - c => PluginMetadata.Match(c.GetMetadata(), metadata) - ); - - if (context.Directory.Exists && !await App.Driver.Instance.UninstallPlugin(context.Directory.FullName)) - { - MessageBox.Show(this, $"'{context.FriendlyName}' failed to uninstall", "Plugin Manager", MessageBoxType.Error); - return false; - } - - AppInfo.PluginManager.UnloadPlugin(context); - return true; - } - - private MenuBar ConstructMenu() - { - var quitCommand = new Command { MenuText = "Exit", Shortcut = Keys.Escape }; - quitCommand.Executed += (_, _) => this.Close(); - - var install = new Command { MenuText = "Install plugin...", Shortcut = Application.Instance.CommonModifier | Keys.O }; - install.Executed += PromptInstallPlugin; - - var refresh = new Command { MenuText = "Refresh", Shortcut = Application.Instance.CommonModifier | Keys.R }; - refresh.Executed += RefreshHandler; - - var alternateSource = new Command { MenuText = "Use alternate source..." }; - alternateSource.Executed += async (sender, e) => await SwitchRepositorySource(); - - var pluginsDirectory = new Command { MenuText = "Open plugins directory..." }; - pluginsDirectory.Executed += (sender, e) => DesktopInterop.OpenFolder(AppInfo.Current.PluginDirectory); - - return new MenuBar() - { - QuitItem = quitCommand, - ApplicationItems = - { - install, - refresh, - alternateSource, - pluginsDirectory - } - }; - } - - private async void PromptInstallPlugin(object sender, EventArgs e) - { - if (!this.ParentWindow.Enabled) - return; - - var dialog = new OpenFileDialog() - { - Title = "Choose a plugin to install...", - Directory = new Uri(Eto.EtoEnvironment.GetFolderPath(Eto.EtoSpecialFolder.Documents)), - MultiSelect = true, - Filters = - { - new FileFilter("Plugin (.zip, .dll)", ".zip", ".dll") - } - }; - - if (dialog.ShowDialog(this) == DialogResult.Ok) - { - foreach (var file in dialog.Filenames) - { - await Install(file); - } - } - } - - private void RefreshHandler(object sender, EventArgs e) - { - if (this.ParentWindow.Enabled) - pluginList.Refresh(); - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Plugins/PluginMetadataList.cs b/OpenTabletDriver.UX/Windows/Plugins/PluginMetadataList.cs deleted file mode 100644 index bccf123e6..000000000 --- a/OpenTabletDriver.UX/Windows/Plugins/PluginMetadataList.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Reflection.Metadata; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Plugins -{ - public class PluginMetadataList : ListBox - { - public PluginMetadataList() - { - ItemTextBinding = Binding.Property(m => m.Name); - - Refresh(); - AppInfo.PluginManager.AssembliesChanged += (sender, e) => Refresh(); - } - - public static PluginMetadataCollection Repository { private set; get; } - - private static readonly TimeSpan DOWNLOAD_TIMEOUT = TimeSpan.FromSeconds(5); - private static readonly Version AppVersion = Assembly.GetEntryAssembly().GetName().Version; - - public void Refresh() => Application.Instance.AsyncInvoke(async () => - { - this.Enabled = false; - - SetRepository(await DownloadMetadataAsync()); - - this.Enabled = true; - }); - - public void SetRepository(PluginMetadataCollection repository) => Application.Instance.AsyncInvoke(() => - { - Repository = repository; - - var selected = this.SelectedItem; - - var local = from ctx in AppInfo.PluginManager.GetLoadedPlugins() - orderby ctx.FriendlyName - select ctx.GetMetadata(); - - var remote = from meta in Repository - where meta.IsSupportedBy(AppVersion) - where !local.Any(m => PluginMetadata.Match(m, meta)) - select meta; - - var plugins = from meta in local.Concat(remote) - orderby meta.PluginVersion descending - group meta by (meta.Name, meta.Owner, meta.RepositoryUrl); - - var query = from plugin in plugins - let meta = plugin.FirstOrDefault() - orderby meta.Name - orderby local.Any(m => PluginMetadata.Match(m, meta)) descending - select meta; - - this.DataStore = query.ToList(); - - SelectFirstOrDefault(p => PluginMetadata.Match(p, selected)); - }); - - public void SelectFirstOrDefault(Func predicate) - { - if ((this.DataStore as IEnumerable)?.FirstOrDefault(m => predicate(m)) is PluginMetadata existingMeta) - { - this.SelectedValue = existingMeta; - } - } - - protected async Task DownloadMetadataAsync() - { - var repoFetch = PluginMetadataCollection.DownloadAsync(); - var timeoutTask = Task.Delay(DOWNLOAD_TIMEOUT); - - try - { - var completedTask = await Task.WhenAny(repoFetch, timeoutTask); - if (completedTask == timeoutTask) - MessageBox.Show("Fetching plugin metadata timed-out. Only local plugins will be shown.", MessageBoxType.Warning); - else - return await repoFetch; - } - catch (HttpRequestException httpEx) - { - MessageBox.Show( - "An error occurred when retrieving metadata. Only local plugins will be shown." + Environment.NewLine + - $"(Status code {httpEx.StatusCode})", - MessageBoxType.Warning - ); - } - catch (Exception e) - { - e.ShowMessageBox(); - Log.Exception(e); - } - return PluginMetadataCollection.Empty; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/StylizedPage.cs b/OpenTabletDriver.UX/Windows/StylizedPage.cs deleted file mode 100644 index c9bebacc4..000000000 --- a/OpenTabletDriver.UX/Windows/StylizedPage.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using Eto.Forms; -using OpenTabletDriver.UX.Attributes; - -namespace OpenTabletDriver.UX.Windows -{ - public class StylizedPage : DocumentPage - { - public StylizedPage() - { - this.Closable = false; - - var type = this.GetType(); - this.Text = type.GetCustomAttribute()?.Name ?? type.Name; - } - } -} diff --git a/OpenTabletDriver.UX/Windows/Tablet/TabletDebugger.cs b/OpenTabletDriver.UX/Windows/Tablet/TabletDebugger.cs deleted file mode 100644 index e52a8563b..000000000 --- a/OpenTabletDriver.UX/Windows/Tablet/TabletDebugger.cs +++ /dev/null @@ -1,376 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Text; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Desktop; -using OpenTabletDriver.Desktop.Contracts; -using OpenTabletDriver.Desktop.RPC; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Timing; -using OpenTabletDriver.UX.Controls.Generic; -using OpenTabletDriver.UX.Tools; - -namespace OpenTabletDriver.UX.Windows.Tablet -{ - public class TabletDebugger : DesktopForm - { - const int LARGE_FONTSIZE = 14; - const int FONTSIZE = LARGE_FONTSIZE - 4; - const int SPACING = 5; - - public TabletDebugger() - : base(Application.Instance.MainForm) - { - Title = "Tablet Debugger"; - - var debugger = new StackLayout - { - HorizontalContentAlignment = HorizontalAlignment.Stretch, - Height = 320, - Padding = SPACING, - Spacing = SPACING, - Items = - { - new StackLayoutItem - { - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Bottom, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new DebuggerGroup - { - Text = "Device", - Content = deviceName = new Label - { - Font = Fonts.Monospace(LARGE_FONTSIZE) - } - } - }, - new DebuggerGroup - { - Text = "Report Rate", - Width = LARGE_FONTSIZE * 6, - Content = reportRate = new Label - { - Font = Fonts.Monospace(LARGE_FONTSIZE) - } - }, - new StackLayoutItem - { - Control = new DebuggerGroup - { - Text = "Reports Recorded", - Width = LARGE_FONTSIZE * 10, - Content = reportsRecorded = new Label - { - Font = Fonts.Monospace(LARGE_FONTSIZE) - } - } - }, - new Group - { - Text = "Options", - Content = enableDataRecording = new CheckBox - { - Text = "Enable Data Recording" - } - } - } - } - }, - new StackLayoutItem - { - Expand = true, - Control = new StackLayout - { - Orientation = Orientation.Horizontal, - VerticalContentAlignment = VerticalAlignment.Stretch, - Height = 240, - Items = - { - new StackLayoutItem - { - Expand = true, - Control = new DebuggerGroup - { - Text = "Raw Tablet Data", - Width = FONTSIZE * 33, - Content = rawTablet = new Label - { - Font = Fonts.Monospace(FONTSIZE) - } - } - }, - new StackLayoutItem - { - Control = new DebuggerGroup - { - Text = "Tablet Report", - Width = FONTSIZE * 33, - Content = tablet = new Label - { - Font = Fonts.Monospace(FONTSIZE) - } - } - } - } - } - } - } - }; - - var splitter = new Splitter - { - Orientation = Orientation.Vertical, - Width = 660, - Height = 800, - FixedPanel = SplitterFixedPanel.Panel2, - Panel1 = new DebuggerGroup - { - Height = 200, - Text = "Visualizer", - Content = tabletVisualizer = new TabletVisualizer() - }, - Panel2 = debugger - }; - - this.Content = new Scrollable - { - Content = splitter - }; - - var reportBinding = ReportDataBinding.Child(c => (c.ToObject() as IDeviceReport)); - - deviceName.TextBinding.Bind(ReportDataBinding.Child(c => c.Tablet.Properties.Name)); - rawTablet.TextBinding.Bind(reportBinding.Child(c => ReportFormatter.GetStringRaw(c))); - tablet.TextBinding.Bind(reportBinding.Child(c => ReportFormatter.GetStringFormat(c))); - reportRate.TextBinding.Bind(ReportPeriodBinding.Convert(c => Math.Round(1000.0 / c) + "hz")); - reportsRecorded.TextBinding.Bind(NumberOfReportsRecordedBinding.Convert(c => c.ToString())); - tabletVisualizer.ReportDataBinding.Bind(ReportDataBinding); - - App.Driver.DeviceReport += HandleReport; - App.Driver.TabletsChanged += HandleTabletsChanged; - App.Driver.Instance.SetTabletDebug(true); - - var outputStream = File.OpenWrite(Path.Join(AppInfo.Current.AppDataDirectory, "tablet-data.txt")); - dataRecordingOutput = new StreamWriter(outputStream); - } - - protected override async void OnClosing(CancelEventArgs e) - { - base.OnClosing(e); - - await App.Driver.Instance.SetTabletDebug(false); - - dataRecordingOutput?.Close(); - dataRecordingOutput = null; - } - - private Label deviceName, rawTablet, tablet, reportRate, reportsRecorded; - private TabletVisualizer tabletVisualizer; - private CheckBox enableDataRecording; - - private DebugReportData reportData; - private double reportPeriod; - private int numReportsRecorded; - - private HPETDeltaStopwatch stopwatch = new HPETDeltaStopwatch(true); - private TextWriter dataRecordingOutput; - - public DebugReportData ReportData - { - set - { - this.reportData = value; - this.OnReportDataChanged(); - } - get => this.reportData; - } - - public double ReportPeriod - { - set - { - this.reportPeriod = value; - this.OnReportPeriodChanged(); - } - get => this.reportPeriod; - } - - public int NumberOfReportsRecorded - { - set - { - this.numReportsRecorded = value; - this.OnNumberOfReportsRecordedChanged(); - } - get => this.numReportsRecorded; - } - - public event EventHandler ReportDataChanged; - public event EventHandler ReportPeriodChanged; - public event EventHandler NumberOfReportsRecordedChanged; - - protected virtual void OnReportDataChanged() - { - ReportDataChanged?.Invoke(this, new EventArgs()); - ReportPeriod += (stopwatch.Restart().TotalMilliseconds - ReportPeriod) / 10.0f; - } - - protected virtual void OnReportPeriodChanged() => ReportPeriodChanged?.Invoke(this, new EventArgs()); - protected virtual void OnNumberOfReportsRecordedChanged() => NumberOfReportsRecordedChanged?.Invoke(this, new EventArgs()); - - public BindableBinding ReportDataBinding - { - get - { - return new BindableBinding( - this, - c => c.ReportData, - (c, v) => c.ReportData = v, - (c, h) => c.ReportDataChanged += h, - (c, h) => c.ReportDataChanged -= h - ); - } - } - - public BindableBinding ReportPeriodBinding - { - get - { - return new BindableBinding( - this, - c => c.ReportPeriod, - (c, v) => c.ReportPeriod = v, - (c, h) => c.ReportPeriodChanged += h, - (c, h) => c.ReportPeriodChanged -= h - ); - } - } - - public BindableBinding NumberOfReportsRecordedBinding - { - get - { - return new BindableBinding( - this, - c => c.NumberOfReportsRecorded, - (c, v) => c.NumberOfReportsRecorded = v, - (c, h) => c.NumberOfReportsRecordedChanged += h, - (c, h) => c.NumberOfReportsRecordedChanged -= h - ); - } - } - - private void HandleReport(object sender, DebugReportData data) => Application.Instance.AsyncInvoke(() => - { - this.ReportData = data; - - if (data.ToObject() is IDeviceReport deviceReport) - { - if (enableDataRecording.Checked ?? false) - { - var output = string.Join(' ', deviceReport.Raw.Select(d => d.ToString("X2"))); - dataRecordingOutput?.WriteLine(output); - NumberOfReportsRecorded++; - } - } - }); - - private void HandleTabletsChanged(object sender, IEnumerable tablets) => Application.Instance.AsyncInvoke(() => - { - StringBuilder sb = new StringBuilder("Tablet Debugger"); - if (tablets != null && tablets.Any()) - { - var numTablets = Math.Min(tablets.Count(), 3); - sb.Append(" - "); - sb.Append(string.Join(", ", tablets.Take(numTablets).Select(t => t.Properties.Name))); - } - this.Title = sb.ToString(); - }); - - private class DebuggerGroup : Group - { - protected override Color VerticalBackgroundColor => base.HorizontalBackgroundColor; - } - - private class TabletVisualizer : ScheduledDrawable - { - private static readonly Color AccentColor = SystemColors.Highlight; - - public DebugReportData ReportData { set; get; } - - public BindableBinding ReportDataBinding - { - get - { - return new BindableBinding( - this, - c => c.ReportData, - (c, v) => c.ReportData = v - ); - } - } - - protected override void OnNextFrame(PaintEventArgs e) - { - if (ReportData?.Tablet is TabletReference tablet) - { - var graphics = e.Graphics; - using (graphics.SaveTransformState()) - { - var digitizer = tablet.Properties.Specifications.Digitizer; - var yScale = (this.ClientSize.Height - SPACING) / digitizer.Height; - var xScale = (this.ClientSize.Width - SPACING) / digitizer.Width; - var finalScale = Math.Min(yScale, xScale); - - var clientCenter = new PointF(this.ClientSize.Width, this.ClientSize.Height) / 2; - var tabletCenter = new PointF(digitizer.Width, digitizer.Height) / 2 * finalScale; - - graphics.TranslateTransform(clientCenter - tabletCenter); - - DrawBackground(graphics, finalScale, tablet); - DrawPosition(graphics, finalScale, tablet); - } - } - } - - protected void DrawBackground(Graphics graphics, float scale, TabletReference tablet) - { - var digitizer = ReportData.Tablet.Properties.Specifications.Digitizer; - var bg = new RectangleF(0, 0, digitizer.Width, digitizer.Height) * scale; - - graphics.FillRectangle(SystemColors.WindowBackground, bg); - graphics.DrawRectangle(AccentColor, bg); - } - - protected void DrawPosition(Graphics graphics, float scale, TabletReference tablet) - { - var report = ReportData?.ToObject(); - var specifications = ReportData.Tablet.Properties.Specifications; - var digitizer = specifications.Digitizer; - var pen = specifications.Pen; - - if (report is IAbsolutePositionReport absReport) - { - var tabletMm = new SizeF(digitizer.Width, digitizer.Height); - var tabletPx = new SizeF(digitizer.MaxX, digitizer.MaxY); - var tabletScale = tabletMm / tabletPx * scale; - var position = new PointF(absReport.Position.X, absReport.Position.Y) * tabletScale; - - var drawRect = RectangleF.FromCenter(position, new SizeF(SPACING, SPACING)); - graphics.FillEllipse(AccentColor, drawRect); - } - } - } - } -} diff --git a/OpenTabletDriver.UX/Windows/TabletDebugger.cs b/OpenTabletDriver.UX/Windows/TabletDebugger.cs new file mode 100644 index 000000000..acab36a95 --- /dev/null +++ b/OpenTabletDriver.UX/Windows/TabletDebugger.cs @@ -0,0 +1,141 @@ +using System.ComponentModel; +using System.Linq.Expressions; +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.Contracts; +using OpenTabletDriver.Desktop.RPC; +using OpenTabletDriver.UX.Components; + +namespace OpenTabletDriver.UX.Windows +{ + public sealed class TabletDebugger : DesktopForm + { + private readonly IDriverDaemon _driverDaemon; + + public TabletDebugger(IControlBuilder controlBuilder, IDriverDaemon driverDaemon) + { + _driverDaemon = driverDaemon; + + Title = "Tablet Debugger"; + MinimumSize = new Size(800, 600); + + Menu = new MenuBar + { + QuitItem = new AppCommand("Close", Close, Keys.Escape) + }; + + var viewer = controlBuilder.Build(); + + var deviceLabel = LabelFor(d => d.DeviceName, "None"); + var reportTypeLabel = LabelFor(d => d.ReportType); + var rawLabel = LabelFor(d => d.Raw); + var propertiesLabel = LabelFor(d => d.Formatted); + + var reportRateLabel = new Label { Text = "0hz" }; + var reportPeriod = 0.0; + var sw = new HPETDeltaStopwatch(); + + _driverDaemon.SetTabletDebug(true).Run(); + _driverDaemon.DeviceReport += (_, data) => Application.Instance.AsyncInvoke(() => + { + viewer.Draw(data); + DataContext = data; + + reportPeriod += (sw.Restart().TotalMilliseconds - reportPeriod) / 10.0; + reportRateLabel.Text = $"{Math.Round(1000.0 / reportPeriod)}hz"; + }); + + var left = new StackLayout + { + Padding = 5, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + new StackLayout + { + Orientation = Orientation.Horizontal, + VerticalContentAlignment = VerticalAlignment.Stretch, + Spacing = 5, + Items = + { + new StackLayoutItem(CreateGroupBox("Device", deviceLabel), true), + CreateGroupBox("Report Rate", reportRateLabel) + } + }, + new StackLayoutItem(CreateGroupBox("Raw", rawLabel), true) + } + }; + + var right = new StackLayout + { + Padding = 5, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Items = + { + CreateGroupBox("Report Type", reportTypeLabel), + new StackLayoutItem(CreateGroupBox("Properties", propertiesLabel), true) + } + }; + + Content = new Splitter + { + Orientation = Orientation.Vertical, + Panel1MinimumSize = 250, + Panel1 = viewer, + Panel2 = new Splitter + { + Orientation = Orientation.Horizontal, + Panel1MinimumSize = 250, + Panel2MinimumSize = 250, + Panel1 = left, + Panel2 = right + } + }; + } + + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + + _driverDaemon.SetTabletDebug(false).Run(); + } + + /// + /// Creates a label for an expression. + /// + /// The expression to bind to. + /// Whether the label font is monospaced. + /// The fallback data + /// + private static Label LabelFor( + Expression>? expression, + string? defaultText = null, + bool monospace = true) + { + var label = new Label + { + Text = defaultText, + Font = monospace ? Fonts.Monospace(10) : default, + Wrap = WrapMode.Word + }; + label.TextBinding.BindDataContext(expression); + return label; + } + + /// + /// Creates a for a control. + /// + /// + /// + /// + private static GroupBox CreateGroupBox(string text, Control? content) + { + return new GroupBox + { + Text = text, + Content = content, + Padding = 5 + }; + } + } +} diff --git a/OpenTabletDriver.UX/Windows/TabletDisplay.cs b/OpenTabletDriver.UX/Windows/TabletDisplay.cs new file mode 100644 index 000000000..784ff274a --- /dev/null +++ b/OpenTabletDriver.UX/Windows/TabletDisplay.cs @@ -0,0 +1,97 @@ +using Eto.Drawing; +using Eto.Forms; +using OpenTabletDriver.Desktop.RPC; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.UX.Windows +{ + public class TabletDisplay : Drawable + { + private readonly App _app; + private DebugReportData? _data; + private TabletConfiguration? _configuration; + + public TabletDisplay(App app) + { + _app = app; + } + + private static Color PointColor { get; } = Colors.White; + private static Color PointLinesColor { get; } = new Color(Colors.White, 0.25f); + private static Color BackgroundFillColor { get; } = new Color(SystemColors.Highlight, 0.75f); + private static Color BackgroundBorderColor { get; } = SystemColors.ControlText; + + private static Font TextFont { get; } = Fonts.Monospace(8); + private static SolidBrush TextBrush { get; } = new SolidBrush(PointLinesColor); + + public void Draw(DebugReportData reportData) + { + _data = reportData; + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + if (_data == null) + return; + + if (_configuration?.Name != _data.DeviceName) + _configuration = _app.Tablets.First(t => t.Name == _data.DeviceName); + + if (_configuration != null) + DrawDigitizer(e.Graphics); + } + + private void DrawDigitizer(Graphics graphics) + { + var digitizer = _configuration!.Specifications.Digitizer!; + var background = new RectangleF(0, 0, digitizer.MaxX, digitizer.MaxY); + + var scaleX = (Width - 5) / background.Width; + var scaleY = (Height - 5) / background.Height; + var scale = scaleX > scaleY ? scaleY : scaleX; + + var offsetX = (Width - background.Width * scale) / 2; + var offsetY = (Height - background.Height * scale) / 2; + + using (graphics.SaveTransformState()) + { + graphics.TranslateTransform(offsetX, offsetY); + + var scaledBackground = background * scale; + graphics.FillRectangle(BackgroundFillColor, scaledBackground); + graphics.DrawRectangle(BackgroundBorderColor, scaledBackground); + + if (_data!.RawPosition == null) + return; + + var x = _data.RawPosition.Value.X * scale; + var y = _data.RawPosition.Value.Y * scale; + var point = RectangleF.FromCenter(new PointF(x, y), new SizeF(5, 5)); + graphics.DrawEllipse(PointColor, point); + + graphics.DrawLine(PointLinesColor, x, 0, x, scaledBackground.Height); + graphics.DrawLine(PointLinesColor, 0, y, scaledBackground.Width, y); + + var text = new FormattedText + { + Font = TextFont, + ForegroundBrush = TextBrush, + Text = _data.RawPosition.ToString(), + }; + + var textPos = new PointF(x + 5, y + 5); + var textSize = text.Measure(); + + if (textSize.Height + textPos.Y > Height - 10) + textPos.Y -= textSize.Height + 10; + if (textSize.Width + textPos.X > Width - 10) + textPos.X -= textSize.Width + 10; + + graphics.DrawText(text, textPos); + } + } + } +} diff --git a/OpenTabletDriver.UX/Windows/Updater/UpdaterWindow.cs b/OpenTabletDriver.UX/Windows/Updater/UpdaterWindow.cs deleted file mode 100644 index 2bd4b0ea2..000000000 --- a/OpenTabletDriver.UX/Windows/Updater/UpdaterWindow.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; -using Eto.Drawing; -using Eto.Forms; -using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.UX.Controls; -using OpenTabletDriver.UX.Controls.Generic; - -namespace OpenTabletDriver.UX.Windows.Updater -{ - public class UpdaterWindow : Form - { - public UpdaterWindow() - { - this.Title = "OpenTabletDriver Updater"; - this.ClientSize = new Size(400, 380); - - this.Content = new Placeholder - { - Text = "Checking for updates...", - ExtraContent = new Panel - { - Padding = 10, - Content = new ProgressBar - { - Indeterminate = true - } - } - }; - - _ = InitializeAsync(); - } - - private async Task InitializeAsync() - { - var updateAvailable = await App.Driver.Instance.HasUpdate(); - if (updateAvailable) - { - var release = await App.Driver.Instance.GetUpdateInfo(); - this.Content = new StackLayout() - { - HorizontalContentAlignment = HorizontalAlignment.Center, - Items = - { - new PaddingSpacerItem(), - new Bitmap(App.Logo.WithSize(256, 256)), - "An update is available to install", - $"v{release.Version}", - new Button(Update) - { - Text = "Install" - }, - new PaddingSpacerItem(), - } - }; - } - else - { - this.Content = new Placeholder - { - Text = "No updates are available." - }; - } - } - - private void Update(object sender, EventArgs e) => Application.Instance.AsyncInvoke(async () => - { - string basePath = AppDomain.CurrentDomain.BaseDirectory; - string path = SystemInterop.CurrentPlatform switch - { - PluginPlatform.Windows => Path.Join(basePath, "OpenTabletDriver.UX.Wpf.exe"), - PluginPlatform.MacOS => Path.Join(basePath, "OpenTabletDriver.UX.MacOS"), - _ => throw new NotSupportedException("Current platform does not support updating.") - }; - - // Disallow multiple invocations - (sender as Control)!.Enabled = false; - - await App.Driver.Instance.InstallUpdate(); - - Process.Start(path); - - if (Application.Instance.QuitIsSupported) - Application.Instance.Quit(); - else - Environment.Exit(0); - }); - } -} diff --git a/OpenTabletDriver.UX/Windows/WindowSingleton.cs b/OpenTabletDriver.UX/Windows/WindowSingleton.cs deleted file mode 100644 index f687fd59c..000000000 --- a/OpenTabletDriver.UX/Windows/WindowSingleton.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Eto.Forms; - -namespace OpenTabletDriver.UX.Windows -{ - public class WindowSingleton where T : Window, new() - { - private T window; - - public void Show() - { - if (window == null) - { - window = new T(); - window.Closed += HandleWindowClosed; - } - - switch (window) - { - case DesktopForm desktopForm: - desktopForm.Show(); - break; - case Form form: - form.Show(); - form.Focus(); - break; - case Dialog dialog: - dialog.ShowModal(); - break; - } - } - - private void HandleWindowClosed(object sender, EventArgs e) - { - window = null; - } - } -} diff --git a/OpenTabletDriver.Windows.slnf b/OpenTabletDriver.Windows.slnf index 16519e1b3..54463620d 100644 --- a/OpenTabletDriver.Windows.slnf +++ b/OpenTabletDriver.Windows.slnf @@ -9,7 +9,6 @@ ".\\OpenTabletDriver.Daemon\\OpenTabletDriver.Daemon.csproj", ".\\OpenTabletDriver.Desktop\\OpenTabletDriver.Desktop.csproj", ".\\OpenTabletDriver.Native\\OpenTabletDriver.Native.csproj", - ".\\OpenTabletDriver.Plugin\\OpenTabletDriver.Plugin.csproj", ".\\OpenTabletDriver.Tests\\OpenTabletDriver.Tests.csproj", ".\\OpenTabletDriver.UX\\OpenTabletDriver.UX.csproj", ".\\OpenTabletDriver.UX.Wpf\\OpenTabletDriver.UX.Wpf.csproj" diff --git a/OpenTabletDriver.sln b/OpenTabletDriver.sln index fbebcff0c..2427227e9 100644 --- a/OpenTabletDriver.sln +++ b/OpenTabletDriver.sln @@ -7,20 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver", "OpenTab EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Native", "OpenTabletDriver.Native\OpenTabletDriver.Native.csproj", "{57A5101A-20F4-41AA-8649-2C3785DD5BA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Plugin", "OpenTabletDriver.Plugin\OpenTabletDriver.Plugin.csproj", "{1EF3ED59-1D11-4814-907B-4F8C90B9032E}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Daemon", "OpenTabletDriver.Daemon\OpenTabletDriver.Daemon.csproj", "{18F3FBE6-110E-4F14-912F-14273028191C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.Gtk", "OpenTabletDriver.UX.Gtk\OpenTabletDriver.UX.Gtk.csproj", "{7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX", "OpenTabletDriver.UX\OpenTabletDriver.UX.csproj", "{1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.Wpf", "OpenTabletDriver.UX.Wpf\OpenTabletDriver.UX.Wpf.csproj", "{CAD634C1-780D-4123-8D58-385A50F3F33C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Console", "OpenTabletDriver.Console\OpenTabletDriver.Console.csproj", "{040FFF57-84CB-464C-83A5-B99002FF6605}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.MacOS", "OpenTabletDriver.UX.MacOS\OpenTabletDriver.UX.MacOS.csproj", "{BF28815D-56CA-443D-8F1E-836C19D94F05}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Tests", "OpenTabletDriver.Tests\OpenTabletDriver.Tests.csproj", "{B0107F45-E452-40E4-8407-14F99895CE98}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Desktop", "OpenTabletDriver.Desktop\OpenTabletDriver.Desktop.csproj", "{A72DF3AF-BFA5-4573-B02B-85E186AA428A}" @@ -31,6 +21,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Tools.udev EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.Configurations", "OpenTabletDriver.Configurations\OpenTabletDriver.Configurations.csproj", "{EF54AA9B-A725-44B5-96B5-AE526EF431CF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX", "OpenTabletDriver.UX\OpenTabletDriver.UX.csproj", "{4879CD37-BB09-4238-8CB9-34074D589969}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.Gtk", "OpenTabletDriver.UX.Gtk\OpenTabletDriver.UX.Gtk.csproj", "{E28E6DE4-382F-4609-AB44-4159BE7A9E44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.Wpf", "OpenTabletDriver.UX.Wpf\OpenTabletDriver.UX.Wpf.csproj", "{865533DD-6FF4-417E-B849-8DCF79B7DB50}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTabletDriver.UX.MacOS", "OpenTabletDriver.UX.MacOS\OpenTabletDriver.UX.MacOS.csproj", "{04F1C172-8816-4FEC-B54B-D82535996C01}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -68,18 +66,6 @@ Global {57A5101A-20F4-41AA-8649-2C3785DD5BA2}.Release|x64.Build.0 = Release|Any CPU {57A5101A-20F4-41AA-8649-2C3785DD5BA2}.Release|x86.ActiveCfg = Release|Any CPU {57A5101A-20F4-41AA-8649-2C3785DD5BA2}.Release|x86.Build.0 = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|x64.ActiveCfg = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|x64.Build.0 = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|x86.ActiveCfg = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Debug|x86.Build.0 = Debug|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|Any CPU.Build.0 = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|x64.ActiveCfg = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|x64.Build.0 = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|x86.ActiveCfg = Release|Any CPU - {1EF3ED59-1D11-4814-907B-4F8C90B9032E}.Release|x86.Build.0 = Release|Any CPU {18F3FBE6-110E-4F14-912F-14273028191C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18F3FBE6-110E-4F14-912F-14273028191C}.Debug|Any CPU.Build.0 = Debug|Any CPU {18F3FBE6-110E-4F14-912F-14273028191C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -92,42 +78,6 @@ Global {18F3FBE6-110E-4F14-912F-14273028191C}.Release|x64.Build.0 = Release|Any CPU {18F3FBE6-110E-4F14-912F-14273028191C}.Release|x86.ActiveCfg = Release|Any CPU {18F3FBE6-110E-4F14-912F-14273028191C}.Release|x86.Build.0 = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|x64.Build.0 = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Debug|x86.Build.0 = Debug|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|Any CPU.Build.0 = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|x64.ActiveCfg = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|x64.Build.0 = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|x86.ActiveCfg = Release|Any CPU - {7A31CE07-C732-4DCF-BBAD-01EFC55A1C60}.Release|x86.Build.0 = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|x64.Build.0 = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|x86.ActiveCfg = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Debug|x86.Build.0 = Debug|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|Any CPU.Build.0 = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|x64.ActiveCfg = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|x64.Build.0 = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|x86.ActiveCfg = Release|Any CPU - {1A9166D9-0B7E-41B6-8210-EC04E17E9C3E}.Release|x86.Build.0 = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|x64.ActiveCfg = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|x64.Build.0 = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|x86.ActiveCfg = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Debug|x86.Build.0 = Debug|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|Any CPU.Build.0 = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|x64.ActiveCfg = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|x64.Build.0 = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|x86.ActiveCfg = Release|Any CPU - {CAD634C1-780D-4123-8D58-385A50F3F33C}.Release|x86.Build.0 = Release|Any CPU {040FFF57-84CB-464C-83A5-B99002FF6605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {040FFF57-84CB-464C-83A5-B99002FF6605}.Debug|Any CPU.Build.0 = Debug|Any CPU {040FFF57-84CB-464C-83A5-B99002FF6605}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -140,18 +90,6 @@ Global {040FFF57-84CB-464C-83A5-B99002FF6605}.Release|x64.Build.0 = Release|Any CPU {040FFF57-84CB-464C-83A5-B99002FF6605}.Release|x86.ActiveCfg = Release|Any CPU {040FFF57-84CB-464C-83A5-B99002FF6605}.Release|x86.Build.0 = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|x64.ActiveCfg = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|x64.Build.0 = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|x86.ActiveCfg = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Debug|x86.Build.0 = Debug|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|Any CPU.Build.0 = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|x64.ActiveCfg = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|x64.Build.0 = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|x86.ActiveCfg = Release|Any CPU - {BF28815D-56CA-443D-8F1E-836C19D94F05}.Release|x86.Build.0 = Release|Any CPU {B0107F45-E452-40E4-8407-14F99895CE98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0107F45-E452-40E4-8407-14F99895CE98}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0107F45-E452-40E4-8407-14F99895CE98}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -212,5 +150,53 @@ Global {EF54AA9B-A725-44B5-96B5-AE526EF431CF}.Release|x64.Build.0 = Release|Any CPU {EF54AA9B-A725-44B5-96B5-AE526EF431CF}.Release|x86.ActiveCfg = Release|Any CPU {EF54AA9B-A725-44B5-96B5-AE526EF431CF}.Release|x86.Build.0 = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|x64.ActiveCfg = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|x64.Build.0 = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|x86.ActiveCfg = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Debug|x86.Build.0 = Debug|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|Any CPU.Build.0 = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|x64.ActiveCfg = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|x64.Build.0 = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|x86.ActiveCfg = Release|Any CPU + {4879CD37-BB09-4238-8CB9-34074D589969}.Release|x86.Build.0 = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|x64.ActiveCfg = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|x64.Build.0 = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|x86.ActiveCfg = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Debug|x86.Build.0 = Debug|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|Any CPU.Build.0 = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|x64.ActiveCfg = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|x64.Build.0 = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|x86.ActiveCfg = Release|Any CPU + {E28E6DE4-382F-4609-AB44-4159BE7A9E44}.Release|x86.Build.0 = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|x64.ActiveCfg = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|x64.Build.0 = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|x86.ActiveCfg = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Debug|x86.Build.0 = Debug|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|Any CPU.Build.0 = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|x64.ActiveCfg = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|x64.Build.0 = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|x86.ActiveCfg = Release|Any CPU + {865533DD-6FF4-417E-B849-8DCF79B7DB50}.Release|x86.Build.0 = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|x64.ActiveCfg = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|x64.Build.0 = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|x86.ActiveCfg = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Debug|x86.Build.0 = Debug|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|Any CPU.Build.0 = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|x64.ActiveCfg = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|x64.Build.0 = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|x86.ActiveCfg = Release|Any CPU + {04F1C172-8816-4FEC-B54B-D82535996C01}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/OpenTabletDriver/AngledArea.cs b/OpenTabletDriver/AngledArea.cs new file mode 100644 index 000000000..f51b6d367 --- /dev/null +++ b/OpenTabletDriver/AngledArea.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// An supporting rotation. + /// + [PublicAPI] + public class AngledArea : Area + { + private float _rotation; + + /// + /// The rotation angle of the area. + /// + [DisplayName(nameof(Rotation))] + public float Rotation + { + set => RaiseAndSetIfChanged(ref _rotation, value); + get => _rotation; + } + + public override Vector2[] GetCorners() + { + var origin = GetPosition(); + var matrix = Matrix3x2.CreateTranslation(-origin); + matrix *= Matrix3x2.CreateRotation((float) (Rotation * Math.PI / 180)); + matrix *= Matrix3x2.CreateTranslation(origin); + + var transformedCorners = from corner in base.GetCorners() + select Vector2.Transform(corner, matrix); + + return transformedCorners.ToArray(); + } + + public override string ToString() => $"[{Width}x{Height}@{GetPosition()}:{Rotation}°]"; + } +} diff --git a/OpenTabletDriver/Area.cs b/OpenTabletDriver/Area.cs new file mode 100644 index 000000000..7e7fb12c8 --- /dev/null +++ b/OpenTabletDriver/Area.cs @@ -0,0 +1,103 @@ +using System.ComponentModel; +using System.Linq; +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A working area designating width and height based at a centered origin position. + /// + [PublicAPI] + public class Area : NotifyPropertyChanged + { + private float _width, _height, _xPosition, _yPosition; + + /// + /// The width of the area. + /// + [DisplayName(nameof(Width))] + public float Width + { + set => RaiseAndSetIfChanged(ref _width, value); + get => _width; + } + + /// + /// The height of the area. + /// + [DisplayName(nameof(Height))] + public float Height + { + set => RaiseAndSetIfChanged(ref _height, value); + get => _height; + } + + /// + /// The X component of the area's center offset. + /// + [DisplayName("X")] + public float XPosition + { + set => RaiseAndSetIfChanged(ref _xPosition, value); + get => _xPosition; + } + + /// + /// The Y component of the area's center offset. + /// + [DisplayName("Y")] + public float YPosition + { + set => RaiseAndSetIfChanged(ref _yPosition, value); + get => _yPosition; + } + + /// + /// Returns the center offset of the area. + /// + /// + /// This is also the rotation origin of the area where applicable. + /// + public Vector2 GetPosition() => new Vector2(XPosition, YPosition); + + /// + /// Returns all corners of the area. + /// + public virtual Vector2[] GetCorners() + { + var halfWidth = Width / 2; + var halfHeight = Height / 2; + + var x = XPosition; + var y = YPosition; + + return new[] + { + new Vector2(x - halfWidth, y - halfHeight), + new Vector2(x - halfWidth, y + halfHeight), + new Vector2(x + halfWidth, y + halfHeight), + new Vector2(x + halfWidth, y - halfHeight) + }; + } + + /// + /// Returns the center offset of the area. + /// + public virtual Vector2 GetCenterOffset() + { + var corners = GetCorners(); + var max = new Vector2( + corners.Max(v => v.X), + corners.Max(v => v.Y) + ); + var min = new Vector2( + corners.Min(v => v.X), + corners.Min(v => v.Y) + ); + return (max - min) / 2; + } + + public override string ToString() => $"[{Width}x{Height}@{GetPosition()}]"; + } +} diff --git a/OpenTabletDriver.Plugin/Attributes/ActionAttribute.cs b/OpenTabletDriver/Attributes/ActionAttribute.cs similarity index 85% rename from OpenTabletDriver.Plugin/Attributes/ActionAttribute.cs rename to OpenTabletDriver/Attributes/ActionAttribute.cs index 782aeccc0..50dd43732 100644 --- a/OpenTabletDriver.Plugin/Attributes/ActionAttribute.cs +++ b/OpenTabletDriver/Attributes/ActionAttribute.cs @@ -1,11 +1,13 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { /// /// Marks a static function to be executed from the client application. /// [AttributeUsage(AttributeTargets.Method)] + [PublicAPI] public class ActionAttribute : Attribute { public ActionAttribute(string groupName, string displayText) diff --git a/OpenTabletDriver.Plugin/Attributes/BuildDateAttribute.cs b/OpenTabletDriver/Attributes/BuildDateAttribute.cs similarity index 50% rename from OpenTabletDriver.Plugin/Attributes/BuildDateAttribute.cs rename to OpenTabletDriver/Attributes/BuildDateAttribute.cs index b2c75344a..99ca5791e 100644 --- a/OpenTabletDriver.Plugin/Attributes/BuildDateAttribute.cs +++ b/OpenTabletDriver/Attributes/BuildDateAttribute.cs @@ -1,14 +1,23 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { + /// + /// Assigns a build date to a target assembly. + /// [AttributeUsage(AttributeTargets.Assembly)] + [PublicAPI] public class BuildDateAttribute : Attribute { - public string BuildDate { get; } public BuildDateAttribute(string buildDate) { BuildDate = buildDate; } + + /// + /// The date in which the assembly was built. + /// + public string BuildDate { get; } } } diff --git a/OpenTabletDriver/Attributes/DeviceHubAttribute.cs b/OpenTabletDriver/Attributes/DeviceHubAttribute.cs new file mode 100644 index 000000000..804e6843b --- /dev/null +++ b/OpenTabletDriver/Attributes/DeviceHubAttribute.cs @@ -0,0 +1,14 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Marks a class as a device hub. + /// + [AttributeUsage(AttributeTargets.Class)] + [PublicAPI] + public class DeviceHubAttribute : Attribute + { + } +} diff --git a/OpenTabletDriver/Attributes/MemberSourcedDefaultsAttribute.cs b/OpenTabletDriver/Attributes/MemberSourcedDefaultsAttribute.cs new file mode 100644 index 000000000..645a63ad9 --- /dev/null +++ b/OpenTabletDriver/Attributes/MemberSourcedDefaultsAttribute.cs @@ -0,0 +1,20 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Applies the default value to a property. + /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] + public class MemberSourcedDefaultsAttribute : Attribute + { + public MemberSourcedDefaultsAttribute(string targetMemberName) + { + TargetMemberName = targetMemberName; + } + + public string TargetMemberName { get; } + } +} diff --git a/OpenTabletDriver/Attributes/MemberValidatedAttribute.cs b/OpenTabletDriver/Attributes/MemberValidatedAttribute.cs new file mode 100644 index 000000000..4b72ea383 --- /dev/null +++ b/OpenTabletDriver/Attributes/MemberValidatedAttribute.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Marks a member that is used for validating setting properties. + /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] + public class MemberValidatedAttribute : Attribute + { + public MemberValidatedAttribute(string memberName, bool requiresInstance = false) + { + MemberName = memberName; + RequiresInstance = requiresInstance; + } + + /// + /// The target member for validating setting properties. + /// + public string MemberName { get; } + + /// + /// Whether an instance of the object is needed for validation to occur. + /// + public bool RequiresInstance { get; } + + public object? GetValue(IServiceProvider serviceProvider, PropertyInfo property) + { + var sourceType = property.ReflectedType!; + var member = sourceType.GetMember(MemberName).First(); + var instance = RequiresInstance ? serviceProvider.GetRequiredService(sourceType) : null; + + try + { + var obj = member.MemberType switch + { + MemberTypes.Property => sourceType.GetProperty(MemberName)!.GetValue(instance), + MemberTypes.Field => sourceType.GetField(MemberName)!.GetValue(instance), + MemberTypes.Method => sourceType.GetMethod(MemberName)!.Invoke(instance, new object[] { serviceProvider }), + _ => default + }; + return obj; + } + catch (Exception e) + { + Log.Write("Plugin", $"Failed to get valid binding values for '{MemberName}'", LogLevel.Error); + + var match = Regex.Match(e.Message, "Non-static (.*) requires a target\\."); + + if (e is TargetException && match.Success) + { + Log.Debug("Plugin", $"Validation {match.Groups[1].Value} must be static"); + } + else + { + Log.Exception(e); + } + } + + return default; + } + } +} diff --git a/OpenTabletDriver.Plugin/Attributes/ModifierAttribute.cs b/OpenTabletDriver/Attributes/ModifierAttribute.cs similarity index 50% rename from OpenTabletDriver.Plugin/Attributes/ModifierAttribute.cs rename to OpenTabletDriver/Attributes/ModifierAttribute.cs index fc2888746..ab961fc83 100644 --- a/OpenTabletDriver.Plugin/Attributes/ModifierAttribute.cs +++ b/OpenTabletDriver/Attributes/ModifierAttribute.cs @@ -1,10 +1,13 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { /// /// Base attribute class for attributes which provide extra information to a property. /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Property)] + [PublicAPI] public abstract class ModifierAttribute : Attribute { } diff --git a/OpenTabletDriver.Plugin/Attributes/PluginIgnoreAttribute.cs b/OpenTabletDriver/Attributes/PluginIgnoreAttribute.cs similarity index 77% rename from OpenTabletDriver.Plugin/Attributes/PluginIgnoreAttribute.cs rename to OpenTabletDriver/Attributes/PluginIgnoreAttribute.cs index 2731d33de..d8558befb 100644 --- a/OpenTabletDriver.Plugin/Attributes/PluginIgnoreAttribute.cs +++ b/OpenTabletDriver/Attributes/PluginIgnoreAttribute.cs @@ -1,11 +1,13 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { /// /// Marks a plugin class to be ignored in reflection calls. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] + [PublicAPI] public class PluginIgnoreAttribute : Attribute { } diff --git a/OpenTabletDriver.Plugin/Attributes/PluginNameAttribute.cs b/OpenTabletDriver/Attributes/PluginNameAttribute.cs similarity index 67% rename from OpenTabletDriver.Plugin/Attributes/PluginNameAttribute.cs rename to OpenTabletDriver/Attributes/PluginNameAttribute.cs index bb65dc84e..806dc5915 100644 --- a/OpenTabletDriver.Plugin/Attributes/PluginNameAttribute.cs +++ b/OpenTabletDriver/Attributes/PluginNameAttribute.cs @@ -1,11 +1,14 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { /// /// Applies a friendly name of a plugin class. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [PublicAPI] public class PluginNameAttribute : Attribute { public PluginNameAttribute(string name) @@ -13,6 +16,6 @@ public PluginNameAttribute(string name) Name = name; } - public readonly string Name; + public string Name { get; } } } diff --git a/OpenTabletDriver/Attributes/RangeSettingAttribute.cs b/OpenTabletDriver/Attributes/RangeSettingAttribute.cs new file mode 100644 index 000000000..1d554ac00 --- /dev/null +++ b/OpenTabletDriver/Attributes/RangeSettingAttribute.cs @@ -0,0 +1,24 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Creates a range setting for a property value between and . + /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] + public class RangeSettingAttribute : SettingAttribute + { + public RangeSettingAttribute(string displayName, float min, float max, float defaultValue = 0f) : base(displayName) + { + Min = min; + Max = max; + DefaultValue = defaultValue; + } + + public float Min { get; } + public float Max { get; } + public float DefaultValue { get; } + } +} diff --git a/OpenTabletDriver/Attributes/SettingAttribute.cs b/OpenTabletDriver/Attributes/SettingAttribute.cs new file mode 100644 index 000000000..df5983084 --- /dev/null +++ b/OpenTabletDriver/Attributes/SettingAttribute.cs @@ -0,0 +1,27 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Marks a property to be modified and saved by a client to settings. + /// + [AttributeUsage(AttributeTargets.Property)] + [MeansImplicitUse(ImplicitUseKindFlags.Assign | ImplicitUseKindFlags.Access)] + [PublicAPI] + public class SettingAttribute : Attribute + { + public SettingAttribute(string displayName) + { + DisplayName = displayName; + } + + public SettingAttribute(string displayName, string? description) : this(displayName) + { + Description = description; + } + + public string DisplayName { get; } + public string? Description { get; } + } +} diff --git a/OpenTabletDriver/Attributes/SupportedPlatformAttribute.cs b/OpenTabletDriver/Attributes/SupportedPlatformAttribute.cs new file mode 100644 index 000000000..20a07e118 --- /dev/null +++ b/OpenTabletDriver/Attributes/SupportedPlatformAttribute.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes +{ + /// + /// Locks a class, struct, or interface to a specific platform. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [PublicAPI] + public class SupportedPlatformAttribute : Attribute + { + public SupportedPlatformAttribute(SystemPlatform platform) + { + Platform = platform; + } + + public SystemPlatform Platform { get; } + + public bool IsCurrentPlatform => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Platform.HasFlag(SystemPlatform.Windows) || + RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && Platform.HasFlag(SystemPlatform.Linux) || + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Platform.HasFlag(SystemPlatform.MacOS) || + RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) && Platform.HasFlag(SystemPlatform.FreeBSD); + } +} diff --git a/OpenTabletDriver.Plugin/Attributes/ToolTipAttribute.cs b/OpenTabletDriver/Attributes/ToolTipAttribute.cs similarity index 55% rename from OpenTabletDriver.Plugin/Attributes/ToolTipAttribute.cs rename to OpenTabletDriver/Attributes/ToolTipAttribute.cs index 540612ce8..f5312335d 100644 --- a/OpenTabletDriver.Plugin/Attributes/ToolTipAttribute.cs +++ b/OpenTabletDriver/Attributes/ToolTipAttribute.cs @@ -1,17 +1,20 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Attributes +namespace OpenTabletDriver.Attributes { /// /// Applies a tooltip to a property on the client. /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] public class ToolTipAttribute : ModifierAttribute { public ToolTipAttribute(string tooltip) { - this.ToolTip = tooltip; + ToolTip = tooltip; } - public string ToolTip { private set; get; } + public string ToolTip { get; } } } diff --git a/OpenTabletDriver/Attributes/UI/LinkedSettingAttribute.cs b/OpenTabletDriver/Attributes/UI/LinkedSettingAttribute.cs new file mode 100644 index 000000000..97aa29d95 --- /dev/null +++ b/OpenTabletDriver/Attributes/UI/LinkedSettingAttribute.cs @@ -0,0 +1,35 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes.UI +{ + /// + /// Designates linking between settings. + /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] + public class LinkedSettingAttribute : Attribute + { + public LinkedSettingAttribute(string targetMemberName, string settingMemberName) + { + TargetMemberName = targetMemberName; + SettingMemberName = settingMemberName; + } + + /// + /// The target setting that is linked. + /// + public string TargetMemberName { get; } + + /// + /// The setting determining whether linking will occur. + /// + public string SettingMemberName { get; } + + public static readonly Type[] SupportedTypes = + { + typeof(Area), + typeof(AngledArea) + }; + } +} diff --git a/OpenTabletDriver/Attributes/UI/LinkedSettingSourceAttribute.cs b/OpenTabletDriver/Attributes/UI/LinkedSettingSourceAttribute.cs new file mode 100644 index 000000000..f33783693 --- /dev/null +++ b/OpenTabletDriver/Attributes/UI/LinkedSettingSourceAttribute.cs @@ -0,0 +1,18 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes.UI +{ + /// + /// Designates a linked setting source. + /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] + public class LinkedSettingSourceAttribute : Attribute + { + public static readonly Type[] SupportedTypes = + { + typeof(bool) + }; + } +} diff --git a/OpenTabletDriver.Plugin/Attributes/UnitAttribute.cs b/OpenTabletDriver/Attributes/UnitAttribute.cs similarity index 51% rename from OpenTabletDriver.Plugin/Attributes/UnitAttribute.cs rename to OpenTabletDriver/Attributes/UnitAttribute.cs index 6cbc438ee..aa24d6b6d 100644 --- a/OpenTabletDriver.Plugin/Attributes/UnitAttribute.cs +++ b/OpenTabletDriver/Attributes/UnitAttribute.cs @@ -1,15 +1,20 @@ -namespace OpenTabletDriver.Plugin.Attributes +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Attributes { /// /// Applies a unit suffix to a property on the client. /// + [AttributeUsage(AttributeTargets.Property)] + [PublicAPI] public class UnitAttribute : ModifierAttribute { public UnitAttribute(string unit) { - this.Unit = unit; + Unit = unit; } - public string Unit { set; get; } + public string Unit { get; } } } diff --git a/OpenTabletDriver/ComponentProviders/DeviceHubsProvider.cs b/OpenTabletDriver/ComponentProviders/DeviceHubsProvider.cs index 01e1c5ad3..f2681741c 100644 --- a/OpenTabletDriver/ComponentProviders/DeviceHubsProvider.cs +++ b/OpenTabletDriver/ComponentProviders/DeviceHubsProvider.cs @@ -2,13 +2,18 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Devices; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Components; +using OpenTabletDriver.Devices; namespace OpenTabletDriver.ComponentProviders { + /// + /// Reflection-sourced device hub provider. + /// + [PublicAPI] public class DeviceHubsProvider : IDeviceHubsProvider { public DeviceHubsProvider(IServiceProvider serviceProvider) @@ -21,6 +26,9 @@ public DeviceHubsProvider(IServiceProvider serviceProvider) .ToArray(); } + /// + /// An enumeration of the reflected device hubs. + /// public IEnumerable DeviceHubs { get; } } } diff --git a/OpenTabletDriver/Components/ICompositeDeviceHub.cs b/OpenTabletDriver/Components/ICompositeDeviceHub.cs new file mode 100644 index 000000000..3433d94d1 --- /dev/null +++ b/OpenTabletDriver/Components/ICompositeDeviceHub.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using OpenTabletDriver.Devices; + +namespace OpenTabletDriver.Components +{ + /// + /// A device hub consisting of one or more device hubs. + /// + [PublicAPI] + public interface ICompositeDeviceHub : IDeviceHub + { + /// + /// The device hubs contained in the composite hub. + /// + IEnumerable DeviceHubs { get; } + + /// + /// Connects a device hub. + /// + /// The device hub type. + void ConnectDeviceHub() where T : IDeviceHub; + + /// + /// Connects a device hub. + /// + /// The device hub instance. + void ConnectDeviceHub(IDeviceHub instance); + + /// + /// Disconnects a device hub. + /// + /// The device hub type. + void DisconnectDeviceHub() where T : IDeviceHub; + + /// + /// Disconnects a device hub. + /// + /// The device hub instance. + void DisconnectDeviceHub(IDeviceHub instance); + } +} diff --git a/OpenTabletDriver/Components/IDeviceConfigurationProvider.cs b/OpenTabletDriver/Components/IDeviceConfigurationProvider.cs new file mode 100644 index 000000000..9e18cc0d4 --- /dev/null +++ b/OpenTabletDriver/Components/IDeviceConfigurationProvider.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Components +{ + /// + /// Provides configurations for all supported tablets. + /// + [PublicAPI] + public interface IDeviceConfigurationProvider + { + /// + /// Enumeration of the configurations for all supported tablets. + /// + IEnumerable TabletConfigurations { get; } + } +} diff --git a/OpenTabletDriver/Components/IDeviceHubsProvider.cs b/OpenTabletDriver/Components/IDeviceHubsProvider.cs new file mode 100644 index 000000000..99c6c92b5 --- /dev/null +++ b/OpenTabletDriver/Components/IDeviceHubsProvider.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using OpenTabletDriver.Devices; + +namespace OpenTabletDriver.Components +{ + /// + /// Provides an enumeration of all supported device hubs. + /// + [PublicAPI] + public interface IDeviceHubsProvider + { + /// + /// Enumeration of all supported device hubs. + /// + IEnumerable DeviceHubs { get; } + } +} diff --git a/OpenTabletDriver/Components/IReportParserProvider.cs b/OpenTabletDriver/Components/IReportParserProvider.cs new file mode 100644 index 000000000..f4b4a8ef1 --- /dev/null +++ b/OpenTabletDriver/Components/IReportParserProvider.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Components +{ + /// + /// Provides a provider for all supported report parsers. + /// + [PublicAPI] + public interface IReportParserProvider + { + /// + /// Requests a device report parser by a specific type path. + /// + /// The type path for the report parser. + /// An instance of the requested device report parser. + IReportParser GetReportParser(string reportParserName); + } +} diff --git a/OpenTabletDriver/Devices/DeviceReader.cs b/OpenTabletDriver/Devices/DeviceReader.cs index 834985c89..be0e709c3 100644 --- a/OpenTabletDriver/Devices/DeviceReader.cs +++ b/OpenTabletDriver/Devices/DeviceReader.cs @@ -1,19 +1,20 @@ using System; using System.IO; using System.Threading; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Tablet; +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver.Devices { + [PublicAPI] public class DeviceReader : IDisposable where T : IDeviceReport { public DeviceReader(IDeviceEndpoint endpoint, IReportParser reportParser) { Endpoint = endpoint; Parser = reportParser; - workerThread = new Thread(Main) + + _workerThread = new Thread(Main) { Name = "OpenTabletDriver Device Reader", IsBackground = true, @@ -21,8 +22,8 @@ public DeviceReader(IDeviceEndpoint endpoint, IReportParser reportParser) }; } - private readonly Thread workerThread; - private bool initialized, connected; + private readonly Thread _workerThread; + private bool _initialized, _connected; /// /// The device endpoint in which is reporting data in the . @@ -32,12 +33,12 @@ public DeviceReader(IDeviceEndpoint endpoint, IReportParser reportParser) /// /// The raw device endpoint report stream. /// - public IDeviceEndpointStream ReportStream { protected set; get; } + public IDeviceEndpointStream? ReportStream { private set; get; } /// - /// The in which the device reports will be parsed with. + /// The in which the device reports will be parsed with. /// - public IReportParser Parser { private set; get; } + public IReportParser Parser { get; } /// /// Whether or not to make an extra cloned report with data left unmodified. @@ -47,7 +48,7 @@ public DeviceReader(IDeviceEndpoint endpoint, IReportParser reportParser) /// /// Invoked when a new report comes in from the device. /// - public event EventHandler Report; + public event EventHandler? Report; /// /// Invoked when a new report comes in from the device. @@ -56,7 +57,7 @@ public DeviceReader(IDeviceEndpoint endpoint, IReportParser reportParser) /// This will only be invoked when is set to true. /// This report is not meant in any way to be modified, as it is supposed to represent the original data. /// - public event EventHandler RawReport; + public event EventHandler? RawReport; /// /// Whether or not the device is actively emitting reports and being parsed. @@ -65,23 +66,23 @@ public bool Connected { protected set { - connected = value; + _connected = value; ConnectionStateChanged?.Invoke(this, Connected); } - get => connected; + get => _connected; } /// /// Invoked when is changed. /// - public event EventHandler ConnectionStateChanged; + public event EventHandler? ConnectionStateChanged; protected virtual bool Initialize() { try { ReportStream = Endpoint.Open(); - return true; + return ReportStream != null; } catch (Exception ex) { @@ -90,24 +91,24 @@ protected virtual bool Initialize() } } - protected virtual void Start() + protected void Start() { - if (!initialized) - initialized = Initialize(); + if (!_initialized) + _initialized = Initialize(); - if (initialized) - workerThread.Start(); + if (_initialized) + _workerThread.Start(); } - protected void Main() + private void Main() { try { Connected = true; while (Connected) { - var data = ReportStream.Read(); - if (Parser.Parse(data) is T report) + var data = ReportStream!.Read(); + if (Parser.Parse(data) is T report) OnReport(report); // We create a clone of the report to avoid data being modified on the tablet debugger. @@ -119,7 +120,7 @@ protected void Main() { Log.Debug("Device", $"{(string.IsNullOrWhiteSpace(dex.ObjectName) ? "A device stream" : dex.ObjectName)} was disposed."); } - catch (IOException ioex) when (ioex.Message == "I/O disconnected." || ioex.Message == "Operation failed after some time.") + catch (IOException ioEx) when (ioEx.Message == "I/O disconnected." || ioEx.Message == "Operation failed after some time.") { Log.Write("Device", "Device disconnected."); } @@ -137,8 +138,8 @@ protected void Main() } } - protected virtual void OnReport(T report) => Report?.Invoke(this, report); - protected virtual void OnRawReport(T report) => RawReport?.Invoke(this, report); + private void OnReport(T report) => Report?.Invoke(this, report); + private void OnRawReport(T report) => RawReport?.Invoke(this, report); public void Dispose() { diff --git a/OpenTabletDriver.Plugin/Devices/DevicesChangedEventArgs.cs b/OpenTabletDriver/Devices/DevicesChangedEventArgs.cs similarity index 56% rename from OpenTabletDriver.Plugin/Devices/DevicesChangedEventArgs.cs rename to OpenTabletDriver/Devices/DevicesChangedEventArgs.cs index 431f75d3d..8988128d0 100644 --- a/OpenTabletDriver.Plugin/Devices/DevicesChangedEventArgs.cs +++ b/OpenTabletDriver/Devices/DevicesChangedEventArgs.cs @@ -1,30 +1,35 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Devices +namespace OpenTabletDriver.Devices { + /// + /// Event triggered by addition or removal of devices. + /// + [PublicAPI] public class DevicesChangedEventArgs : EventArgs { - public DevicesChangedEventArgs(IEnumerable oldList, IEnumerable newList) + public DevicesChangedEventArgs(IEnumerable? oldList, IEnumerable newList) { Previous = oldList; Current = newList; } - public IEnumerable Previous { get; } + public IEnumerable? Previous { get; } public IEnumerable Current { get; } - public IEnumerable Additions => Current.Except(Previous, Comparer); - public IEnumerable Removals => Previous.Except(Current, Comparer); + public IEnumerable Additions => Current.Except(Previous ?? Array.Empty(), Comparer); + public IEnumerable Removals => Previous?.Except(Current, Comparer) ?? Current; public IEnumerable Changes => Additions.Concat(Removals); public static readonly IEqualityComparer Comparer = new DeviceEndpointComparer(); private class DeviceEndpointComparer : IEqualityComparer { - public bool Equals(IDeviceEndpoint x, IDeviceEndpoint y) => x?.DevicePath == y?.DevicePath; - public int GetHashCode(IDeviceEndpoint obj) => obj?.DevicePath?.GetHashCode() ?? 0; + public bool Equals(IDeviceEndpoint? x, IDeviceEndpoint? y) => x?.DevicePath == y?.DevicePath; + public int GetHashCode(IDeviceEndpoint? obj) => obj?.DevicePath.GetHashCode() ?? 0; } } } diff --git a/OpenTabletDriver/Devices/HidSharpBackend/Extensions.cs b/OpenTabletDriver/Devices/HidSharpBackend/Extensions.cs new file mode 100644 index 000000000..27c1aee83 --- /dev/null +++ b/OpenTabletDriver/Devices/HidSharpBackend/Extensions.cs @@ -0,0 +1,31 @@ +using System; + +namespace OpenTabletDriver.Devices.HidSharpBackend +{ + internal static class Extensions + { + private static bool TryGet( + this TSource source, + Func predicate, + out TValue? value + ) + { + try + { + value = predicate(source); + return true; + } + catch + { + value = default; + return false; + } + } + + public static TValue SafeGet( + this TSource source, + Func predicate, + TValue fallback + ) => TryGet(source, predicate, out var value) ? value! : fallback; + } +} diff --git a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpDeviceRoot.cs b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpDeviceRoot.cs index ad33f66ea..863032023 100644 --- a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpDeviceRoot.cs +++ b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpDeviceRoot.cs @@ -2,31 +2,34 @@ using System.Collections.Generic; using System.Linq; using HidSharp; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Devices; +using JetBrains.Annotations; +using OpenTabletDriver.Attributes; namespace OpenTabletDriver.Devices.HidSharpBackend { - [DeviceHub] - public class HidSharpDeviceRootHub : IDeviceHub + /// + /// Device hub implemented with HidSharpCore. + /// + [DeviceHub, PublicAPI] + public sealed class HidSharpDeviceRootHub : IDeviceHub { public HidSharpDeviceRootHub() { DeviceList.Local.Changed += (sender, e) => { var newList = DeviceList.Local.GetHidDevices().Select(d => new HidSharpEndpoint(d)); - var changes = new DevicesChangedEventArgs(hidDevices, newList); + var changes = new DevicesChangedEventArgs(_hidDevices, newList); if (changes.Changes.Any()) { DevicesChanged?.Invoke(this, changes); - hidDevices = newList; + _hidDevices = newList; } }; } - private IEnumerable hidDevices = DeviceList.Local.GetHidDevices().Select(d => new HidSharpEndpoint(d)); + private IEnumerable _hidDevices = DeviceList.Local.GetHidDevices().Select(d => new HidSharpEndpoint(d)); - public event EventHandler DevicesChanged; + public event EventHandler? DevicesChanged; public IEnumerable GetDevices() { diff --git a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpoint.cs b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpoint.cs index faf71400e..ce2089a1e 100644 --- a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpoint.cs +++ b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpoint.cs @@ -1,32 +1,30 @@ using HidSharp; -using OpenTabletDriver; -using OpenTabletDriver.Plugin.Devices; namespace OpenTabletDriver.Devices.HidSharpBackend { - public class HidSharpEndpoint : IDeviceEndpoint + internal sealed class HidSharpEndpoint : IDeviceEndpoint { - internal HidSharpEndpoint(HidDevice device) + public HidSharpEndpoint(HidDevice device) { - this.device = device; + _device = device; } - private HidDevice device; + private readonly HidDevice _device; - public int ProductID => device.ProductID; - public int VendorID => device.VendorID; - public int InputReportLength => device.SafeGet(d => d.GetMaxInputReportLength(), -1); - public int OutputReportLength => device.SafeGet(d => d.GetMaxOutputReportLength(), -1); - public int FeatureReportLength => device.SafeGet(d => d.GetMaxFeatureReportLength(), -1); + public int ProductID => _device.ProductID; + public int VendorID => _device.VendorID; + public int InputReportLength => _device.SafeGet(d => d.GetMaxInputReportLength(), -1); + public int OutputReportLength => _device.SafeGet(d => d.GetMaxOutputReportLength(), -1); + public int FeatureReportLength => _device.SafeGet(d => d.GetMaxFeatureReportLength(), -1); - public string Manufacturer => device.SafeGet(d => d.GetManufacturer(), "Unknown Manufacturer"); - public string ProductName => device.SafeGet(d => d.GetProductName(), "Unknown Product Name"); - public string FriendlyName => device.SafeGet(d => d.GetFriendlyName(), "Unknown Product Name"); - public string SerialNumber => device.SafeGet(d => d.GetSerialNumber(), string.Empty); - public string DevicePath => device.SafeGet(d => d.DevicePath, "Invalid Device Path"); - public bool CanOpen => device.SafeGet(d => d.CanOpen, false); + public string Manufacturer => _device.SafeGet(d => d.GetManufacturer(), "Unknown Manufacturer"); + public string ProductName => _device.SafeGet(d => d.GetProductName(), "Unknown Product Name"); + public string FriendlyName => _device.SafeGet(d => d.GetFriendlyName(), "Unknown Product Name"); + public string? SerialNumber => _device.SafeGet(d => d.GetSerialNumber(), null); + public string DevicePath => _device.SafeGet(d => d.DevicePath, "Invalid Device Path"); + public bool CanOpen => _device.SafeGet(d => d.CanOpen, false); - public IDeviceEndpointStream Open() => device.TryOpen(out var stream) ? new HidSharpEndpointStream(stream) : null; - public string GetDeviceString(byte index) => device.GetDeviceString(index); + public IDeviceEndpointStream? Open() => _device.TryOpen(out var stream) ? new HidSharpEndpointStream(stream) : null; + public string GetDeviceString(byte index) => _device.GetDeviceString(index); } } diff --git a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpointStream.cs b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpointStream.cs index 6022546db..f79d090ab 100644 --- a/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpointStream.cs +++ b/OpenTabletDriver/Devices/HidSharpBackend/HidSharpEndpointStream.cs @@ -1,25 +1,23 @@ -using System.IO; using HidSharp; -using OpenTabletDriver.Plugin.Devices; namespace OpenTabletDriver.Devices.HidSharpBackend { - public class HidSharpEndpointStream : IDeviceEndpointStream + internal sealed class HidSharpEndpointStream : IDeviceEndpointStream { - internal HidSharpEndpointStream(HidStream stream) + public HidSharpEndpointStream(HidStream stream) { - this.stream = stream; - stream.ReadTimeout = int.MaxValue; + _stream = stream; + _stream.ReadTimeout = int.MaxValue; } - private HidStream stream; + private readonly HidStream _stream; - public byte[] Read() => stream.Read(); - public void Write(byte[] buffer) => stream.Write(buffer); + public byte[] Read() => _stream.Read(); + public void Write(byte[] buffer) => _stream.Write(buffer); - public void GetFeature(byte[] buffer) => stream.GetFeature(buffer); - public void SetFeature(byte[] buffer) => stream.SetFeature(buffer); + public void GetFeature(byte[] buffer) => _stream.GetFeature(buffer); + public void SetFeature(byte[] buffer) => _stream.SetFeature(buffer); - public void Dispose() => stream.Dispose(); + public void Dispose() => _stream.Dispose(); } } diff --git a/OpenTabletDriver/Devices/IDeviceEndpoint.cs b/OpenTabletDriver/Devices/IDeviceEndpoint.cs new file mode 100644 index 000000000..ee19d14da --- /dev/null +++ b/OpenTabletDriver/Devices/IDeviceEndpoint.cs @@ -0,0 +1,92 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Devices +{ + /// + /// A USB device endpoint. + /// + [PublicAPI] + public interface IDeviceEndpoint + { + /// + /// The device's product identifier. This is assigned by the vendor. + /// + int ProductID { get; } + + /// + /// The device's vendor identifier. This is assigned by the USB-IF. + /// + /// + /// See https://www.usb.org/developers for more information. + /// + int VendorID { get; } + + /// + /// The length of the device endpoint's input reports. + /// + int InputReportLength { get; } + + /// + /// The length of the device endpoint's output reports. + /// + int OutputReportLength { get; } + + /// + /// The length of the device endpoint's feature reports. + /// + int FeatureReportLength { get; } + + /// + /// The device's manufacturer. + /// + string? Manufacturer { get; } + + /// + /// The device's product name assigned by the vendor. + /// + string? ProductName { get; } + + /// + /// The device's product name assigned by the vendor. + /// + string? FriendlyName { get; } + + /// + /// The device's serial number, if applicable. + /// + string? SerialNumber { get; } + + /// + /// The internal system device path. + /// This often varies by platform and host device. + /// + string DevicePath { get; } + + /// + /// Whether you can open the device endpoint stream. + /// + /// + /// When false, it may indicate permission issues or that the device is exclusively opened elsewhere. + /// + bool CanOpen { get; } + + /// + /// Opens the device endpoint stream for read/write. + /// + /// + /// A if successful, otherwise null. + /// + IDeviceEndpointStream? Open(); + + /// + /// Requests a USB device string embedded in the device firmware. + /// + /// + /// The index in which to query. + /// + /// + /// The requested string from device firmware, or null. + /// + string? GetDeviceString(byte index); + } +} diff --git a/OpenTabletDriver/Devices/IDeviceEndpointStream.cs b/OpenTabletDriver/Devices/IDeviceEndpointStream.cs new file mode 100644 index 000000000..016011021 --- /dev/null +++ b/OpenTabletDriver/Devices/IDeviceEndpointStream.cs @@ -0,0 +1,50 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Devices +{ + /// + /// A USB device endpoint stream. + /// + [PublicAPI] + public interface IDeviceEndpointStream : IDisposable + { + /// + /// Reads the next report from the device endpoint. + /// + /// + /// This method blocks until a full report is received. + /// + /// + /// A device report. + /// + byte[] Read(); + + /// + /// Writes to the device endpoint stream. + /// + /// + /// This method blocks until the write is complete. + /// + /// + /// The data in which to write to the device endpoint stream. + /// + void Write(byte[] buffer); + + /// + /// Invokes GetFeature on the device endpoint. + /// + /// + /// The data in which to pass. + /// + void GetFeature(byte[] buffer); + + /// + /// Invokes SetFeature on the device endpoint. + /// + /// + /// The data in which to pass. + /// + void SetFeature(byte[] buffer); + } +} diff --git a/OpenTabletDriver/Devices/IDeviceHub.cs b/OpenTabletDriver/Devices/IDeviceHub.cs new file mode 100644 index 000000000..02590e2d7 --- /dev/null +++ b/OpenTabletDriver/Devices/IDeviceHub.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Devices +{ + /// + /// A device hub, providing an enumeration of connected devices. + /// + [PublicAPI] + public interface IDeviceHub + { + /// + /// Invoked when device changes occur. + /// + event EventHandler? DevicesChanged; + + /// + /// Requests all connected devices from the hub. + /// + /// + /// An enumeration of + /// + IEnumerable GetDevices(); + } +} diff --git a/OpenTabletDriver/Devices/RootHub.cs b/OpenTabletDriver/Devices/RootHub.cs index 2a2ca0156..fa7b1686d 100644 --- a/OpenTabletDriver/Devices/RootHub.cs +++ b/OpenTabletDriver/Devices/RootHub.cs @@ -3,43 +3,44 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Devices; - -#nullable enable +using OpenTabletDriver.Components; namespace OpenTabletDriver.Devices { - public class RootHub : ICompositeDeviceHub, IDeviceHub + /// + /// A reflection-sourced composite root hub. + /// + [PublicAPI] + public class RootHub : ICompositeDeviceHub { public RootHub(IDeviceHubsProvider hubsProvider) { - internalHubs = hubsProvider.DeviceHubs.ToHashSet(); - hubs = new HashSet(internalHubs); + var internalHubs = hubsProvider.DeviceHubs.ToHashSet(); + _hubs = new HashSet(internalHubs); + ForceEnumeration(); - foreach (var hub in hubs) + foreach (var hub in _hubs) { HookDeviceNotification(hub); } - Log.Write(nameof(RootHub), $"Initialized internal child hubs: {string.Join(", ", hubs.Select(h => h.GetType().Name))}", LogLevel.Debug); + Log.Write(nameof(RootHub), $"Initialized internal child hubs: {string.Join(", ", _hubs.Select(h => h.GetType().Name))}", LogLevel.Debug); } - private readonly object syncObject = new(); - private readonly HashSet internalHubs; - private readonly HashSet hubs; - private List? oldEndpoints; - private readonly List endpoints = new(); - private long version; - private int currentlyDebouncing; - private IServiceProvider? serviceProvider; + private readonly object _syncObject = new object(); + private readonly HashSet _hubs; + private List? _oldEndpoints; + private readonly List _endpoints = new List(); + private long _version; + private int _currentlyDebouncing; + private IServiceProvider? _serviceProvider; public event EventHandler? DevicesChanged; - public IEnumerable DeviceHubs => hubs; + public IEnumerable DeviceHubs => _hubs; public static RootHub WithProvider(IServiceProvider provider) { @@ -49,21 +50,21 @@ public static RootHub WithProvider(IServiceProvider provider) public IEnumerable GetDevices() { - return endpoints; + return _endpoints; } public void ConnectDeviceHub() where T : IDeviceHub { - if (serviceProvider == null) + if (_serviceProvider == null) throw new InvalidOperationException("Cannot instantiate device hubs without a service provider"); - ConnectDeviceHub(ActivatorUtilities.CreateInstance(serviceProvider!)); + ConnectDeviceHub(ActivatorUtilities.CreateInstance(_serviceProvider!)); } public void ConnectDeviceHub(IDeviceHub rootHub) { Log.Write(nameof(RootHub), $"Connecting hub: {rootHub.GetType().Name}", LogLevel.Debug); - if (hubs.Add(rootHub)) + if (_hubs.Add(rootHub)) { CommitHubChange(); HookDeviceNotification(rootHub); @@ -85,7 +86,7 @@ public void DisconnectDeviceHub() where T : IDeviceHub public void DisconnectDeviceHub(IDeviceHub rootHub) { Log.Write(nameof(RootHub), $"Disconnecting hub: {rootHub.GetType().Name}", LogLevel.Debug); - if (hubs.Remove(rootHub)) + if (_hubs.Remove(rootHub)) { CommitHubChange(); UnhookDeviceNotification(rootHub); @@ -98,32 +99,32 @@ public void DisconnectDeviceHub(IDeviceHub rootHub) private async void OnDevicesChanged(object? sender, DevicesChangedEventArgs eventArgs) { - var lastVersion = Interlocked.Increment(ref version); - if (Interlocked.Increment(ref currentlyDebouncing) == 1) + var lastVersion = Interlocked.Increment(ref _version); + if (Interlocked.Increment(ref _currentlyDebouncing) == 1) { // This event is the first of a potential sequence of events, copy old endpoint list - oldEndpoints = new List(endpoints); + _oldEndpoints = new List(_endpoints); } - lock (syncObject) + lock (_syncObject) { - endpoints.RemoveAll(e => eventArgs.Removals.Contains(e, DevicesChangedEventArgs.Comparer)); - endpoints.AddRange(eventArgs.Additions); + _endpoints.RemoveAll(e => eventArgs.Removals.Contains(e, DevicesChangedEventArgs.Comparer)); + _endpoints.AddRange(eventArgs.Additions); } await Task.Delay(20); - if (version == lastVersion) + if (_version == lastVersion) { Log.Write(nameof(RootHub), "Invoking DevicesChanged", LogLevel.Debug); - DevicesChanged?.Invoke(this, new DevicesChangedEventArgs(oldEndpoints, endpoints)); + DevicesChanged?.Invoke(this, new DevicesChangedEventArgs(_oldEndpoints, _endpoints)); } else { Log.Write(nameof(RootHub), $"Debounced {sender?.GetType().Name}'s DevicesChanged event"); } - Interlocked.Decrement(ref currentlyDebouncing); + Interlocked.Decrement(ref _currentlyDebouncing); } private void HookDeviceNotification(IDeviceHub rootHub) @@ -140,28 +141,28 @@ private void HandleHubDeviceNotification(object? sender, DevicesChangedEventArgs { if (eventArgs.Changes.Any()) { - Log.Debug(sender?.GetType().Name, $"Changes: {eventArgs.Changes.Count()}, Add: {eventArgs.Additions.Count()}, Remove: {eventArgs.Removals.Count()}"); + Log.Debug(sender?.GetType().Name ?? nameof(RootHub), $"Changes: {eventArgs.Changes.Count()}, Add: {eventArgs.Additions.Count()}, Remove: {eventArgs.Removals.Count()}"); OnDevicesChanged(sender, eventArgs); } } private void CommitHubChange() { - oldEndpoints = new List(endpoints); + _oldEndpoints = new List(_endpoints); ForceEnumeration(); - DevicesChanged?.Invoke(this, new DevicesChangedEventArgs(endpoints, oldEndpoints)); - oldEndpoints = null; + DevicesChanged?.Invoke(this, new DevicesChangedEventArgs(_endpoints, _oldEndpoints)); + _oldEndpoints = null; } private void ForceEnumeration() { - endpoints.Clear(); - endpoints.AddRange(hubs.SelectMany(h => h.GetDevices())); + _endpoints.Clear(); + _endpoints.AddRange(_hubs.SelectMany(h => h.GetDevices())); } private RootHub RegisterServiceProvider(IServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; + this._serviceProvider = serviceProvider; return this; } } diff --git a/OpenTabletDriver/Devices/WinUSB/WinUSBInterface.cs b/OpenTabletDriver/Devices/WinUSB/WinUSBInterface.cs index 3135b91b3..d93e5dd4b 100644 --- a/OpenTabletDriver/Devices/WinUSB/WinUSBInterface.cs +++ b/OpenTabletDriver/Devices/WinUSB/WinUSBInterface.cs @@ -4,13 +4,12 @@ using System.Threading; using OpenTabletDriver.Native.Windows; using OpenTabletDriver.Native.Windows.USB; -using OpenTabletDriver.Plugin.Devices; using static OpenTabletDriver.Native.Windows.Windows; using static OpenTabletDriver.Native.Windows.WinUsb; namespace OpenTabletDriver.Devices.WinUSB { - public class WinUSBInterface : IDeviceEndpoint + internal class WinUSBInterface : IDeviceEndpoint { public unsafe WinUSBInterface(string devicePath) { @@ -67,11 +66,11 @@ public unsafe WinUSBInterface(string devicePath) VendorID = deviceDescriptor.idVendor; Manufacturer = deviceDescriptor.iManufacturer != 0 - ? GetDeviceString(deviceDescriptor.iManufacturer) + ? GetDeviceString(deviceDescriptor.iManufacturer) ?? string.Empty : "Unknown Manufacturer"; ProductName = deviceDescriptor.iProduct != 0 - ? GetDeviceString(deviceDescriptor.iProduct) + ? GetDeviceString(deviceDescriptor.iProduct) ?? string.Empty : "Unknown Product Name"; SerialNumber = deviceDescriptor.iSerialNumber != 0 @@ -80,37 +79,37 @@ public unsafe WinUSBInterface(string devicePath) }); } - private int referenceCount; - private SafeFileHandle activeFileHandle; - private SafeWinUsbInterfaceHandle activeWinUsbHandle; + private int _referenceCount; + private SafeFileHandle? _activeFileHandle; + private SafeWinUsbInterfaceHandle? _activeWinUsbHandle; - internal int InterfaceNum { get; private set; } - internal byte? InputPipe { get; private set; } - internal byte? OutputPipe { get; private set; } + internal int InterfaceNum { private set; get; } + internal byte? InputPipe { private set; get; } + internal byte? OutputPipe { private set; get; } - public int ProductID { get; private set; } + public int ProductID { private set; get; } - public int VendorID { get; private set; } + public int VendorID { private set; get; } - public int InputReportLength { get; private set; } + public int InputReportLength { private set; get; } - public int OutputReportLength { get; private set; } + public int OutputReportLength { private set; get; } public int FeatureReportLength => 0; // requires parsing report descriptor to determine feature report length - public string Manufacturer { get; private set; } + public string? Manufacturer { private set; get; } - public string ProductName { get; private set; } + public string? ProductName { private set; get; } - public string FriendlyName => ProductName; + public string? FriendlyName => ProductName; - public string SerialNumber { get; private set; } + public string? SerialNumber { private set; get; } public string DevicePath { get; } public bool CanOpen => true; - public unsafe string GetDeviceString(byte index) + public unsafe string? GetDeviceString(byte index) { return WithHandle(winUsbHandle => { @@ -136,7 +135,7 @@ public IDeviceEndpointStream Open() return new WinUSBInterfaceStream(new WeakReference(this)); } - private void WithHandle(Action predicate) + private void WithHandle(Action predicate) { var handle = BorrowHandle(); try @@ -149,7 +148,7 @@ private void WithHandle(Action predicate) } } - private T WithHandle(Func predicate) + private T WithHandle(Func predicate) { var handle = BorrowHandle(); try @@ -162,11 +161,11 @@ private T WithHandle(Func predicate) } } - internal SafeWinUsbInterfaceHandle BorrowHandle() + internal SafeWinUsbInterfaceHandle? BorrowHandle() { - if (Interlocked.Increment(ref referenceCount) == 1) + if (Interlocked.Increment(ref _referenceCount) == 1) { - activeFileHandle = CreateFile(DevicePath, + _activeFileHandle = CreateFile(DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, @@ -175,29 +174,29 @@ internal SafeWinUsbInterfaceHandle BorrowHandle() IntPtr.Zero ); - if (activeFileHandle.IsInvalid) + if (_activeFileHandle.IsInvalid) throw new IOException("Failed to open file handle to WinUSB interface"); - if (!WinUsb_Initialize(activeFileHandle, out activeWinUsbHandle)) + if (!WinUsb_Initialize(_activeFileHandle, out _activeWinUsbHandle)) throw new IOException("Failed to initialize WinUSB interface"); } - return activeWinUsbHandle; + return _activeWinUsbHandle; } // Take reference, so we may easily add multiple interface support in the future - internal void ReturnHandle(SafeWinUsbInterfaceHandle handle) + internal void ReturnHandle(SafeWinUsbInterfaceHandle? handle) { - if (activeWinUsbHandle != handle) + if (_activeWinUsbHandle != handle) throw new InvalidOperationException("Returning handle is not equal to active handle"); - if (Interlocked.Decrement(ref referenceCount) == 0) + if (Interlocked.Decrement(ref _referenceCount) == 0) { - activeWinUsbHandle.Dispose(); - activeFileHandle.Dispose(); + _activeWinUsbHandle?.Dispose(); + _activeFileHandle?.Dispose(); - activeWinUsbHandle = null; - activeFileHandle = null; + _activeWinUsbHandle = null; + _activeFileHandle = null; } } } diff --git a/OpenTabletDriver/Devices/WinUSB/WinUSBInterfaceStream.cs b/OpenTabletDriver/Devices/WinUSB/WinUSBInterfaceStream.cs index fef8f1860..920473f5f 100644 --- a/OpenTabletDriver/Devices/WinUSB/WinUSBInterfaceStream.cs +++ b/OpenTabletDriver/Devices/WinUSB/WinUSBInterfaceStream.cs @@ -1,71 +1,69 @@ using System; -using System.IO; using System.Runtime.CompilerServices; using OpenTabletDriver.Native.Windows.USB; -using OpenTabletDriver.Plugin.Devices; using static OpenTabletDriver.Native.Windows.WinUsb; namespace OpenTabletDriver.Devices.WinUSB { - public unsafe class WinUSBInterfaceStream : IDeviceEndpointStream + internal unsafe class WinUSBInterfaceStream : IDeviceEndpointStream { - private readonly WeakReference parentInterface; - private readonly int interfaceNum; - private readonly SafeWinUsbInterfaceHandle winUsbHandle; - private readonly byte readPipe; - private readonly byte writePipe; - private readonly byte[] readBuffer; - private readonly byte* readPtr; - private readonly byte[] writeBuffer; - private readonly byte* writePtr; + private readonly WeakReference _parentInterface; + private readonly int _interfaceNum; + private readonly SafeWinUsbInterfaceHandle? _winUsbHandle; + private readonly byte _readPipe; + private readonly byte _writePipe; + private readonly byte[] _readBuffer = Array.Empty(); + private readonly byte* _readPtr; + private readonly byte[] _writeBuffer = Array.Empty(); + private readonly byte* _writePtr; public WinUSBInterfaceStream(WeakReference parentInterface) { - this.parentInterface = parentInterface; + _parentInterface = parentInterface; if (!parentInterface.TryGetTarget(out var usbInterface)) throw new InvalidOperationException("Weak reference to parent interface is unexpectedly invalid"); - this.interfaceNum = usbInterface.InterfaceNum; + _interfaceNum = usbInterface.InterfaceNum; - winUsbHandle = usbInterface.BorrowHandle(); + _winUsbHandle = usbInterface.BorrowHandle(); if (usbInterface.InputPipe is byte readPipe) { - this.readPipe = readPipe; - readBuffer = GC.AllocateArray(usbInterface.InputReportLength, true); - readPtr = (byte*)Unsafe.AsPointer(ref readBuffer[0]); + _readPipe = readPipe; + _readBuffer = GC.AllocateArray(usbInterface.InputReportLength, true); + _readPtr = (byte*)Unsafe.AsPointer(ref _readBuffer[0]); } if (usbInterface.OutputPipe is byte writePipe) { - this.writePipe = writePipe; - writeBuffer = GC.AllocateArray(usbInterface.OutputReportLength, true); - writePtr = (byte*)Unsafe.AsPointer(ref writeBuffer[0]); + _writePipe = writePipe; + _writeBuffer = GC.AllocateArray(usbInterface.OutputReportLength, true); + _writePtr = (byte*)Unsafe.AsPointer(ref _writeBuffer[0]); } } public byte[] Read() { - WinUsb_ReadPipe(winUsbHandle, readPipe, readPtr, (uint)readBuffer.Length, out var bytesRead, null); - return bytesRead < readBuffer.Length - ? readBuffer.AsSpan(0, (int)bytesRead).ToArray() - : readBuffer; + WinUsb_ReadPipe(_winUsbHandle, _readPipe, _readPtr, (uint)_readBuffer.Length, out var bytesRead, null); + return bytesRead < _readBuffer.Length + ? _readBuffer.AsSpan(0, (int)bytesRead).ToArray() + : _readBuffer; } public void Write(byte[] buffer) { - if (buffer.Length < writeBuffer.Length) + if (buffer.Length < _writeBuffer.Length) { - writeBuffer.AsSpan().Clear(); - buffer.AsSpan().CopyTo(writeBuffer); - WinUsb_WritePipe(winUsbHandle, writePipe, writePtr, (uint)writeBuffer.Length, out _, null); + _writeBuffer.AsSpan().Clear(); + buffer.AsSpan().CopyTo(_writeBuffer); + WinUsb_WritePipe(_winUsbHandle, _writePipe, _writePtr, (uint)_writeBuffer.Length, out _, null); } else { fixed (void* bufferPtr = &buffer[0]) { - WinUsb_WritePipe(winUsbHandle, writePipe, bufferPtr, (uint)buffer.Length, out _, null); + WinUsb_WritePipe(_winUsbHandle, _writePipe, bufferPtr, (uint)buffer.Length, out _, null); } } } @@ -78,13 +76,13 @@ public unsafe void GetFeature(byte[] buffer) bmRequestType = new RequestType(RequestDirection.DeviceToHost, RequestInternalType.Class, RequestRecipient.Interface), bRequest = 0x01, // GET_REPORT wValue = (ushort)((buffer[0] & 0xff) | (0x0300)), - wIndex = (ushort)interfaceNum, + wIndex = (ushort)_interfaceNum, wLength = (ushort)length }; fixed (void* bufferPtr = &buffer[0]) { - WinUsb_ControlTransfer(winUsbHandle, packet, bufferPtr, (uint)length, out _, null); + WinUsb_ControlTransfer(_winUsbHandle, packet, bufferPtr, (uint)length, out _, null); } } @@ -96,20 +94,20 @@ public void SetFeature(byte[] buffer) bmRequestType = new RequestType(RequestDirection.HostToDevice, RequestInternalType.Class, RequestRecipient.Interface), bRequest = 0x09, // SET_REPORT wValue = (ushort)((buffer[0] & 0xff) | (0x0300)), - wIndex = (ushort)interfaceNum, + wIndex = (ushort)_interfaceNum, wLength = (ushort)length }; fixed (void* bufferPtr = &buffer[0]) { - WinUsb_ControlTransfer(winUsbHandle, packet, bufferPtr, (uint)length, out _, null); + WinUsb_ControlTransfer(_winUsbHandle, packet, bufferPtr, (uint)length, out _, null); } } public void Dispose() { - if (parentInterface.TryGetTarget(out var usbInterface)) - usbInterface.ReturnHandle(winUsbHandle); + if (_parentInterface.TryGetTarget(out var usbInterface)) + usbInterface.ReturnHandle(_winUsbHandle); } } } diff --git a/OpenTabletDriver/Devices/WinUSB/WinUSBRootHub.cs b/OpenTabletDriver/Devices/WinUSB/WinUSBRootHub.cs index c2893f671..b2ee67762 100644 --- a/OpenTabletDriver/Devices/WinUSB/WinUSBRootHub.cs +++ b/OpenTabletDriver/Devices/WinUSB/WinUSBRootHub.cs @@ -2,21 +2,19 @@ using System.Collections.Generic; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; +using OpenTabletDriver.Attributes; using OpenTabletDriver.Native.Windows.CM; using OpenTabletDriver.Native.Windows.SetupApiStructs; using OpenTabletDriver.Native.Windows.USB; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Devices; using static OpenTabletDriver.Native.Windows.CfgMgr32; using static OpenTabletDriver.Native.Windows.SetupAPI; namespace OpenTabletDriver.Devices.WinUSB { - [DeviceHub, SupportedPlatform(PluginPlatform.Windows)] + [DeviceHub, SupportedPlatform(SystemPlatform.Windows)] public class WinUSBRootHub : CriticalFinalizerObject, IDeviceHub { - private static readonly Guid[] _winUsbGuids = new Guid[] + private static readonly Guid[] WinUsbGuids = new Guid[] { // Standard WinUSB (Zadig) new Guid("{dee824ef-729b-4a0e-9c14-b7117d33a817}"), @@ -26,8 +24,8 @@ public class WinUSBRootHub : CriticalFinalizerObject, IDeviceHub }; private readonly CM_NOTIFY_CALLBACK _callback; - private readonly GCHandle _callbackPin; - private List _oldDevices; + private GCHandle _callbackPin; + private List? _oldDevices; private List _currentDevices; private readonly Dictionary _notificationHandles = new(); @@ -48,11 +46,11 @@ public unsafe WinUSBRootHub() _currentDevices = new List(); - foreach (var guid in _winUsbGuids) + foreach (var guid in WinUsbGuids) EnumerateAllDevicesWithGuid(_currentDevices, guid); } - public event EventHandler DevicesChanged; + public event EventHandler? DevicesChanged; public IEnumerable GetDevices() { @@ -64,7 +62,7 @@ private void Enumerate() _oldDevices = _currentDevices; _currentDevices = new List(); - foreach (var guid in _winUsbGuids) + foreach (var guid in WinUsbGuids) EnumerateAllDevicesWithGuid(_currentDevices, guid); DevicesChanged?.Invoke(this, new DevicesChangedEventArgs(_oldDevices, _currentDevices)); @@ -115,9 +113,9 @@ private static void TryAdd(List list, string devicePath) } } - private unsafe int NotificationCallback(IntPtr hNotify, IntPtr Context, CM_NOTIFY_ACTION Action, CM_NOTIFY_EVENT_DATA* EventData, int EventDataSize) + private unsafe int NotificationCallback(IntPtr hNotify, IntPtr context, CM_NOTIFY_ACTION action, CM_NOTIFY_EVENT_DATA* eventData, int eventDataSize) { - switch (Action) + switch (action) { case CM_NOTIFY_ACTION.DEVICEINTERFACEARRIVAL: case CM_NOTIFY_ACTION.DEVICEINTERFACEREMOVAL: @@ -131,7 +129,7 @@ private unsafe int NotificationCallback(IntPtr hNotify, IntPtr Context, CM_NOTIF private void HookDeviceNotification() { - foreach (var guid in _winUsbGuids) + foreach (var guid in WinUsbGuids) { var notificationFilter = CM_NOTIFY_FILTER.Create(guid); var ret = CM_Register_Notification(in notificationFilter, IntPtr.Zero, _callback, out var notificationHandle); diff --git a/OpenTabletDriver/Driver.cs b/OpenTabletDriver/Driver.cs index 87888d610..01efcebbd 100644 --- a/OpenTabletDriver/Driver.cs +++ b/OpenTabletDriver/Driver.cs @@ -3,40 +3,45 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using JetBrains.Annotations; +using OpenTabletDriver.Components; +using OpenTabletDriver.Devices; using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Components; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Tablet; - -#nullable enable +using OpenTabletDriver.Tablet; namespace OpenTabletDriver { + /// + /// A base implementation of . + /// + [PublicAPI] public class Driver : IDriver, IDisposable { - public Driver(ICompositeDeviceHub deviceHub, IReportParserProvider reportParserProvider, IDeviceConfigurationProvider configurationProvider) + private readonly ICompositeDeviceHub _compositeDeviceHub; + private readonly IReportParserProvider _reportParserProvider; + private readonly IDeviceConfigurationProvider _deviceConfigurationProvider; + + public Driver( + ICompositeDeviceHub deviceHub, + IReportParserProvider reportParserProvider, + IDeviceConfigurationProvider configurationProvider + ) { - CompositeDeviceHub = deviceHub; + _compositeDeviceHub = deviceHub; _reportParserProvider = reportParserProvider; _deviceConfigurationProvider = configurationProvider; } - private readonly IReportParserProvider _reportParserProvider; - private readonly IDeviceConfigurationProvider _deviceConfigurationProvider; + public event EventHandler>? InputDevicesChanged; - public event EventHandler>? TabletsChanged; - - public ICompositeDeviceHub CompositeDeviceHub { get; } - public InputDeviceTreeList InputDevices { get; } = new(); - public IEnumerable Tablets => InputDevices.Select(c => c.CreateReference()); + public InputDeviceCollection InputDevices { get; } = new InputDeviceCollection(); public IReportParser GetReportParser(DeviceIdentifier identifier) { return _reportParserProvider.GetReportParser(identifier.ReportParser); } - public virtual bool Detect() + public virtual void Detect() { bool success = false; @@ -45,7 +50,7 @@ public virtual bool Detect() InputDevices.Clear(); foreach (var config in _deviceConfigurationProvider.TabletConfigurations) { - if (Match(config) is InputDeviceTree tree) + if (Match(config) is InputDevice tree) { success = true; InputDevices.Add(tree); @@ -53,45 +58,43 @@ public virtual bool Detect() tree.Disconnected += (sender, e) => { InputDevices.Remove(tree); - TabletsChanged?.Invoke(this, Tablets); + InputDevicesChanged?.Invoke(this, InputDevices); }; } } - TabletsChanged?.Invoke(this, Tablets); + InputDevicesChanged?.Invoke(this, InputDevices); if (!success) { Log.Write("Detect", "No tablets were detected."); } - - return success; } - protected virtual InputDeviceTree? Match(TabletConfiguration config) + protected virtual InputDevice? Match(TabletConfiguration config) { Log.Debug("Detect", $"Searching for tablet '{config.Name}'"); try { - var devices = new List(); - if (MatchDevice(config, config.DigitizerIdentifiers) is InputDevice digitizer) + var devices = new List(); + if (MatchDevice(config, config.DigitizerIdentifiers) is InputDeviceEndpoint digitizer) { Log.Write("Detect", $"Found tablet '{config.Name}'"); devices.Add(digitizer); - if (config.AuxilaryDeviceIdentifiers.Any()) + if (config.AuxiliaryDeviceIdentifiers.Any()) { - if (MatchDevice(config, config.AuxilaryDeviceIdentifiers) is InputDevice aux) + if (MatchDevice(config, config.AuxiliaryDeviceIdentifiers) is InputDeviceEndpoint aux) devices.Add(aux); else Log.Write("Detect", "Failed to find auxiliary device, express keys may be unavailable.", LogLevel.Warning); } - return new InputDeviceTree(config, devices); + return new InputDevice(config, devices); } } catch (IOException iex) when (iex.Message.Contains("Unable to open HID class device") - && SystemInterop.CurrentPlatform == PluginPlatform.Linux) + && SystemInterop.CurrentPlatform == SystemPlatform.Linux) { Log.Write( "Driver", @@ -101,7 +104,7 @@ public virtual bool Detect() ); } catch (ArgumentOutOfRangeException aex) when (aex.Message.Contains("Value range is [0, 15]") - && SystemInterop.CurrentPlatform == PluginPlatform.Linux) + && SystemInterop.CurrentPlatform == SystemPlatform.Linux) { Log.Write( "Driver", @@ -117,7 +120,7 @@ public virtual bool Detect() return null; } - private InputDevice? MatchDevice(TabletConfiguration config, IList identifiers) + private InputDeviceEndpoint? MatchDevice(TabletConfiguration config, IList identifiers) { foreach (var identifier in identifiers) { @@ -130,7 +133,7 @@ public virtual bool Detect() { try { - return new InputDevice(this, dev, config, identifier); + return new InputDeviceEndpoint(this, dev, config, identifier); } catch (Exception ex) { @@ -144,7 +147,7 @@ public virtual bool Detect() private IEnumerable GetMatchingDevices(TabletConfiguration configuration, DeviceIdentifier identifier) { - return from device in CompositeDeviceHub.GetDevices() + return from device in _compositeDeviceHub.GetDevices() where identifier.VendorID == device.VendorID where identifier.ProductID == device.ProductID where device.CanOpen @@ -155,7 +158,7 @@ where DeviceMatchesAttribute(device, configuration.Attributes) select device; } - private bool DeviceMatchesStrings(IDeviceEndpoint device, IDictionary deviceStrings) + private bool DeviceMatchesStrings(IDeviceEndpoint device, IDictionary? deviceStrings) { if (deviceStrings == null || deviceStrings.Count == 0) return true; @@ -165,7 +168,7 @@ private bool DeviceMatchesStrings(IDeviceEndpoint device, IDictionary configure) - { - if (_hasBuilt) - throw new DriverAlreadyBuiltException(); - - configure(_driverServices); - return this; - } - - /// - /// Builds an instance of . - /// - /// The final service collection associated to the driver. - /// The built Driver with its lifetime managed by . - public T Build(out IServiceCollection serviceCollection) where T : class, IDriver - { - if (_hasBuilt) - throw new DriverAlreadyBuiltException(); - - _driverServices.AddSingleton(); -#if DEBUG - var serviceProvider = _driverServices.BuildServiceProvider(new ServiceProviderOptions - { - ValidateScopes = true, - ValidateOnBuild = true - }); -#else - var serviceProvider = _driverServices.BuildServiceProvider(); -#endif - _hasBuilt = true; - serviceCollection = _driverServices; - - if (serviceProvider.GetService() is not T driver) - throw new InvalidOperationException(); - - return driver; - } - } -} diff --git a/OpenTabletDriver/DriverServiceCollection.cs b/OpenTabletDriver/DriverServiceCollection.cs deleted file mode 100644 index f9a8a8908..000000000 --- a/OpenTabletDriver/DriverServiceCollection.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using OpenTabletDriver.ComponentProviders; -using OpenTabletDriver.Configurations; -using OpenTabletDriver.Devices; -using OpenTabletDriver.Plugin.Components; - -#nullable enable - -namespace OpenTabletDriver -{ - public class DriverServiceCollection : ServiceCollection - { - private static IEnumerable RequiredServices => new ServiceDescriptor[] - { - ServiceDescriptor.Singleton(), - ServiceDescriptor.Singleton(serviceProvider => new DeviceHubsProvider(serviceProvider)), - ServiceDescriptor.Singleton(serviceProvider => RootHub.WithProvider(serviceProvider)), - ServiceDescriptor.Singleton() - }; - - public DriverServiceCollection() - { - foreach (var serviceDescriptor in RequiredServices) - this.Add(serviceDescriptor); - } - } -} diff --git a/OpenTabletDriver/DriverAlreadyBuiltException.cs b/OpenTabletDriver/Exceptions/DriverAlreadyBuiltException.cs similarity index 84% rename from OpenTabletDriver/DriverAlreadyBuiltException.cs rename to OpenTabletDriver/Exceptions/DriverAlreadyBuiltException.cs index f88f0308e..7d65d26c3 100644 --- a/OpenTabletDriver/DriverAlreadyBuiltException.cs +++ b/OpenTabletDriver/Exceptions/DriverAlreadyBuiltException.cs @@ -1,9 +1,9 @@ using System; +using JetBrains.Annotations; -#nullable enable - -namespace OpenTabletDriver +namespace OpenTabletDriver.Exceptions { + [PublicAPI] public class DriverAlreadyBuiltException : Exception { public DriverAlreadyBuiltException() : this("A driver instance has already been built") diff --git a/OpenTabletDriver/Extensions.cs b/OpenTabletDriver/Extensions.cs deleted file mode 100644 index 1fc3cec4e..000000000 --- a/OpenTabletDriver/Extensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using OpenTabletDriver.Plugin; - -namespace OpenTabletDriver -{ - internal static class Extensions - { - public static bool TryGet(this TSource source, Func predicate, out TValue value) - { - try - { - value = predicate(source); - return true; - } - catch (Exception ex) - { - Log.Exception(ex); - } - value = default; - return false; - } - - public static TValue SafeGet(this TSource source, Func predicate, TValue fallback) => TryGet(source, predicate, out var value) ? value : fallback; - } -} diff --git a/OpenTabletDriver/HPETDeltaStopwatch.cs b/OpenTabletDriver/HPETDeltaStopwatch.cs new file mode 100644 index 000000000..ca5438bc3 --- /dev/null +++ b/OpenTabletDriver/HPETDeltaStopwatch.cs @@ -0,0 +1,98 @@ +using System; +using System.Diagnostics; +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A high precision event timer stopwatch. + /// + [PublicAPI] + public sealed class HPETDeltaStopwatch + { + private static readonly Stopwatch InternalWatch = Stopwatch.StartNew(); + private TimeSpan _start; + private TimeSpan _end; + private bool _isRunning; + + public HPETDeltaStopwatch(bool startRunning = true) + { + _isRunning = startRunning; + _start = _isRunning ? InternalWatch.Elapsed : default; + } + + /// + /// The amount of time since the last reset. + /// + public static TimeSpan RuntimeElapsed => InternalWatch.Elapsed; + + /// + /// The amount of time since the last start. + /// + public TimeSpan Elapsed => _isRunning ? RuntimeElapsed - _start : _end - _start; + + /// + /// Starts the stopwatch. + /// + public void Start() + { + if (!_isRunning) + { + _isRunning = true; + _start = InternalWatch.Elapsed; + } + } + + /// + /// Stops the stopwatch. + /// + /// + /// Elapsed time since the last start. + /// + public TimeSpan Stop() + { + if (_isRunning) + { + _isRunning = false; + _end = InternalWatch.Elapsed; + } + return _end - _start; + } + + /// + /// Restarts the stopwatch. + /// + /// + /// Elapsed time since the last start. + /// + public TimeSpan Restart() + { + if (_isRunning) + { + var current = InternalWatch.Elapsed; + var delta = current - _start; + _start = current; + return delta; + } + else + { + var delta = _end - _start; + Start(); + return delta; + } + } + + /// + /// Stops and resets the stopwatch. + /// + /// + /// Elapsed time since the last start. + /// + public TimeSpan Reset() + { + var delta = Stop(); + _start = _end = default; + return delta; + } + } +} diff --git a/OpenTabletDriver/IBinding.cs b/OpenTabletDriver/IBinding.cs new file mode 100644 index 000000000..bffd1732c --- /dev/null +++ b/OpenTabletDriver/IBinding.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A base binding interface. + /// + [PublicAPI] + public interface IBinding + { + } +} diff --git a/OpenTabletDriver.Plugin/IDriver.cs b/OpenTabletDriver/IDriver.cs similarity index 71% rename from OpenTabletDriver.Plugin/IDriver.cs rename to OpenTabletDriver/IDriver.cs index 1a4e3dd79..868d7fd42 100644 --- a/OpenTabletDriver.Plugin/IDriver.cs +++ b/OpenTabletDriver/IDriver.cs @@ -1,26 +1,31 @@ using System; using System.Collections.Generic; -using OpenTabletDriver.Plugin.Tablet; +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; -namespace OpenTabletDriver.Plugin +namespace OpenTabletDriver { + /// + /// A base driver interface. + /// + [PublicAPI] public interface IDriver { /// /// Invoked whenever a tablet is either detected or is disconnected. /// - event EventHandler> TabletsChanged; + event EventHandler> InputDevicesChanged; /// /// The currently active and detected tablets. /// - IEnumerable Tablets { get; } + InputDeviceCollection InputDevices { get; } /// /// Attempts to detect a tablet. /// /// True if any configuration successfully matched. - bool Detect(); + void Detect(); /// /// Retrieve and construct the the report parser for an identifier. diff --git a/OpenTabletDriver/ISettingsProvider.cs b/OpenTabletDriver/ISettingsProvider.cs new file mode 100644 index 000000000..b3cc872fe --- /dev/null +++ b/OpenTabletDriver/ISettingsProvider.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A settings provider interface that injects settings marked by . + /// + [PublicAPI] + public interface ISettingsProvider + { + /// + /// Injects all settings properties marked by . + /// + /// The object to inject settings into. + void Inject(object obj); + } +} diff --git a/OpenTabletDriver.Plugin/IStateBinding.cs b/OpenTabletDriver/IStateBinding.cs similarity index 54% rename from OpenTabletDriver.Plugin/IStateBinding.cs rename to OpenTabletDriver/IStateBinding.cs index 5aa979751..4dc044d21 100644 --- a/OpenTabletDriver.Plugin/IStateBinding.cs +++ b/OpenTabletDriver/IStateBinding.cs @@ -1,21 +1,22 @@ -using OpenTabletDriver.Plugin.Tablet; +using OpenTabletDriver.Tablet; -namespace OpenTabletDriver.Plugin +namespace OpenTabletDriver { + /// + /// A binding with a boolean state. + /// public interface IStateBinding : IBinding { /// /// The method to perform when the binding is being activated. /// - /// The tablet that this report is from. /// The report that triggered the press. - void Press(TabletReference tablet, IDeviceReport report); + void Press(IDeviceReport report); /// /// Invoked when the binding is being released. /// - /// The tablet that this report is from. /// The report that triggered the release. - void Release(TabletReference tablet, IDeviceReport report); + void Release(IDeviceReport report); } } diff --git a/OpenTabletDriver/ITimer.cs b/OpenTabletDriver/ITimer.cs new file mode 100644 index 000000000..06ba40678 --- /dev/null +++ b/OpenTabletDriver/ITimer.cs @@ -0,0 +1,37 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A high precision timer. + /// + [PublicAPI] + public interface ITimer : IDisposable + { + /// + /// Starts the timer. + /// + void Start(); + + /// + /// Stops the timer. + /// + void Stop(); + + /// + /// Current state of the timer. + /// + bool Enabled { get; } + + /// + /// The timer's interval for invoking + /// + float Interval { set; get; } + + /// + /// Invoked when the timer elapses the interval. + /// + event Action? Elapsed; + } +} diff --git a/OpenTabletDriver/ITool.cs b/OpenTabletDriver/ITool.cs new file mode 100644 index 000000000..5498e7796 --- /dev/null +++ b/OpenTabletDriver/ITool.cs @@ -0,0 +1,20 @@ +using System; +using JetBrains.Annotations; + +namespace OpenTabletDriver +{ + /// + /// A plugin that will be started up and kept running until OpenTabletDriver is closed. + /// + [PublicAPI] + public interface ITool : IDisposable + { + /// + /// Initializes the tool. + /// + /// + /// Whether the tool successfully initialized. + /// + bool Initialize(); + } +} diff --git a/OpenTabletDriver/InputDevice.cs b/OpenTabletDriver/InputDevice.cs index 7f8ce87f6..f0ff0f380 100644 --- a/OpenTabletDriver/InputDevice.cs +++ b/OpenTabletDriver/InputDevice.cs @@ -1,126 +1,63 @@ using System; using System.Collections.Generic; -using System.Threading; -using OpenTabletDriver.Devices; -using OpenTabletDriver.Plugin; -using OpenTabletDriver.Plugin.Devices; -using OpenTabletDriver.Plugin.Tablet; +using JetBrains.Annotations; +using Newtonsoft.Json; +using OpenTabletDriver.Output; +using OpenTabletDriver.Tablet; namespace OpenTabletDriver { - public class InputDevice : DeviceReader + /// + /// A configured input device. + /// + [PublicAPI] + public class InputDevice { - private readonly uint _featureInitDelayMs; - private const string DELAY_ATTRIBUTE_KEY_NAME = "FeatureInitDelayMs"; - - public InputDevice(IDriver driver, IDeviceEndpoint device, TabletConfiguration configuration, DeviceIdentifier identifier) - : base(device, driver.GetReportParser(identifier)) + public InputDevice(TabletConfiguration configuration, IList endpoints) { - if (driver == null || device == null || configuration == null || identifier == null) - { - string argumentName = driver == null ? nameof(driver) : - device == null ? nameof(device) : - configuration == null ? nameof(configuration) : - nameof(identifier); - throw new ArgumentNullException(argumentName); - } - - Endpoint = device; Configuration = configuration; - Identifier = identifier; - - if (Configuration.Attributes.TryGetValue(DELAY_ATTRIBUTE_KEY_NAME, out var delayStr)) - if (!uint.TryParse(delayStr, out _featureInitDelayMs)) - Log.Write("Device", $"Could not parse '{delayStr}' from attribute {DELAY_ATTRIBUTE_KEY_NAME}", LogLevel.Warning); + Endpoints = endpoints; - Start(); + foreach (var dev in Endpoints) + { + // Hook endpoint states + dev.ConnectionStateChanged += (sender, reading) => + { + if (_connected && !reading) + { + _connected = false; + Disconnected?.Invoke(this, EventArgs.Empty); + } + }; + dev.Report += HandleReport; + } } - /// - /// The driver in which this was sourced from. - /// - public IDriver Driver { private set; get; } - - /// - /// The tablet configuration referring to this device. - /// - public TabletConfiguration Configuration { private set; get; } - - /// - /// The device identifier used to detect this device. - /// - /// - public DeviceIdentifier Identifier { private set; get; } - - protected override bool Initialize() + [JsonConstructor] + private InputDevice() { - if (!base.Initialize()) - return false; - - Log.Debug("Device", $"Initializing device '{Endpoint.FriendlyName}' {Endpoint.DevicePath}"); - Log.Debug("Device", $"Using report parser type '{Identifier.ReportParser}'"); - - if (_featureInitDelayMs != 0) - Log.Debug("Device", $"{DELAY_ATTRIBUTE_KEY_NAME} is set in tablet configuration, will sleep for {_featureInitDelayMs}ms between FeatureInitReports"); - - foreach (byte index in Identifier.InitializationStrings ?? new List()) - { - Endpoint.GetDeviceString(index); - Log.Debug("Device", $"Initialized string index {index}"); - } + Endpoints = Array.Empty(); + Configuration = null!; + } - foreach (var report in Identifier.FeatureInitReport ?? new List()) - { - if (report == null || report.Length == 0) - continue; + private bool _connected = true; - try - { - if (_featureInitDelayMs != 0) - Thread.Sleep((int)_featureInitDelayMs); - ReportStream.SetFeature(report); - Log.Debug("Device", "Set device feature: " + BitConverter.ToString(report)); - } - catch - { - Log.Write("Device", "Failed to set device feature: " + BitConverter.ToString(report), LogLevel.Warning); - } - } + public event EventHandler? Disconnected; - foreach (var report in Identifier.OutputInitReport ?? new List()) - { - if (report == null || report.Length == 0) - continue; + public TabletConfiguration Configuration { protected set; get; } - try - { - ReportStream.Write(report); - Log.Debug("Device", "Set device output: " + BitConverter.ToString(report)); - } - catch - { - Log.Write("Device", "Failed to set device output: " + BitConverter.ToString(report), LogLevel.Warning); - } - } + [JsonIgnore] + public IList Endpoints { get; } - return true; - } + /// + /// The active output mode at the end of the data pipeline for all data to be processed. + /// + [JsonIgnore] + public IOutputMode? OutputMode { set; get; } - private bool TryGetDeviceProperty(Func predicate, out T value) + private void HandleReport(object? sender, IDeviceReport? report) { - try - { - value = predicate(Endpoint); - return true; - } - catch (Exception ex) - { - Log.Exception(ex); - } - value = default(T); - return false; + OutputMode?.Read(report!); } - - private T SafeGetDeviceProperty(Func predicate, T fallback) => TryGetDeviceProperty(predicate, out var val) ? val : fallback; } } diff --git a/OpenTabletDriver/InputDeviceTreeList.cs b/OpenTabletDriver/InputDeviceCollection.cs similarity index 54% rename from OpenTabletDriver/InputDeviceTreeList.cs rename to OpenTabletDriver/InputDeviceCollection.cs index ece82dfda..6db505825 100644 --- a/OpenTabletDriver/InputDeviceTreeList.cs +++ b/OpenTabletDriver/InputDeviceCollection.cs @@ -1,15 +1,20 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using JetBrains.Annotations; namespace OpenTabletDriver { - public class InputDeviceTreeList : Collection + /// + /// A collection of s. + /// + [PublicAPI] + public class InputDeviceCollection : Collection { protected override void ClearItems() { - var outdatedDevices = new List(); + var outdatedDevices = new List(); foreach (var tree in base.Items) - foreach (var dev in tree.InputDevices) + foreach (var dev in tree.Endpoints) outdatedDevices.Add(dev); foreach (var dev in outdatedDevices) diff --git a/OpenTabletDriver/InputDeviceEndpoint.cs b/OpenTabletDriver/InputDeviceEndpoint.cs new file mode 100644 index 000000000..48e3edddd --- /dev/null +++ b/OpenTabletDriver/InputDeviceEndpoint.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using OpenTabletDriver.Devices; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver +{ + /// + /// An input device endpoint. + /// + [PublicAPI] + public class InputDeviceEndpoint : DeviceReader + { + public InputDeviceEndpoint(IDriver driver, IDeviceEndpoint device, TabletConfiguration configuration, DeviceIdentifier identifier) + : base(device, driver.GetReportParser(identifier)) + { + if (driver == null || device == null || configuration == null || identifier == null) + { + string argumentName = driver == null ? nameof(driver) : + device == null ? nameof(device) : + configuration == null ? nameof(configuration) : + nameof(identifier); + throw new ArgumentNullException(argumentName); + } + + Endpoint = device; + Configuration = configuration; + Identifier = identifier; + + Start(); + } + + /// + /// The tablet configuration referring to this device. + /// + public TabletConfiguration Configuration { get; } + + /// + /// The device identifier used to detect this device. + /// + /// + public DeviceIdentifier Identifier { get; } + + protected override bool Initialize() + { + if (!base.Initialize()) + return false; + + Log.Debug("Device", $"Initializing device '{Endpoint.FriendlyName}' {Endpoint.DevicePath}"); + Log.Debug("Device", $"Using report parser type '{Identifier.ReportParser}'"); + + foreach (byte index in Identifier.InitializationStrings ?? new List()) + { + Endpoint.GetDeviceString(index); + Log.Debug("Device", $"Initialized string index {index}"); + } + + foreach (var report in Identifier.FeatureInitReport ?? new List()) + { + if (report.Length == 0) + continue; + + try + { + ReportStream!.SetFeature(report); + Log.Debug("Device", "Set device feature: " + BitConverter.ToString(report)); + } + catch + { + Log.Write("Device", "Failed to set device feature: " + BitConverter.ToString(report), LogLevel.Warning); + } + } + + foreach (var report in Identifier.OutputInitReport ?? new List()) + { + if (report.Length == 0) + continue; + + try + { + ReportStream!.Write(report); + Log.Debug("Device", "Set device output: " + BitConverter.ToString(report)); + } + catch + { + Log.Write("Device", "Failed to set device output: " + BitConverter.ToString(report), LogLevel.Warning); + } + } + + return true; + } + } +} diff --git a/OpenTabletDriver/InputDeviceTree.cs b/OpenTabletDriver/InputDeviceTree.cs deleted file mode 100644 index 817f59e2b..000000000 --- a/OpenTabletDriver/InputDeviceTree.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using OpenTabletDriver.Plugin.Output; -using OpenTabletDriver.Plugin.Tablet; - -#nullable enable - -namespace OpenTabletDriver -{ - public class InputDeviceTree - { - public InputDeviceTree(TabletConfiguration configuration, IList inputDevices) - { - Properties = configuration; - InputDevices = inputDevices; - - foreach (var dev in InputDevices) - { - // Hook endpoint states - dev.ConnectionStateChanged += (sender, reading) => - { - if (this.connected && !reading) - { - this.connected = false; - Disconnected?.Invoke(this, new EventArgs()); - } - }; - } - } - - private bool connected = true; - private IList inputDevices; - - public event EventHandler? Disconnected; - - public TabletConfiguration Properties { protected set; get; } - public IList InputDevices - { - [MemberNotNull(nameof(inputDevices))] - protected set - { - this.inputDevices = value; - foreach (var dev in InputDevices) - dev.Report += HandleReport; - } - get => this.inputDevices; - } - - /// - /// The active output mode at the end of the data pipeline for all data to be processed. - /// - public IOutputMode? OutputMode { set; get; } - - public TabletReference CreateReference() => new TabletReference(Properties, InputDevices.Select(c => c.Identifier)); - - private void HandleReport(object? sender, IDeviceReport report) - { - OutputMode?.Read(report); - } - - public static implicit operator TabletReference(InputDeviceTree deviceGroup) => deviceGroup.CreateReference(); - } -} diff --git a/OpenTabletDriver/Instance.cs b/OpenTabletDriver/Instance.cs index 18412062e..133332b65 100644 --- a/OpenTabletDriver/Instance.cs +++ b/OpenTabletDriver/Instance.cs @@ -2,20 +2,39 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using JetBrains.Annotations; namespace OpenTabletDriver { - public class Instance : IDisposable + /// + /// A utility for existing instance detection via . + /// + [PublicAPI] + public sealed class Instance : IDisposable { + private readonly Mutex _mutex; + public Instance(string name) { - this.mutex = new Mutex(true, $"{MUTEX_PREFIX}{name}", out bool createdNew); - this.Name = name; + _mutex = new Mutex(true, $"{MUTEX_PREFIX}{name}", out bool createdNew); + Name = name; AlreadyExists = !createdNew; if (createdNew) - ownedInstances.Add(this); + OwnedInstances.Add(this); } + private const string MUTEX_PREFIX = @"Global\"; + + private static readonly List OwnedInstances = new List(); + + public string Name { get; } + public bool AlreadyExists { private set; get; } + + /// + /// Checks if an instance already exists for this name. + /// + /// The instance name. + /// True if an instance already exists, otherwise false. public static bool Exists(string name) { var result = Mutex.TryOpenExisting($"{MUTEX_PREFIX}{name}", out var mutex); @@ -26,23 +45,20 @@ public static bool Exists(string name) } } + /// + /// Checks if an instance is owned by the current application. + /// + /// The instance name. + /// True if the current application owns the instance, otherwise false. public static bool IsOwnerOf(string name) { - return ownedInstances.Any(i => i.Name == name); + return OwnedInstances.Any(i => i.Name == name); } - private const string MUTEX_PREFIX = @"Global\"; - private readonly static List ownedInstances = new List(); - - private Mutex mutex; - - public string Name { get; } - public bool AlreadyExists { protected set; get; } - public void Dispose() { - mutex.Close(); - mutex.Dispose(); + _mutex.Close(); + _mutex.Dispose(); } } } diff --git a/OpenTabletDriver/Interop/SleepDetectionThread.cs b/OpenTabletDriver/Interop/SleepDetectionThread.cs deleted file mode 100644 index 66f0d29e4..000000000 --- a/OpenTabletDriver/Interop/SleepDetectionThread.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; - -namespace OpenTabletDriver.Interop -{ - public class SleepDetectionThread : IDisposable - { - private readonly Stopwatch stopwatch = new(); - private readonly Action action; - private Task task; - private CancellationTokenSource cancellationTokenSource; - private double prev; - - public SleepDetectionThread(Action action) - { - this.action = action; - } - - public void Start() - { - if (task != null) - { - Stop(); - } - - cancellationTokenSource = new(); - task = DetectionLoop(cancellationTokenSource.Token); - } - - public void Stop() - { - if (task != null) - { - cancellationTokenSource.Cancel(); - task.Wait(); - - cancellationTokenSource.Dispose(); - cancellationTokenSource = null; - task = null; - } - } - - private async Task DetectionLoop(CancellationToken cancellationToken) - { - stopwatch.Start(); - while (!cancellationToken.IsCancellationRequested) - { - var elapsed = stopwatch.Elapsed.TotalSeconds; - if (elapsed - prev > 2) - action?.Invoke(); - - prev = elapsed; - - await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); - } - stopwatch.Stop(); - } - - public void Dispose() - { - Stop(); - GC.SuppressFinalize(this); - } - } -} diff --git a/OpenTabletDriver/Interop/SystemInterop.cs b/OpenTabletDriver/Interop/SystemInterop.cs index df12adf5a..9bd38228c 100644 --- a/OpenTabletDriver/Interop/SystemInterop.cs +++ b/OpenTabletDriver/Interop/SystemInterop.cs @@ -1,28 +1,31 @@ using System.Runtime.InteropServices; -using OpenTabletDriver.Plugin; +using JetBrains.Annotations; namespace OpenTabletDriver.Interop { - public class SystemInterop + /// + /// Provides system platform detection. + /// + [PublicAPI] + public static class SystemInterop { - protected SystemInterop() - { - } - - public static PluginPlatform CurrentPlatform + /// + /// The currently running system platform. + /// + public static SystemPlatform CurrentPlatform { get { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return PluginPlatform.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return PluginPlatform.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return PluginPlatform.MacOS; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - return PluginPlatform.FreeBSD; - else - return 0; + return SystemPlatform.Windows; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return SystemPlatform.Linux; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return SystemPlatform.MacOS; + if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + return SystemPlatform.FreeBSD; + + return default; } } } diff --git a/OpenTabletDriver.Plugin/Log.cs b/OpenTabletDriver/Log.cs similarity index 83% rename from OpenTabletDriver.Plugin/Log.cs rename to OpenTabletDriver/Log.cs index 4d1914507..afecad23e 100644 --- a/OpenTabletDriver.Plugin/Log.cs +++ b/OpenTabletDriver/Log.cs @@ -1,14 +1,19 @@ using System; -using OpenTabletDriver.Plugin.Logging; +using JetBrains.Annotations; +using OpenTabletDriver.Logging; -namespace OpenTabletDriver.Plugin +namespace OpenTabletDriver { + /// + /// The core logging service. + /// + [PublicAPI] public static class Log { /// - /// Event hook to recieve log messages. + /// Event hook to receive log messages. /// - public static event EventHandler Output; + public static event EventHandler? Output; /// /// Invoke sending a log message. @@ -26,7 +31,7 @@ public static void Write(LogMessage message) /// A formatted string. public static string GetStringFormat(LogMessage message) { - string text = message.ToString(); + var text = message.ToString(); // Append stack trace if an exception was caught. if (!string.IsNullOrWhiteSpace(message.StackTrace)) @@ -41,15 +46,16 @@ public static string GetStringFormat(LogMessage message) /// The group in which the belongs to. /// Text for the . /// The severity level of the . + /// The stack trace included with this . /// Whether or not the log message should create a notification in the user's desktop environment. - public static void Write(string group, string text, LogLevel level = LogLevel.Info, bool createStackTrace = false, bool notify = false) + public static void Write(string group, string text, LogLevel level = LogLevel.Info, string? stackTrace = null, bool notify = false) { var message = new LogMessage { Group = group, Message = text, Level = level, - StackTrace = createStackTrace ? Environment.StackTrace : null, + StackTrace = stackTrace, Notification = notify }; Write(message); @@ -64,7 +70,7 @@ public static void Write(string group, string text, LogLevel level = LogLevel.In /// Level of severity for the message, see . Defaults to Info. public static void WriteNotify(string group, string text, LogLevel level = LogLevel.Info) { - Write(group, text, level, false, true); + Write(group, text, level, notify: true); } /// @@ -81,7 +87,7 @@ public static void Debug(string group, string text) /// Writes to the log event with an exception, encoding its stack trace. /// /// The object to create the from. - public static void Exception(Exception ex) + public static void Exception(Exception? ex) { if (ex == null) return; diff --git a/OpenTabletDriver.Plugin/LogLevel.cs b/OpenTabletDriver/LogLevel.cs similarity index 67% rename from OpenTabletDriver.Plugin/LogLevel.cs rename to OpenTabletDriver/LogLevel.cs index 66bf3df72..cac5becf6 100644 --- a/OpenTabletDriver.Plugin/LogLevel.cs +++ b/OpenTabletDriver/LogLevel.cs @@ -1,8 +1,12 @@ -namespace OpenTabletDriver.Plugin +using JetBrains.Annotations; +using OpenTabletDriver.Logging; + +namespace OpenTabletDriver { /// /// Various severity levels for s. /// + [PublicAPI] public enum LogLevel { Debug, diff --git a/OpenTabletDriver.Plugin/Logging/LogMessage.cs b/OpenTabletDriver/Logging/LogMessage.cs similarity index 81% rename from OpenTabletDriver.Plugin/Logging/LogMessage.cs rename to OpenTabletDriver/Logging/LogMessage.cs index 33d8f8cf1..f02720d29 100644 --- a/OpenTabletDriver.Plugin/Logging/LogMessage.cs +++ b/OpenTabletDriver/Logging/LogMessage.cs @@ -1,8 +1,14 @@ using System; -using System.Text; +using JetBrains.Annotations; +using Newtonsoft.Json; -namespace OpenTabletDriver.Plugin.Logging +namespace OpenTabletDriver.Logging { + /// + /// A driver log message. + /// + [JsonObject] + [PublicAPI] public class LogMessage { public LogMessage() @@ -25,17 +31,17 @@ public LogMessage(Exception exception) /// /// The group in which the log message belongs to. /// - public string Group { set; get; } + public string? Group { set; get; } /// /// The content of the log message. /// - public string Message { set; get; } + public string? Message { set; get; } /// /// The stack trace at the time of the log message. /// - public string StackTrace { set; get; } + public string? StackTrace { set; get; } /// /// The severity level of the log message. diff --git a/OpenTabletDriver/NotifyPropertyChanged.cs b/OpenTabletDriver/NotifyPropertyChanged.cs new file mode 100644 index 000000000..03bae39bb --- /dev/null +++ b/OpenTabletDriver/NotifyPropertyChanged.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace OpenTabletDriver +{ + public class NotifyPropertyChanged : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + + protected void RaiseAndSetIfChanged(ref T obj, T newValue, [CallerMemberName] string propertyName = "") + { + obj = newValue; + RaiseChanged(propertyName); + } + + private void RaiseChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/OpenTabletDriver/OpenTabletDriver.csproj b/OpenTabletDriver/OpenTabletDriver.csproj index 03f90ee37..f864efeb8 100644 --- a/OpenTabletDriver/OpenTabletDriver.csproj +++ b/OpenTabletDriver/OpenTabletDriver.csproj @@ -4,6 +4,7 @@ net5.0;$(FrameworkBase) true VSTHRD100; VSTHRD101; VSTHRD110; VSTHRD200 + enable @@ -19,9 +20,7 @@ - - diff --git a/OpenTabletDriver.Plugin/Output/AbsoluteOutputMode.cs b/OpenTabletDriver/Output/AbsoluteOutputMode.cs similarity index 51% rename from OpenTabletDriver.Plugin/Output/AbsoluteOutputMode.cs rename to OpenTabletDriver/Output/AbsoluteOutputMode.cs index 75247c4b9..ac4ccdfc8 100644 --- a/OpenTabletDriver.Plugin/Output/AbsoluteOutputMode.cs +++ b/OpenTabletDriver/Output/AbsoluteOutputMode.cs @@ -1,49 +1,70 @@ -using System.Numerics; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Tablet; - -namespace OpenTabletDriver.Plugin.Output +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using JetBrains.Annotations; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Attributes.UI; +using OpenTabletDriver.Platform.Display; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Output { /// - /// An absolutely positioned output mode. + /// An absolute positioned output mode. /// [PluginIgnore] + [PublicAPI] public abstract class AbsoluteOutputMode : OutputMode { - private Vector2 min, max; - private Area outputArea, inputArea; + protected AbsoluteOutputMode(InputDevice tablet, IAbsolutePointer absolutePointer) + : base(tablet) + { + Pointer = absolutePointer; + } + + private Vector2 _min, _max; + private AngledArea? _inputArea; + private Area? _outputArea; /// /// The area in which the tablet's input is transformed to. /// - public Area Input + [Setting("Input Area")] + [MemberSourcedDefaults(nameof(GetDefaultInputArea))] + [LinkedSetting(nameof(Output), nameof(LockAspectRatio))] + public AngledArea? Input { set { - this.inputArea = value; - this.TransformationMatrix = CreateTransformationMatrix(); + _inputArea = value; + TransformationMatrix = CreateTransformationMatrix(); } - get => this.inputArea; + get => _inputArea; } /// /// The area in which the final processed output is transformed to. /// - public Area Output + [Setting("Output Area")] + [MemberSourcedDefaults(nameof(GetDefaultOutputArea))] + [LinkedSetting(nameof(Input), nameof(LockAspectRatio))] + public Area? Output { set { - this.outputArea = value; - this.TransformationMatrix = CreateTransformationMatrix(); + _outputArea = value; + TransformationMatrix = CreateTransformationMatrix(); } - get => this.outputArea; + get => _outputArea; } /// - /// The class in which the final absolute positioned output is handled. + /// Whether to lock aspect ratio when applying area settings. /// - public abstract IAbsolutePointer Pointer { set; get; } + [Setting("Lock Aspect Ratio", "Locks area aspect ratios from changing."), DefaultValue(false)] + [LinkedSettingSource] + public bool LockAspectRatio { set; get; } /// /// Whether to clip all tablet inputs to the assigned areas. @@ -52,6 +73,7 @@ public Area Output /// If false, input outside of the area can escape the assigned areas, but still will be transformed. /// If true, input outside of the area will be clipped to the edges of the assigned areas. /// + [Setting("Area Clipping", "Locks input within the area."), DefaultValue(true)] public bool AreaClipping { set; get; } /// @@ -60,34 +82,45 @@ public Area Output /// /// If true, is automatically implied true. /// + [Setting("Area Limiting", "Locks input within the area and discards input outside of it."), DefaultValue(true)] public bool AreaLimiting { set; get; } + /// + /// Whether to lock the area position and size inside of the maximum bounds. + /// Typically used in the user interface. + /// + [Setting("Keep inside maximum bounds", "Locks input within the maximum area bounds."), DefaultValue(true)] + public bool LockToBounds { set; get; } + + /// + /// The class in which the final absolute positioned output is handled. + /// + public IAbsolutePointer Pointer { get; } + protected override Matrix3x2 CreateTransformationMatrix() { - if (Input != null && Output != null && Tablet != null) + if (Input != null && Output != null) { - var transform = CalculateTransformation(Input, Output, Tablet.Properties.Specifications.Digitizer); + var transform = CalculateTransformation(Input, Output, Tablet.Configuration.Specifications.Digitizer!); var halfDisplayWidth = Output?.Width / 2 ?? 0; var halfDisplayHeight = Output?.Height / 2 ?? 0; - var minX = Output?.Position.X - halfDisplayWidth ?? 0; - var maxX = Output?.Position.X + Output?.Width - halfDisplayWidth ?? 0; - var minY = Output?.Position.Y - halfDisplayHeight ?? 0; - var maxY = Output?.Position.Y + Output?.Height - halfDisplayHeight ?? 0; + var minX = Output?.XPosition - halfDisplayWidth ?? 0; + var maxX = Output?.XPosition + Output?.Width - halfDisplayWidth ?? 0; + var minY = Output?.YPosition - halfDisplayHeight ?? 0; + var maxY = Output?.YPosition + Output?.Height - halfDisplayHeight ?? 0; - this.min = new Vector2(minX, minY); - this.max = new Vector2(maxX, maxY); + _min = new Vector2(minX, minY); + _max = new Vector2(maxX, maxY); return transform; } - else - { - return Matrix3x2.Identity; - } + + return Matrix3x2.Identity; } - protected static Matrix3x2 CalculateTransformation(Area input, Area output, DigitizerSpecifications digitizer) + private static Matrix3x2 CalculateTransformation(AngledArea input, Area output, DigitizerSpecifications digitizer) { // Convert raw tablet data to millimeters var res = Matrix3x2.CreateScale( @@ -96,7 +129,7 @@ protected static Matrix3x2 CalculateTransformation(Area input, Area output, Digi // Translate to the center of input area res *= Matrix3x2.CreateTranslation( - -input.Position.X, -input.Position.Y); + -input.XPosition, -input.YPosition); // Apply rotation res *= Matrix3x2.CreateRotation( @@ -108,7 +141,7 @@ protected static Matrix3x2 CalculateTransformation(Area input, Area output, Digi // Translate output to virtual screen coordinates res *= Matrix3x2.CreateTranslation( - output.Position.X, output.Position.Y); + output.XPosition, output.YPosition); return res; } @@ -117,13 +150,13 @@ protected static Matrix3x2 CalculateTransformation(Area input, Area output, Digi /// Transposes, transforms, and performs all absolute positioning calculations to a . /// /// The in which to transform. - protected override IAbsolutePositionReport Transform(IAbsolutePositionReport report) + protected override IAbsolutePositionReport? Transform(IAbsolutePositionReport report) { // Apply transformation - var pos = Vector2.Transform(report.Position, this.TransformationMatrix); + var pos = Vector2.Transform(report.Position, TransformationMatrix); // Clipping to display bounds - var clippedPoint = Vector2.Clamp(pos, this.min, this.max); + var clippedPoint = Vector2.Clamp(pos, _min, _max); if (AreaLimiting && clippedPoint != pos) return null; @@ -135,6 +168,7 @@ protected override IAbsolutePositionReport Transform(IAbsolutePositionReport rep return report; } + [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")] protected override void OnOutput(IDeviceReport report) { if (report is IEraserReport eraserReport && Pointer is IEraserHandler eraserHandler) @@ -142,7 +176,7 @@ protected override void OnOutput(IDeviceReport report) if (report is IAbsolutePositionReport absReport) Pointer.SetPosition(absReport.Position); if (report is ITabletReport tabletReport && Pointer is IPressureHandler pressureHandler) - pressureHandler.SetPressure(tabletReport.Pressure / (float)Tablet.Properties.Specifications.Pen.MaxPressure); + pressureHandler.SetPressure(tabletReport.Pressure / (float)Tablet.Configuration.Specifications.Pen!.MaxPressure); if (report is ITiltReport tiltReport && Pointer is ITiltHandler tiltHandler) tiltHandler.SetTilt(tiltReport.Tilt); if (report is IProximityReport proximityReport) @@ -159,5 +193,28 @@ protected override void OnOutput(IDeviceReport report) synchronousPointer.Flush(); } } + + public static AngledArea GetDefaultInputArea(DigitizerSpecifications digitizer) + { + return new AngledArea + { + Width = digitizer.Width, + Height = digitizer.Height, + XPosition = digitizer.Width / 2, + YPosition = digitizer.Height / 2, + Rotation = 0 + }; + } + + public static Area GetDefaultOutputArea(IVirtualScreen virtualScreen) + { + return new Area + { + Width = virtualScreen.Width, + Height = virtualScreen.Height, + XPosition = virtualScreen.Width / 2, + YPosition = virtualScreen.Height / 2 + }; + } } } diff --git a/OpenTabletDriver/Output/AsyncDevicePipelineElement.cs b/OpenTabletDriver/Output/AsyncDevicePipelineElement.cs new file mode 100644 index 000000000..202243357 --- /dev/null +++ b/OpenTabletDriver/Output/AsyncDevicePipelineElement.cs @@ -0,0 +1,122 @@ +using System; +using System.ComponentModel; +using JetBrains.Annotations; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Output +{ + /// + /// An asynchronous pipeline element, controlled by a hardware scheduler. + /// + /// + /// The pipeline element type. + /// + [PublicAPI] + public abstract class AsyncDevicePipelineElement : IDevicePipelineElement, IDisposable + { + private readonly ITimer _scheduler; + private readonly HPETDeltaStopwatch _consumeWatch = new HPETDeltaStopwatch(); + + protected AsyncDevicePipelineElement(ITimer scheduler) + { + _scheduler = scheduler; + _scheduler.Elapsed += () => + { + lock (_synchronizationObject) + { + UpdateState(); + } + }; + _scheduler.Start(); + } + + private readonly object _synchronizationObject = new object(); + private float? _reportMsAvg; + private float _frequency; + + /// + /// The current state of the . + /// + protected IDeviceReport? State { set; get; } + + public event Action? Emit; + + public abstract PipelinePosition Position { get; } + + [Setting("Frequency"), Unit("hz"), DefaultValue(1000.0f)] + public float Frequency + { + set + { + _frequency = value; + if (_scheduler.Enabled) + _scheduler.Stop(); + _scheduler.Interval = 1000f / value; + _scheduler.Start(); + } + get => _frequency; + } + + public void Consume(IDeviceReport value) + { + lock (_synchronizationObject) + { + State = value; + ConsumeState(); + var consumeDelta = (float)_consumeWatch.Restart().TotalMilliseconds; + if (consumeDelta < 150) + _reportMsAvg = (_reportMsAvg + ((consumeDelta - _reportMsAvg) * 0.1f)) ?? consumeDelta; + } + } + + /// + /// Sets the internal state of the within a synchronized context + /// to avoid race conditions against . + /// + /// + /// This is called by whenever a report is received from a linked upstream element. + /// + protected abstract void ConsumeState(); + + /// + /// Updates the state of the within a synchronized context. + /// This is invoked by the on the interval defined by . + /// The implementer must invoke to continue the input pipeline. + /// + /// + /// Call to check if the pen is in range and avoid false emit. + /// + protected abstract void UpdateState(); + + /// + /// Determines if pen is in tablet hover range. + /// + /// + /// We determine that a pen is out of range when is called within + /// after a time equivalent to a report and a half has already passed. If however, the report interval is faster than 3ms, + /// we instead check if 3ms has already passed before declaring that the pen is out of range. + /// + /// True if pen is in range + protected bool PenIsInRange() + { + return (float)_consumeWatch.Elapsed.TotalMilliseconds < Math.Max(3, (_reportMsAvg * 1.5f) ?? float.MaxValue); + } + + /// + /// Invokes event and transfers data to the next element. + /// + protected void OnEmit() + { + if (State != null) + Emit?.Invoke(State); + } + + public void Dispose() + { + _scheduler.Dispose(); + } + + ~AsyncDevicePipelineElement() => Dispose(); + } +} diff --git a/OpenTabletDriver/Output/IDevicePipelineElement.cs b/OpenTabletDriver/Output/IDevicePipelineElement.cs new file mode 100644 index 000000000..222701bc7 --- /dev/null +++ b/OpenTabletDriver/Output/IDevicePipelineElement.cs @@ -0,0 +1,19 @@ +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Output +{ + /// + /// A pipeline element with a predefined position within the pipeline. + /// + /// + /// The pipeline element type. + /// + public interface IDevicePipelineElement : IPipelineElement + { + /// + /// The position in which this will be processed. + /// This helps determine what the expected input units will be. + /// + PipelinePosition Position { get; } + } +} diff --git a/OpenTabletDriver/Output/IMouseButtonSource.cs b/OpenTabletDriver/Output/IMouseButtonSource.cs new file mode 100644 index 000000000..fff5f3f96 --- /dev/null +++ b/OpenTabletDriver/Output/IMouseButtonSource.cs @@ -0,0 +1,9 @@ +using OpenTabletDriver.Platform.Pointer; + +namespace OpenTabletDriver.Output +{ + public interface IMouseButtonSource + { + IMouseButtonHandler MouseButtonHandler { get; } + } +} diff --git a/OpenTabletDriver.Plugin/Output/IOutputMode.cs b/OpenTabletDriver/Output/IOutputMode.cs similarity index 65% rename from OpenTabletDriver.Plugin/Output/IOutputMode.cs rename to OpenTabletDriver/Output/IOutputMode.cs index 53c05be21..ca2f66239 100644 --- a/OpenTabletDriver.Plugin/Output/IOutputMode.cs +++ b/OpenTabletDriver/Output/IOutputMode.cs @@ -1,9 +1,14 @@ using System.Collections.Generic; using System.Numerics; -using OpenTabletDriver.Plugin.Tablet; +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; -namespace OpenTabletDriver.Plugin.Output +namespace OpenTabletDriver.Output { + /// + /// An output mode in which all reports are ultimately transformed and subsequently handled. + /// + [PublicAPI] public interface IOutputMode : IPipelineElement { /// @@ -15,17 +20,16 @@ public interface IOutputMode : IPipelineElement /// /// The list of pipeline elements in which the report is modified. /// - IList> Elements { set; get; } + IList? Elements { set; get; } /// - /// The transformation matrix in which the + /// The transformation matrix in which the device report will be modified. /// - /// Matrix3x2 TransformationMatrix { get; } /// /// The current tablet assigned to this /// - TabletReference Tablet { set; get; } + InputDevice Tablet { get; } } } diff --git a/OpenTabletDriver.Plugin/Output/IPipelineElement.cs b/OpenTabletDriver/Output/IPipelineElement.cs similarity index 56% rename from OpenTabletDriver.Plugin/Output/IPipelineElement.cs rename to OpenTabletDriver/Output/IPipelineElement.cs index f02c11664..c3704cfd9 100644 --- a/OpenTabletDriver.Plugin/Output/IPipelineElement.cs +++ b/OpenTabletDriver/Output/IPipelineElement.cs @@ -1,7 +1,15 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Output +namespace OpenTabletDriver.Output { + /// + /// A pipeline element in which is modified. + /// + /// + /// The pipeline element type. + /// + [PublicAPI] public interface IPipelineElement { /// @@ -12,6 +20,6 @@ public interface IPipelineElement /// /// Invoked when an object of is to be pushed to the next link in the pipeline. /// - event Action Emit; + event Action? Emit; } } diff --git a/OpenTabletDriver/Output/OutputMode.cs b/OpenTabletDriver/Output/OutputMode.cs new file mode 100644 index 000000000..25ccf03cf --- /dev/null +++ b/OpenTabletDriver/Output/OutputMode.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using JetBrains.Annotations; +using OpenTabletDriver.Tablet; + +namespace OpenTabletDriver.Output +{ + /// + /// The base implementation of an , complete with transformation calculation. + /// + [PublicAPI] + public abstract class OutputMode : PipelineManager, IOutputMode + { + protected OutputMode(InputDevice tablet) + { + Tablet = tablet; + Passthrough = true; + } + + private bool _passthrough; + private InputDevice _tablet = null!; + private IList? _elements; + private IPipelineElement? _entryElement; + + public event Action? Emit; + + protected bool Passthrough + { + private set + { + Action output = OnOutput; + if (value && !_passthrough) + { + _entryElement = this; + Link(this, output); + _passthrough = true; + } + else if (!value && _passthrough) + { + _entryElement = null; + Unlink(this, output); + _passthrough = false; + } + } + get => _passthrough; + } + + private IList _preTransformElements = Array.Empty(); + private IList _postTransformElements = Array.Empty(); + + public Matrix3x2 TransformationMatrix { protected set; get; } + + public IList? Elements + { + set + { + _elements = value; + + Passthrough = false; + DestroyInternalLinks(); + + if (Elements != null && Elements.Any()) + { + _preTransformElements = GroupElements(Elements, PipelinePosition.PreTransform); + _postTransformElements = GroupElements(Elements, PipelinePosition.PostTransform); + + Action output = OnOutput; + + if (_preTransformElements.Any() && !_postTransformElements.Any()) + { + _entryElement = _preTransformElements.First(); + + // PreTransform --> Transform --> Output + LinkAll(_preTransformElements, this, output); + } + else if (_postTransformElements.Any() && !_preTransformElements.Any()) + { + _entryElement = this; + + // Transform --> PostTransform --> Output + LinkAll(this, _postTransformElements, output); + } + else if (_preTransformElements.Any() && _postTransformElements.Any()) + { + _entryElement = _preTransformElements.First(); + + // PreTransform --> Transform --> PostTransform --> Output + LinkAll(_preTransformElements, this, _postTransformElements, output); + } + } + else + { + Passthrough = true; + _preTransformElements = Array.Empty(); + _postTransformElements = Array.Empty(); + } + } + get => _elements; + } + + public InputDevice Tablet + { + set + { + _tablet = value; + TransformationMatrix = CreateTransformationMatrix(); + } + get => _tablet; + } + + public virtual void Consume(IDeviceReport report) + { + if (report is IAbsolutePositionReport tabletReport) + if (Transform(tabletReport) is IAbsolutePositionReport transformedReport) + report = transformedReport; + + Emit?.Invoke(report); + } + + public virtual void Read(IDeviceReport deviceReport) => _entryElement?.Consume(deviceReport); + + /// + /// Invoked to calculate the new transformation matrix. + /// + /// A transformation matrix to be applied to the IDeviceReport. + protected abstract Matrix3x2 CreateTransformationMatrix(); + + /// + /// Transform an absolutely positioned device report. + /// + /// + /// The tablet report to be transformed. + /// + /// + /// A transformed . + /// + protected abstract IAbsolutePositionReport? Transform(IAbsolutePositionReport tabletReport); + + /// + /// Pushes the final report state to its native handlers. + /// + /// A transformed report to output. + protected abstract void OnOutput(IDeviceReport report); + + private void DestroyInternalLinks() + { + Action output = OnOutput; + + if (_preTransformElements.Any() && !_postTransformElements.Any()) + { + UnlinkAll(_preTransformElements, this, output); + } + else if (_postTransformElements.Any() && !_preTransformElements.Any()) + { + UnlinkAll(this, _postTransformElements, output); + } + else if (_preTransformElements.Any() && _postTransformElements.Any()) + { + UnlinkAll(_preTransformElements, this, _postTransformElements, output); + } + } + } +} diff --git a/OpenTabletDriver.Plugin/Output/PipelineManager.cs b/OpenTabletDriver/Output/PipelineManager.cs similarity index 74% rename from OpenTabletDriver.Plugin/Output/PipelineManager.cs rename to OpenTabletDriver/Output/PipelineManager.cs index 7d36fae1b..406b2ef50 100644 --- a/OpenTabletDriver.Plugin/Output/PipelineManager.cs +++ b/OpenTabletDriver/Output/PipelineManager.cs @@ -1,12 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Output +namespace OpenTabletDriver.Output { + /// + /// A pipeline manager handling chaining of pipeline elements and final output. + /// + /// + /// The type being passed through the pipeline. + /// + [PublicAPI] public class PipelineManager { - protected void Link(IPipelineElement source, T2 destination) + protected void Link(IPipelineElement? source, T2 destination) { if (source != null && destination != null) { @@ -25,7 +33,7 @@ protected void Link(IPipelineElement source, T2 destination) } } - protected void Unlink(IPipelineElement source, T2 destination) + protected void Unlink(IPipelineElement? source, T2 destination) { if (source != null && destination != null) { @@ -44,11 +52,11 @@ protected void Unlink(IPipelineElement source, T2 destination) } } - protected void LinkElements(IEnumerable> elements) + protected void LinkElements(IEnumerable>? elements) { if (elements != null && elements.Any()) { - IPipelineElement prevElement = null; + var prevElement = default(IPipelineElement); foreach (var element in elements) { Link(prevElement, element); @@ -57,11 +65,11 @@ protected void LinkElements(IEnumerable> elements) } } - protected void UnlinkElements(IEnumerable> elements) + protected void UnlinkElements(IEnumerable>? elements) { if (elements != null && elements.Any()) { - IPipelineElement prevElement = null; + var prevElement = default(IPipelineElement?); foreach (var element in elements) { Unlink(prevElement, element); @@ -72,7 +80,7 @@ protected void UnlinkElements(IEnumerable> elements) protected void LinkAll(params object[] elements) { - foreach ((var prev, var next) in elements.Zip(elements.Skip(1))) + foreach (var (prev, next) in elements.Zip(elements.Skip(1))) { if (prev is IPipelineElement prevElement) { @@ -88,7 +96,7 @@ protected void LinkAll(params object[] elements) protected void UnlinkAll(params object[] elements) { - foreach ((var prev, var next) in elements.Zip(elements.Skip(1))) + foreach (var (prev, next) in elements.Zip(elements.Skip(1))) { if (prev is IPipelineElement prevElement) { @@ -102,9 +110,9 @@ protected void UnlinkAll(params object[] elements) } } - protected IList> GroupElements(IList> elements, PipelinePosition position) + protected IList GroupElements(IList? elements, PipelinePosition position) { - return elements.Where(e => e.Position == position)?.ToArray() ?? Array.Empty>(); + return elements?.Where(e => e.Position == position).ToArray() ?? Array.Empty(); } } } diff --git a/OpenTabletDriver.Plugin/Output/PipelinePosition.cs b/OpenTabletDriver/Output/PipelinePosition.cs similarity index 51% rename from OpenTabletDriver.Plugin/Output/PipelinePosition.cs rename to OpenTabletDriver/Output/PipelinePosition.cs index 3106b5f12..6cfabb1da 100644 --- a/OpenTabletDriver.Plugin/Output/PipelinePosition.cs +++ b/OpenTabletDriver/Output/PipelinePosition.cs @@ -1,5 +1,11 @@ -namespace OpenTabletDriver.Plugin.Output +using JetBrains.Annotations; + +namespace OpenTabletDriver.Output { + /// + /// Marks the position in a positioned pipeline element. + /// + [PublicAPI] public enum PipelinePosition { None = 0, diff --git a/OpenTabletDriver.Plugin/Output/RelativeOutputMode.cs b/OpenTabletDriver/Output/RelativeOutputMode.cs similarity index 51% rename from OpenTabletDriver.Plugin/Output/RelativeOutputMode.cs rename to OpenTabletDriver/Output/RelativeOutputMode.cs index 4bd0dabc0..9acc37a84 100644 --- a/OpenTabletDriver.Plugin/Output/RelativeOutputMode.cs +++ b/OpenTabletDriver/Output/RelativeOutputMode.cs @@ -1,28 +1,37 @@ using System; +using System.ComponentModel; using System.Numerics; -using OpenTabletDriver.Plugin.Attributes; -using OpenTabletDriver.Plugin.Platform.Pointer; -using OpenTabletDriver.Plugin.Tablet; -using OpenTabletDriver.Plugin.Timing; +using JetBrains.Annotations; +using OpenTabletDriver.Attributes; +using OpenTabletDriver.Platform.Pointer; +using OpenTabletDriver.Tablet; -namespace OpenTabletDriver.Plugin.Output +namespace OpenTabletDriver.Output { /// - /// A relatively positioned output mode. + /// A relative positioned output mode. /// [PluginIgnore] + [PublicAPI] public abstract class RelativeOutputMode : OutputMode { - private Vector2? lastPos; - private HPETDeltaStopwatch stopwatch = new HPETDeltaStopwatch(true); - private bool skipReport; + private readonly HPETDeltaStopwatch _stopwatch = new HPETDeltaStopwatch(); + + protected RelativeOutputMode(InputDevice tablet, IRelativePointer relativePointer) + : base(tablet) + { + Pointer = relativePointer; + } + + private Vector2? _lastPos; + private bool _skipReport; /// /// The class in which the final relative positioned output is handled. /// - public abstract IRelativePointer Pointer { set; get; } + public IRelativePointer Pointer { get; } - private Vector2 sensitivity; + private Vector2 _sensitivity; /// /// The sensitivity vector in which input will be transformed. @@ -30,62 +39,65 @@ public abstract class RelativeOutputMode : OutputMode /// This sensitivity is in mm/px. /// /// + [Setting("Sensitivity"), Unit("px/mm"), MemberSourcedDefaults(nameof(GetDefaultSensitivity))] public Vector2 Sensitivity { set { - this.sensitivity = value; - this.TransformationMatrix = CreateTransformationMatrix(); + _sensitivity = value; + TransformationMatrix = CreateTransformationMatrix(); } - get => this.sensitivity; + get => _sensitivity; } - private float rotation; + private float _rotation; /// /// The angle of rotation to be applied to the input. /// + [Setting("Rotation"), Unit("°"), DefaultValue(0f)] public float Rotation { set { - this.rotation = value; - this.TransformationMatrix = CreateTransformationMatrix(); + _rotation = value; + TransformationMatrix = CreateTransformationMatrix(); } - get => this.rotation; + get => _rotation; } /// /// The delay in which to reset the last known position in relative positioning. /// - public TimeSpan ResetTime { set; get; } + [Setting("Reset Delay"), MemberSourcedDefaults(nameof(GetDefaultResetDelay))] + public TimeSpan ResetDelay { set; get; } protected override Matrix3x2 CreateTransformationMatrix() { - this.skipReport = true; // Prevents cursor from jumping on sensitivity change + _skipReport = true; // Prevents cursor from jumping on sensitivity change var transform = Matrix3x2.CreateRotation( - (float)(-Rotation * System.Math.PI / 180)); + (float)(-Rotation * Math.PI / 180)); - var digitizer = Tablet?.Properties.Specifications.Digitizer; + var digitizer = Tablet?.Configuration.Specifications.Digitizer; return transform *= Matrix3x2.CreateScale( - sensitivity.X * ((digitizer?.Width / digitizer?.MaxX) ?? 0.01f), - sensitivity.Y * ((digitizer?.Height / digitizer?.MaxY) ?? 0.01f)); + _sensitivity.X * ((digitizer?.Width / digitizer?.MaxX) ?? 0.01f), + _sensitivity.Y * ((digitizer?.Height / digitizer?.MaxY) ?? 0.01f)); } - protected override IAbsolutePositionReport Transform(IAbsolutePositionReport report) + protected override IAbsolutePositionReport? Transform(IAbsolutePositionReport report) { - var deltaTime = stopwatch.Restart(); + var deltaTime = _stopwatch.Restart(); - var pos = Vector2.Transform(report.Position, this.TransformationMatrix); - var delta = pos - this.lastPos; + var pos = Vector2.Transform(report.Position, TransformationMatrix); + var delta = pos - _lastPos; - this.lastPos = pos; - report.Position = deltaTime < ResetTime ? delta.GetValueOrDefault() : Vector2.Zero; + _lastPos = pos; + report.Position = deltaTime < ResetDelay ? delta.GetValueOrDefault() : Vector2.Zero; - if (skipReport) + if (_skipReport) { - skipReport = false; + _skipReport = false; return null; } @@ -99,7 +111,7 @@ protected override void OnOutput(IDeviceReport report) if (report is IAbsolutePositionReport absReport) Pointer.SetPosition(absReport.Position); if (report is ITabletReport tabletReport && Pointer is IPressureHandler pressureHandler) - pressureHandler.SetPressure(tabletReport.Pressure / (float)Tablet.Properties.Specifications.Pen.MaxPressure); + pressureHandler.SetPressure(tabletReport.Pressure / (float)Tablet.Configuration.Specifications.Pen!.MaxPressure); if (report is ITiltReport tiltReport && Pointer is ITiltHandler tiltHandler) tiltHandler.SetTilt(tiltReport.Tilt); if (report is IProximityReport proximityReport) @@ -116,5 +128,15 @@ protected override void OnOutput(IDeviceReport report) synchronousPointer.Flush(); } } + + public static Vector2 GetDefaultSensitivity() + { + return new Vector2(10, 10); + } + + public static TimeSpan GetDefaultResetDelay() + { + return TimeSpan.FromMilliseconds(500); + } } } diff --git a/OpenTabletDriver/Platform/Display/IDisplay.cs b/OpenTabletDriver/Platform/Display/IDisplay.cs new file mode 100644 index 000000000..7205d745a --- /dev/null +++ b/OpenTabletDriver/Platform/Display/IDisplay.cs @@ -0,0 +1,32 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Display +{ + /// + /// A physical display from the display server. + /// + [PublicAPI] + public interface IDisplay + { + /// + /// The index of the display, typically in order top left to bottom right. + /// + int Index { get; } + + /// + /// The width of the display in pixels. + /// + float Width { get; } + + /// + /// The height of the display in pixels. + /// + float Height { get; } + + /// + /// The offset of the display in pixels, anchored from the top left corner of the display. + /// + Vector2 Position { get; } + } +} diff --git a/OpenTabletDriver/Platform/Display/IVirtualScreen.cs b/OpenTabletDriver/Platform/Display/IVirtualScreen.cs new file mode 100644 index 000000000..24e0012ae --- /dev/null +++ b/OpenTabletDriver/Platform/Display/IVirtualScreen.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Display +{ + /// + /// A root display server with a virtual for the maximum usable area. + /// + [PublicAPI] + public interface IVirtualScreen : IDisplay + { + /// + /// An enumeration of all displays on this display server. + /// + IEnumerable Displays { get; } + } +} diff --git a/OpenTabletDriver/Platform/Environment/IEnvironmentHandler.cs b/OpenTabletDriver/Platform/Environment/IEnvironmentHandler.cs new file mode 100644 index 000000000..28cbeaf6a --- /dev/null +++ b/OpenTabletDriver/Platform/Environment/IEnvironmentHandler.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Environment +{ + /// + /// A system environment handler. + /// This is used to open files in the shell. + /// + [PublicAPI] + public interface IEnvironmentHandler + { + /// + /// Opens a file at the specified path. + /// + /// + /// The path to open. + /// + void Open(string path); + + /// + /// Opens a folder at the specified path. + /// + /// + /// The path to open. + /// + void OpenFolder(string path); + } +} diff --git a/OpenTabletDriver/Platform/Keyboard/IKeysProvider.cs b/OpenTabletDriver/Platform/Keyboard/IKeysProvider.cs new file mode 100644 index 000000000..02f6d547f --- /dev/null +++ b/OpenTabletDriver/Platform/Keyboard/IKeysProvider.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Keyboard +{ + /// + /// A native keys provider, mapping Eto.Forms' key names to native key types. + /// + [PublicAPI] + public interface IKeysProvider + { + /// + /// A dictionary mapping Eto.Forms' key names to native key types. + /// + IDictionary EtoToNative { get; } + } +} diff --git a/OpenTabletDriver/Platform/Keyboard/IVirtualKeyboard.cs b/OpenTabletDriver/Platform/Keyboard/IVirtualKeyboard.cs new file mode 100644 index 000000000..bddbc59d8 --- /dev/null +++ b/OpenTabletDriver/Platform/Keyboard/IVirtualKeyboard.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Keyboard +{ + /// + /// A virtual keyboard for invoking keyboard events. + /// + [PublicAPI] + public interface IVirtualKeyboard + { + /// + /// Invokes a key press. + /// + /// + /// The key to press. + /// + void Press(string key); + + /// + /// Invokes a key release. + /// + /// + /// The key to release. + /// + void Release(string key); + + /// + /// Invokes multiple keys being pressed at once. + /// + /// + /// The keys to press. + /// + void Press(IEnumerable keys); + + /// + /// Invokes multiple keys being released at once. + /// + /// + /// The keys to release. + /// + void Release(IEnumerable keys); + + /// + /// A list of all supported keys in this virtual keyboard. + /// + /// + /// This is sourced from , so it will only include platform supported keys. + /// + IEnumerable SupportedKeys { get; } + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IAbsolutePointer.cs b/OpenTabletDriver/Platform/Pointer/IAbsolutePointer.cs new file mode 100644 index 000000000..fa8b8521c --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IAbsolutePointer.cs @@ -0,0 +1,21 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// An absolute positioned pointer. Accepts an absolutely positioned vector and invokes a handler performing + /// absolute positioning. + /// + [PublicAPI] + public interface IAbsolutePointer : IMouseButtonHandler + { + /// + /// Sets an absolute position. + /// + /// + /// The position to set mapped to the output in pixels. + /// + void SetPosition(Vector2 pos); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IEraserHandler.cs b/OpenTabletDriver/Platform/Pointer/IEraserHandler.cs new file mode 100644 index 000000000..e5e609e52 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IEraserHandler.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A native eraser handler, commonly used in drawing applications to apply an erase function or binding. + /// + [PublicAPI] + public interface IEraserHandler + { + /// + /// Sets the current eraser state. + /// + /// + /// Whether the eraser is the active tip. + /// + void SetEraser(bool isEraser); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IHoverDistanceHandler.cs b/OpenTabletDriver/Platform/Pointer/IHoverDistanceHandler.cs new file mode 100644 index 000000000..12601d0d6 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IHoverDistanceHandler.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A pen hover distance handler. + /// + [PublicAPI] + public interface IHoverDistanceHandler + { + /// + /// Sets the active pen hover distance. + /// + /// + /// Sets the pen hover distance. This is an arbitrary value with no defined real-world units. + /// + void SetHoverDistance(uint distance); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IMouseButtonHandler.cs b/OpenTabletDriver/Platform/Pointer/IMouseButtonHandler.cs new file mode 100644 index 000000000..641112191 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IMouseButtonHandler.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A mouse button handler. + /// + [PublicAPI] + public interface IMouseButtonHandler + { + /// + /// Presses a mouse button. + /// + /// + /// The button to press. + /// + void MouseDown(MouseButton button); + + /// + /// Releases a mouse button. + /// + /// + /// The button to release. + /// + void MouseUp(MouseButton button); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IPressureHandler.cs b/OpenTabletDriver/Platform/Pointer/IPressureHandler.cs new file mode 100644 index 000000000..dac358e6a --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IPressureHandler.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A pen pressure handler. + /// + [PublicAPI] + public interface IPressureHandler : IAbsolutePointer + { + /// + /// Sets the pressure of the pen. + /// + /// + /// The percentage of pressure output, ranging from 0 to 100. + /// + void SetPressure(float percentage); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IProximityHandler.cs b/OpenTabletDriver/Platform/Pointer/IProximityHandler.cs new file mode 100644 index 000000000..bf6032ee7 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IProximityHandler.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A pen proximity handler. + /// + [PublicAPI] + public interface IProximityHandler + { + /// + /// Sets the pen proximity state. + /// + /// + /// Whether the pen is in proximity to the tablet. + /// + void SetProximity(bool proximity); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/IRelativePointer.cs b/OpenTabletDriver/Platform/Pointer/IRelativePointer.cs new file mode 100644 index 000000000..8ca512fb7 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/IRelativePointer.cs @@ -0,0 +1,21 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A relative positioned pointer. Accepts a relatively positioned vector and invokes a handler performing relative + /// positioning. + /// + [PublicAPI] + public interface IRelativePointer : IMouseButtonHandler + { + /// + /// Sets a relative position. + /// + /// + /// The position to change the output by in pixels. + /// + void SetPosition(Vector2 delta); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/ISynchronousPointer.cs b/OpenTabletDriver/Platform/Pointer/ISynchronousPointer.cs new file mode 100644 index 000000000..2e7c1dd13 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/ISynchronousPointer.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A pointer which requires syncing to update the current state. + /// + [PublicAPI] + public interface ISynchronousPointer + { + /// + /// Resets the pointer's state. + /// + void Reset(); + + /// + /// Flushes the current state to the native handler. + /// + void Flush(); + } +} diff --git a/OpenTabletDriver/Platform/Pointer/ITiltHandler.cs b/OpenTabletDriver/Platform/Pointer/ITiltHandler.cs new file mode 100644 index 000000000..1fe57d684 --- /dev/null +++ b/OpenTabletDriver/Platform/Pointer/ITiltHandler.cs @@ -0,0 +1,18 @@ +using System.Numerics; + +namespace OpenTabletDriver.Platform.Pointer +{ + /// + /// A pen tilt handler. + /// + public interface ITiltHandler + { + /// + /// Sets the pen tilt angle. + /// + /// + /// The current tilt angle. This is an arbitrary value with no defined real-world units. + /// + void SetTilt(Vector2 tilt); + } +} diff --git a/OpenTabletDriver.Plugin/Platform/Pointer/MouseButton.cs b/OpenTabletDriver/Platform/Pointer/MouseButton.cs similarity index 51% rename from OpenTabletDriver.Plugin/Platform/Pointer/MouseButton.cs rename to OpenTabletDriver/Platform/Pointer/MouseButton.cs index b15cf3e9d..00a79fd8a 100644 --- a/OpenTabletDriver.Plugin/Platform/Pointer/MouseButton.cs +++ b/OpenTabletDriver/Platform/Pointer/MouseButton.cs @@ -1,5 +1,11 @@ -namespace OpenTabletDriver.Plugin.Platform.Pointer +using JetBrains.Annotations; + +namespace OpenTabletDriver.Platform.Pointer { + /// + /// A mouse button. + /// + [PublicAPI] public enum MouseButton { None = 0, diff --git a/OpenTabletDriver/SystemDrivers/DriverInfo.cs b/OpenTabletDriver/SystemDrivers/DriverInfo.cs index 05f3dba93..01c4813d6 100644 --- a/OpenTabletDriver/SystemDrivers/DriverInfo.cs +++ b/OpenTabletDriver/SystemDrivers/DriverInfo.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using OpenTabletDriver.SystemDrivers.InfoProviders; +using JetBrains.Annotations; +using OpenTabletDriver.SystemDrivers.Providers; namespace OpenTabletDriver.SystemDrivers { @@ -11,12 +13,13 @@ namespace OpenTabletDriver.SystemDrivers /// /// See to get all the currently active tablet drivers. /// + [PublicAPI] public class DriverInfo { /// /// The human-friendly name of the driver. /// - public string Name { get; internal set; } + public string Name { internal set; get; } = string.Empty; /// /// Running processes that might be associated with the driver. @@ -24,17 +27,17 @@ public class DriverInfo /// /// This is set to null when there is no associated process. /// - public Process[] Processes { get; internal set; } + public Process[] Processes { internal set; get; } = Array.Empty(); /// /// Provides hints of whether this driver might interfere with OTD's detection mechanism, or prevent OTD from accessing the tablet. /// - public bool IsBlockingDriver { get; internal set; } + public bool IsBlockingDriver { internal set; get; } /// /// Returns true if this driver sends input to the operating system. /// - public bool IsSendingInput { get; internal set; } + public bool IsSendingInput { internal set; get; } /// /// Retrieves all the currently active tablet drivers. @@ -58,10 +61,10 @@ public static IEnumerable GetDriverInfos() // Remove "UC Logic" duplicates return providers.Select(provider => provider.GetDriverInfo()) .Where(i => i != null) - .GroupBy(i => i.Name) - .Select(g => g.First()); + .GroupBy(i => i!.Name) + .Select(g => g.First())!; } - internal static Process[] SystemProcesses { get; private set; } + internal static Process[] SystemProcesses { get; private set; } = Array.Empty(); } } diff --git a/OpenTabletDriver/SystemDrivers/IDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/IDriverInfoProvider.cs index 0b34c5da1..81bf3c5eb 100644 --- a/OpenTabletDriver/SystemDrivers/IDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/IDriverInfoProvider.cs @@ -2,6 +2,6 @@ namespace OpenTabletDriver.SystemDrivers { internal interface IDriverInfoProvider { - DriverInfo GetDriverInfo(); + DriverInfo? GetDriverInfo(); } } diff --git a/OpenTabletDriver/SystemDrivers/Providers/GaomonDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/GaomonDriverInfoProvider.cs index 0b68a9c4d..77c64c7e1 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/GaomonDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/GaomonDriverInfoProvider.cs @@ -1,4 +1,4 @@ -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class GaomonDriverInfoProvider : ProcessModuleQueryableDriverInfoProvider { diff --git a/OpenTabletDriver/SystemDrivers/Providers/HuionDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/HuionDriverInfoProvider.cs index 5fc6c4569..1be0e5bb5 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/HuionDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/HuionDriverInfoProvider.cs @@ -1,4 +1,4 @@ -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class HuionDriverInfoProvider : ProcessModuleQueryableDriverInfoProvider { diff --git a/OpenTabletDriver/SystemDrivers/Providers/OpenTabletDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/OpenTabletDriverInfoProvider.cs index 9d2da60fc..569426e9e 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/OpenTabletDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/OpenTabletDriverInfoProvider.cs @@ -1,8 +1,8 @@ -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { - public class OpenTabletDriverInfoProvider : IDriverInfoProvider + internal class OpenTabletDriverInfoProvider : IDriverInfoProvider { - public DriverInfo GetDriverInfo() + public DriverInfo? GetDriverInfo() { var daemonInstanceName = "OpenTabletDriver.Daemon"; if (Instance.Exists(daemonInstanceName) && !Instance.IsOwnerOf(daemonInstanceName)) diff --git a/OpenTabletDriver/SystemDrivers/Providers/ProcessModuleQueryableDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/ProcessModuleQueryableDriverInfoProvider.cs index 2cfe139bb..a7378987b 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/ProcessModuleQueryableDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/ProcessModuleQueryableDriverInfoProvider.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; +using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal abstract class ProcessModuleQueryableDriverInfoProvider : IDriverInfoProvider { @@ -16,33 +15,35 @@ internal abstract class ProcessModuleQueryableDriverInfoProvider : IDriverInfoPr protected abstract string[] WinProcessNames { get; } protected abstract string[] Heuristics { get; } - private static string pnpUtil; - private static string linuxModules; + private static string? pnpUtil; + private static string? linuxModules; - public DriverInfo GetDriverInfo() + public DriverInfo? GetDriverInfo() { return SystemInterop.CurrentPlatform switch { - PluginPlatform.Windows => GetWinDriverInfo(), - PluginPlatform.Linux => GetLinuxDriverInfo(), + SystemPlatform.Windows => GetWinDriverInfo(), + SystemPlatform.Linux => GetLinuxDriverInfo(), _ => null }; } - protected virtual DriverInfo GetWinDriverInfo() + protected virtual DriverInfo? GetWinDriverInfo() { - IEnumerable processes; - var match = Heuristics.Any(name => Regex.IsMatch(pnpUtil, name, RegexOptions.IgnoreCase)); + if (pnpUtil == null) + Refresh(); + + var match = Heuristics.Any(name => Regex.IsMatch(pnpUtil!, name, RegexOptions.IgnoreCase)); if (match) { - processes = DriverInfo.SystemProcesses + var processes = DriverInfo.SystemProcesses .Where(p => WinProcessNames.Concat(Heuristics) - .Any(n => Regex.IsMatch(p.ProcessName, n, RegexOptions.IgnoreCase))); + .Any(n => Regex.IsMatch(p.ProcessName, n, RegexOptions.IgnoreCase))); return new DriverInfo { Name = FriendlyName, - Processes = processes.Any() ? processes.ToArray() : null, + Processes = processes.Any() ? processes.ToArray() : Array.Empty(), IsBlockingDriver = true, IsSendingInput = processes.Any() }; @@ -51,9 +52,12 @@ protected virtual DriverInfo GetWinDriverInfo() return null; } - protected virtual DriverInfo GetLinuxDriverInfo() + protected virtual DriverInfo? GetLinuxDriverInfo() { - if (Regex.IsMatch(linuxModules, LinuxModuleName)) + if (linuxModules == null) + Refresh(); + + if (Regex.IsMatch(linuxModules!, LinuxModuleName)) { return new DriverInfo { @@ -62,17 +66,15 @@ protected virtual DriverInfo GetLinuxDriverInfo() IsSendingInput = true }; } - else - { - return null; - } + + return null; } internal static void Refresh() { switch (SystemInterop.CurrentPlatform) { - case PluginPlatform.Windows: + case SystemPlatform.Windows: { var pnputilProc = new Process { @@ -89,7 +91,7 @@ internal static void Refresh() pnpUtil = pnputilProc.StandardOutput.ReadToEnd(); break; } - case PluginPlatform.Linux: + case SystemPlatform.Linux: { linuxModules = File.ReadAllText("/proc/modules"); break; diff --git a/OpenTabletDriver/SystemDrivers/Providers/TabletDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/TabletDriverInfoProvider.cs index eaa62fb7c..a511f1421 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/TabletDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/TabletDriverInfoProvider.cs @@ -1,20 +1,19 @@ using System.Linq; using OpenTabletDriver.Interop; -using OpenTabletDriver.Plugin; -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class TabletDriverInfoProvider : IDriverInfoProvider { - private readonly string[] ProcessNames = new string[] + private static readonly string[] ProcessNames = new string[] { "TabletDriverGUI", "TabletDriverService" }; - public DriverInfo GetDriverInfo() + public DriverInfo? GetDriverInfo() { - if (SystemInterop.CurrentPlatform == PluginPlatform.Windows) + if (SystemInterop.CurrentPlatform == SystemPlatform.Windows) { var processes = DriverInfo.SystemProcesses.Where(p => ProcessNames.Contains(p.ProcessName)).ToArray(); if (processes.Any()) diff --git a/OpenTabletDriver/SystemDrivers/Providers/VeikkDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/VeikkDriverInfoProvider.cs index 0741a3060..b12cf11e9 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/VeikkDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/VeikkDriverInfoProvider.cs @@ -1,4 +1,4 @@ -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class VeikkDriverInfoDriver : ProcessModuleQueryableDriverInfoProvider { diff --git a/OpenTabletDriver/SystemDrivers/Providers/WacomDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/WacomDriverInfoProvider.cs index f37d0bb1d..91ac878b6 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/WacomDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/WacomDriverInfoProvider.cs @@ -1,6 +1,6 @@ using System; -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class WacomDriverInfoProvider : ProcessModuleQueryableDriverInfoProvider { @@ -17,7 +17,7 @@ internal class WacomDriverInfoProvider : ProcessModuleQueryableDriverInfoProvide "Wacom" }; - protected override DriverInfo GetWinDriverInfo() + protected override DriverInfo? GetWinDriverInfo() { var info = base.GetWinDriverInfo(); if (info != null) @@ -26,7 +26,7 @@ protected override DriverInfo GetWinDriverInfo() return info; } - protected override DriverInfo GetLinuxDriverInfo() + protected override DriverInfo? GetLinuxDriverInfo() { var info = base.GetLinuxDriverInfo(); if (info != null) diff --git a/OpenTabletDriver/SystemDrivers/Providers/XPPenDriverInfoProvider.cs b/OpenTabletDriver/SystemDrivers/Providers/XPPenDriverInfoProvider.cs index 14abd7a2e..a6bd32156 100644 --- a/OpenTabletDriver/SystemDrivers/Providers/XPPenDriverInfoProvider.cs +++ b/OpenTabletDriver/SystemDrivers/Providers/XPPenDriverInfoProvider.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Text.RegularExpressions; -namespace OpenTabletDriver.SystemDrivers.InfoProviders +namespace OpenTabletDriver.SystemDrivers.Providers { internal class XPPenDriverInfoProvider : ProcessModuleQueryableDriverInfoProvider { @@ -32,7 +32,7 @@ internal class XPPenDriverInfoProvider : ProcessModuleQueryableDriverInfoProvide "Veikk" }; - protected override DriverInfo GetWinDriverInfo() + protected override DriverInfo? GetWinDriverInfo() { var processes = DriverInfo.SystemProcesses .Where(p => WinProcessNames.Concat(Heuristics) @@ -49,10 +49,8 @@ protected override DriverInfo GetWinDriverInfo() IsSendingInput = true }; } - else - { - return null; - } + + return null; } } } diff --git a/OpenTabletDriver.Plugin/PluginPlatform.cs b/OpenTabletDriver/SystemPlatform.cs similarity index 54% rename from OpenTabletDriver.Plugin/PluginPlatform.cs rename to OpenTabletDriver/SystemPlatform.cs index cc371370f..f3145e662 100644 --- a/OpenTabletDriver.Plugin/PluginPlatform.cs +++ b/OpenTabletDriver/SystemPlatform.cs @@ -1,9 +1,14 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin +namespace OpenTabletDriver { + /// + /// An operating system platform. + /// + [PublicAPI] [Flags] - public enum PluginPlatform + public enum SystemPlatform { Unknown = 0, Windows = 1 << 0, diff --git a/OpenTabletDriver.Plugin/Tablet/AuxReport.cs b/OpenTabletDriver/Tablet/AuxReport.cs similarity index 85% rename from OpenTabletDriver.Plugin/Tablet/AuxReport.cs rename to OpenTabletDriver/Tablet/AuxReport.cs index 220c39488..f3868b1fb 100644 --- a/OpenTabletDriver.Plugin/Tablet/AuxReport.cs +++ b/OpenTabletDriver/Tablet/AuxReport.cs @@ -1,5 +1,8 @@ -namespace OpenTabletDriver.Plugin.Tablet +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + [PublicAPI] public struct AuxReport : IAuxReport { public AuxReport(byte[] report) diff --git a/OpenTabletDriver.Plugin/Tablet/AuxReportParser.cs b/OpenTabletDriver/Tablet/AuxReportParser.cs similarity index 70% rename from OpenTabletDriver.Plugin/Tablet/AuxReportParser.cs rename to OpenTabletDriver/Tablet/AuxReportParser.cs index 9ce347bb0..a1f31730a 100644 --- a/OpenTabletDriver.Plugin/Tablet/AuxReportParser.cs +++ b/OpenTabletDriver/Tablet/AuxReportParser.cs @@ -1,5 +1,8 @@ -namespace OpenTabletDriver.Plugin.Tablet +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + [PublicAPI] public class AuxReportParser : IReportParser { public IDeviceReport Parse(byte[] data) diff --git a/OpenTabletDriver/Tablet/ButtonSpecifications.cs b/OpenTabletDriver/Tablet/ButtonSpecifications.cs new file mode 100644 index 000000000..43aa6440a --- /dev/null +++ b/OpenTabletDriver/Tablet/ButtonSpecifications.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// Device specifications for buttons. + /// + [PublicAPI] + public class ButtonSpecifications + { + /// + /// The amount of buttons. + /// + [DisplayName("Buttons")] + public uint ButtonCount { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/ByteExtensions.cs b/OpenTabletDriver/Tablet/ByteExtensions.cs new file mode 100644 index 000000000..4b0f3f6fa --- /dev/null +++ b/OpenTabletDriver/Tablet/ByteExtensions.cs @@ -0,0 +1,24 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// Utility extensions for bytes. + /// + [PublicAPI] + public static class ByteExtensions + { + /// + /// Returns if a bit is set in a byte. + /// + /// The byte to index. + /// The index of the bit. + /// + /// Boolean representing if the bit was set. + /// + public static bool IsBitSet(this byte a, int bit) + { + return (a & (1 << bit)) != 0; + } + } +} diff --git a/OpenTabletDriver.Plugin/Tablet/DeviceIdentifier.cs b/OpenTabletDriver/Tablet/DeviceIdentifier.cs similarity index 73% rename from OpenTabletDriver.Plugin/Tablet/DeviceIdentifier.cs rename to OpenTabletDriver/Tablet/DeviceIdentifier.cs index 75967c2f7..f16e53c07 100644 --- a/OpenTabletDriver.Plugin/Tablet/DeviceIdentifier.cs +++ b/OpenTabletDriver/Tablet/DeviceIdentifier.cs @@ -1,56 +1,67 @@ using System.Collections.Generic; +using System.ComponentModel; +using JetBrains.Annotations; -#nullable enable - -namespace OpenTabletDriver.Plugin.Tablet +namespace OpenTabletDriver.Tablet { + /// + /// A device identifier. + /// + [PublicAPI] public class DeviceIdentifier { /// /// The Vendor ID of the device. /// + [DisplayName("Vendor ID")] public int VendorID { set; get; } /// /// The Product ID of the device. /// + [DisplayName("Product ID")] public int ProductID { set; get; } /// /// The maximum input report length reported by the device. /// + [DisplayName("Input Report Length")] public uint? InputReportLength { set; get; } /// /// The maximum output report length reported by the device. /// + [DisplayName("Output Report Length")] public uint? OutputReportLength { set; get; } /// /// The device report parser used by the detected device. /// + [DisplayName("Report Parser")] public string ReportParser { set; get; } = string.Empty; /// /// The feature report sent to initialize tablet functions. /// + [DisplayName("Feature Initialization Report")] public List? FeatureInitReport { set; get; } /// /// The output report sent to initialize tablet functions. /// + [DisplayName("Output Initialization Report")] public List? OutputInitReport { set; get; } /// /// Device strings to match against, used for identification. /// - /// The index to query - /// The value to match to the queried index + [DisplayName("Device Strings")] public Dictionary DeviceStrings { set; get; } = new Dictionary(); /// /// Device strings to query to initialize device endpoints. /// + [DisplayName("Initialization Strings")] public List InitializationStrings { set; get; } = new List(); } } diff --git a/OpenTabletDriver.Plugin/Tablet/DeviceReport.cs b/OpenTabletDriver/Tablet/DeviceReport.cs similarity index 70% rename from OpenTabletDriver.Plugin/Tablet/DeviceReport.cs rename to OpenTabletDriver/Tablet/DeviceReport.cs index 74809c4e1..5cd8e2769 100644 --- a/OpenTabletDriver.Plugin/Tablet/DeviceReport.cs +++ b/OpenTabletDriver/Tablet/DeviceReport.cs @@ -1,5 +1,8 @@ -namespace OpenTabletDriver.Plugin.Tablet +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + [PublicAPI] public struct DeviceReport : IDeviceReport { public DeviceReport(byte[] report) diff --git a/OpenTabletDriver.Plugin/Tablet/DeviceVendor.cs b/OpenTabletDriver/Tablet/DeviceVendor.cs similarity index 73% rename from OpenTabletDriver.Plugin/Tablet/DeviceVendor.cs rename to OpenTabletDriver/Tablet/DeviceVendor.cs index b5bb21988..3c8c22ed0 100644 --- a/OpenTabletDriver.Plugin/Tablet/DeviceVendor.cs +++ b/OpenTabletDriver/Tablet/DeviceVendor.cs @@ -1,8 +1,10 @@ using System; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Tablet +namespace OpenTabletDriver.Tablet { [Flags] + [PublicAPI] public enum DeviceVendor { Wacom = 0x056A, diff --git a/OpenTabletDriver.Plugin/Tablet/DigitizerSpecifications.cs b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs similarity index 71% rename from OpenTabletDriver.Plugin/Tablet/DigitizerSpecifications.cs rename to OpenTabletDriver/Tablet/DigitizerSpecifications.cs index 08e849cda..7683919dc 100644 --- a/OpenTabletDriver.Plugin/Tablet/DigitizerSpecifications.cs +++ b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs @@ -1,5 +1,12 @@ -namespace OpenTabletDriver.Plugin.Tablet +using System.ComponentModel; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + /// + /// Specifications for a digitizer. + /// + [PublicAPI] public class DigitizerSpecifications { /// @@ -15,11 +22,13 @@ public class DigitizerSpecifications /// /// The maximum X coordinate for the digitizer. /// + [DisplayName("Max X")] public float MaxX { set; get; } /// /// The maximum Y coordinate for the digitizer. /// + [DisplayName("Max Y")] public float MaxY { set; get; } } } diff --git a/OpenTabletDriver/Tablet/IAbsolutePositionReport.cs b/OpenTabletDriver/Tablet/IAbsolutePositionReport.cs new file mode 100644 index 000000000..5b4148d77 --- /dev/null +++ b/OpenTabletDriver/Tablet/IAbsolutePositionReport.cs @@ -0,0 +1,15 @@ +using System.Numerics; + +namespace OpenTabletDriver.Tablet +{ + /// + /// An absolute positioned device report. + /// + public interface IAbsolutePositionReport : IDeviceReport + { + /// + /// The absolute position of the pen. + /// + Vector2 Position { get; set; } + } +} diff --git a/OpenTabletDriver/Tablet/IAreaConverter.cs b/OpenTabletDriver/Tablet/IAreaConverter.cs new file mode 100644 index 000000000..bca546d2a --- /dev/null +++ b/OpenTabletDriver/Tablet/IAreaConverter.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// An area conversion utility, from manufacturer driver area to an . + /// \ + [PublicAPI] + public interface IAreaConverter + { + /// + /// The vendor in which this converter is designated. + /// + DeviceVendor Vendor { get; } + + /// + /// The label for the top argument. + /// + string Top { get; } + + /// + /// The label for the left argument. + /// + string Left { get; } + + /// + /// The label for the bottom argument. + /// + string Bottom { get; } + + /// + /// The label for the right argument. + /// + string Right { get; } + + /// + /// Converts a manufacturer driver area to an OpenTabletDriver area. + /// + /// The tablet in which the area is designated to. + /// The dimension defined by the label. + /// The dimension defined by the label. + /// The dimension defined by the label. + /// The dimension defined by the label. + /// An converted from manufacturer specifications. + AngledArea Convert(InputDevice tablet, double top, double left, double bottom, double right); + } +} diff --git a/OpenTabletDriver/Tablet/IAuxReport.cs b/OpenTabletDriver/Tablet/IAuxReport.cs new file mode 100644 index 000000000..d8fa9b3b7 --- /dev/null +++ b/OpenTabletDriver/Tablet/IAuxReport.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// An auxiliary report containing states of express keys. + /// + [PublicAPI] + public interface IAuxReport : IDeviceReport + { + /// + /// The states of all express keys. + /// + bool[] AuxButtons { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IDeviceReport.cs b/OpenTabletDriver/Tablet/IDeviceReport.cs new file mode 100644 index 000000000..25b3f0804 --- /dev/null +++ b/OpenTabletDriver/Tablet/IDeviceReport.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report. + /// + [PublicAPI] + public interface IDeviceReport + { + /// + /// The raw data forming the device report. + /// + byte[] Raw { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IEraserReport.cs b/OpenTabletDriver/Tablet/IEraserReport.cs new file mode 100644 index 000000000..14ed86950 --- /dev/null +++ b/OpenTabletDriver/Tablet/IEraserReport.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// An report designating the pen eraser state. + /// + [PublicAPI] + public interface IEraserReport : IDeviceReport + { + /// + /// Whether the eraser is the pen side in range. + /// + bool Eraser { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IMouseReport.cs b/OpenTabletDriver/Tablet/IMouseReport.cs new file mode 100644 index 000000000..97df8b7b3 --- /dev/null +++ b/OpenTabletDriver/Tablet/IMouseReport.cs @@ -0,0 +1,22 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A report containing information for a absolute positioned mouse. + /// + [PublicAPI] + public interface IMouseReport : IAbsolutePositionReport + { + /// + /// The current mouse buttons states. + /// + bool[] MouseButtons { set; get; } + + /// + /// The current scroll delta. + /// + Vector2 Scroll { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IProximityReport.cs b/OpenTabletDriver/Tablet/IProximityReport.cs new file mode 100644 index 000000000..2ab8fc0a8 --- /dev/null +++ b/OpenTabletDriver/Tablet/IProximityReport.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A report containing information for pen proximity. + /// + [PublicAPI] + public interface IProximityReport : IDeviceReport + { + /// + /// Whether the pen is in proximity. + /// + bool NearProximity { set; get; } + + /// + /// The hover distance of the pen. This is an arbitrary value with no defined real-world units. + /// + uint HoverDistance { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IReportParser.cs b/OpenTabletDriver/Tablet/IReportParser.cs new file mode 100644 index 000000000..9114f20e0 --- /dev/null +++ b/OpenTabletDriver/Tablet/IReportParser.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report parser. + /// + /// + /// The parsed report type. + /// + [PublicAPI] + public interface IReportParser where T : IDeviceReport + { + /// + /// Parses a device report from raw data. + /// + /// The report data to parse. + /// A parsed report of . + T Parse(byte[] report); + } +} diff --git a/OpenTabletDriver/Tablet/ITabletReport.cs b/OpenTabletDriver/Tablet/ITabletReport.cs new file mode 100644 index 000000000..eae60bda2 --- /dev/null +++ b/OpenTabletDriver/Tablet/ITabletReport.cs @@ -0,0 +1,24 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report containing information for the pen. + /// + [PublicAPI] + public interface ITabletReport : IAbsolutePositionReport + { + /// + /// The current pressure level in device reported units. + /// + /// + /// This is converted by to get the pressure percentage. + /// + uint Pressure { set; get; } + + /// + /// The current states of the pen buttons. + /// + bool[] PenButtons { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/ITiltReport.cs b/OpenTabletDriver/Tablet/ITiltReport.cs new file mode 100644 index 000000000..a122115ae --- /dev/null +++ b/OpenTabletDriver/Tablet/ITiltReport.cs @@ -0,0 +1,17 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report containing a tilt vector for the pen. + /// + [PublicAPI] + public interface ITiltReport : IDeviceReport + { + /// + /// The current tilt angle of the pen. This is an arbitrary value with no defined real-world units. + /// + Vector2 Tilt { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/IToolReport.cs b/OpenTabletDriver/Tablet/IToolReport.cs new file mode 100644 index 000000000..5db6a2698 --- /dev/null +++ b/OpenTabletDriver/Tablet/IToolReport.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report containing information about the current tool. + /// + [PublicAPI] + public interface IToolReport : IDeviceReport + { + /// + /// A serial number for the currently active tool. + /// + ulong Serial { set; get; } + + /// + /// The currently active tool identifier, typically used like a model number. + /// + uint RawToolID { set; get; } + + /// + /// The currently active tool type. + /// + ToolType Tool { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/OutOfRangeReport.cs b/OpenTabletDriver/Tablet/OutOfRangeReport.cs new file mode 100644 index 000000000..7d88f15ce --- /dev/null +++ b/OpenTabletDriver/Tablet/OutOfRangeReport.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// A device report designating that no useful data is parsed. + /// Typically designates that no buttons are pressed and no tool is in range. + /// + [PublicAPI] + public struct OutOfRangeReport : IDeviceReport + { + public OutOfRangeReport(byte[] report) + { + Raw = report; + } + + public byte[] Raw { set; get; } + } +} diff --git a/OpenTabletDriver.Plugin/Tablet/PassthroughReportParser.cs b/OpenTabletDriver/Tablet/PassthroughReportParser.cs similarity index 51% rename from OpenTabletDriver.Plugin/Tablet/PassthroughReportParser.cs rename to OpenTabletDriver/Tablet/PassthroughReportParser.cs index da9ae7295..7e53a80a3 100644 --- a/OpenTabletDriver.Plugin/Tablet/PassthroughReportParser.cs +++ b/OpenTabletDriver/Tablet/PassthroughReportParser.cs @@ -1,5 +1,11 @@ -namespace OpenTabletDriver.Plugin.Tablet +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + /// + /// A report parser that parses a basic . + /// + [PublicAPI] public class PassthroughReportParser : IReportParser { public virtual IDeviceReport Parse(byte[] data) diff --git a/OpenTabletDriver/Tablet/PenSpecifications.cs b/OpenTabletDriver/Tablet/PenSpecifications.cs new file mode 100644 index 000000000..33979f548 --- /dev/null +++ b/OpenTabletDriver/Tablet/PenSpecifications.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// Device specifications for a pen tool. + /// + [PublicAPI] + public class PenSpecifications : ButtonSpecifications + { + /// + /// The maximum pressure that the pen supports. + /// + [DisplayName("Max Pressure")] + public uint MaxPressure { set; get; } + } +} diff --git a/OpenTabletDriver.Plugin/Tablet/TabletConfiguration.cs b/OpenTabletDriver/Tablet/TabletConfiguration.cs similarity index 67% rename from OpenTabletDriver.Plugin/Tablet/TabletConfiguration.cs rename to OpenTabletDriver/Tablet/TabletConfiguration.cs index 7c699bdd5..917c8a6cf 100644 --- a/OpenTabletDriver.Plugin/Tablet/TabletConfiguration.cs +++ b/OpenTabletDriver/Tablet/TabletConfiguration.cs @@ -1,9 +1,13 @@ using System.Collections.Generic; +using System.ComponentModel; +using JetBrains.Annotations; -#nullable enable - -namespace OpenTabletDriver.Plugin.Tablet +namespace OpenTabletDriver.Tablet { + /// + /// A tablet device configuration. Provides everything needed to detect and configure a device. + /// + [PublicAPI] public class TabletConfiguration { /// @@ -19,12 +23,14 @@ public class TabletConfiguration /// /// The digitizer device identifier. /// + [DisplayName("Digitizer Identifiers")] public List DigitizerIdentifiers { set; get; } = new List(); /// /// The auxiliary device identifier. /// - public List AuxilaryDeviceIdentifiers { set; get; } = new List(); + [DisplayName("Auxiliary Identifiers")] + public List AuxiliaryDeviceIdentifiers { set; get; } = new List(); /// /// Other information about the tablet that can be used in tools or other applications. diff --git a/OpenTabletDriver.Plugin/Tablet/TabletReport.cs b/OpenTabletDriver/Tablet/TabletReport.cs similarity index 90% rename from OpenTabletDriver.Plugin/Tablet/TabletReport.cs rename to OpenTabletDriver/Tablet/TabletReport.cs index 7d1dcd77c..65d7892a8 100644 --- a/OpenTabletDriver.Plugin/Tablet/TabletReport.cs +++ b/OpenTabletDriver/Tablet/TabletReport.cs @@ -1,8 +1,10 @@ using System.Numerics; using System.Runtime.CompilerServices; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Tablet +namespace OpenTabletDriver.Tablet { + [PublicAPI] public struct TabletReport : ITabletReport { public TabletReport(byte[] report) diff --git a/OpenTabletDriver.Plugin/Tablet/TabletReportParser.cs b/OpenTabletDriver/Tablet/TabletReportParser.cs similarity index 50% rename from OpenTabletDriver.Plugin/Tablet/TabletReportParser.cs rename to OpenTabletDriver/Tablet/TabletReportParser.cs index ee9e11672..47c8cb2b6 100644 --- a/OpenTabletDriver.Plugin/Tablet/TabletReportParser.cs +++ b/OpenTabletDriver/Tablet/TabletReportParser.cs @@ -1,5 +1,11 @@ -namespace OpenTabletDriver.Plugin.Tablet +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet { + /// + /// A report parser that returns a generic . + /// + [PublicAPI] public class TabletReportParser : IReportParser { public virtual IDeviceReport Parse(byte[] data) diff --git a/OpenTabletDriver/Tablet/TabletSpecifications.cs b/OpenTabletDriver/Tablet/TabletSpecifications.cs new file mode 100644 index 000000000..020f240ce --- /dev/null +++ b/OpenTabletDriver/Tablet/TabletSpecifications.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// Device specifications for a tablet device. + /// + [PublicAPI] + public class TabletSpecifications + { + /// + /// Specifications for the tablet digitizer. + /// + public DigitizerSpecifications? Digitizer { set; get; } + + /// + /// Specifications for the tablet's pen. + /// + public PenSpecifications? Pen { set; get; } + + /// + /// Specifications for the auxiliary buttons. + /// + [DisplayName("Auxiliary")] + public ButtonSpecifications? AuxiliaryButtons { set; get; } + + /// + /// Specifications for the mouse buttons. + /// + [DisplayName("Mouse")] + public ButtonSpecifications? MouseButtons { set; get; } + + /// + /// Specifications for the touch digitizer. + /// + public DigitizerSpecifications? Touch { set; get; } + } +} diff --git a/OpenTabletDriver.Plugin/Tablet/TiltTabletReport.cs b/OpenTabletDriver/Tablet/TiltTabletReport.cs similarity index 86% rename from OpenTabletDriver.Plugin/Tablet/TiltTabletReport.cs rename to OpenTabletDriver/Tablet/TiltTabletReport.cs index c52518552..4264ebc77 100644 --- a/OpenTabletDriver.Plugin/Tablet/TiltTabletReport.cs +++ b/OpenTabletDriver/Tablet/TiltTabletReport.cs @@ -1,8 +1,13 @@ using System.Numerics; using System.Runtime.CompilerServices; +using JetBrains.Annotations; -namespace OpenTabletDriver.Plugin.Tablet +namespace OpenTabletDriver.Tablet { + /// + /// A pen tilt-supported tablet device report. + /// + [PublicAPI] public struct TiltTabletReport : ITabletReport, ITiltReport { public TiltTabletReport(byte[] report) diff --git a/OpenTabletDriver/Tablet/ToolType.cs b/OpenTabletDriver/Tablet/ToolType.cs new file mode 100644 index 000000000..2db1ed544 --- /dev/null +++ b/OpenTabletDriver/Tablet/ToolType.cs @@ -0,0 +1,14 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet +{ + /// + /// The tablet device tool type. + /// + [PublicAPI] + public enum ToolType + { + Pen, + Eraser + } +} diff --git a/OpenTabletDriver/Tablet/Touch/ITouchReport.cs b/OpenTabletDriver/Tablet/Touch/ITouchReport.cs new file mode 100644 index 000000000..a7f463cc8 --- /dev/null +++ b/OpenTabletDriver/Tablet/Touch/ITouchReport.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet.Touch +{ + /// + /// A device report containing touch input. + /// + [PublicAPI] + public interface ITouchReport : IDeviceReport + { + /// + /// The absolutely positioned touch points. + /// + TouchPoint?[] Touches { set; get; } + } +} diff --git a/OpenTabletDriver/Tablet/Touch/TouchPoint.cs b/OpenTabletDriver/Tablet/Touch/TouchPoint.cs new file mode 100644 index 000000000..c49c94556 --- /dev/null +++ b/OpenTabletDriver/Tablet/Touch/TouchPoint.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using JetBrains.Annotations; + +namespace OpenTabletDriver.Tablet.Touch +{ + /// + /// A touched point from an . + /// + [PublicAPI] + public struct TouchPoint + { + /// + /// The identifier of the touch point. + /// + public byte TouchID { init; get; } + + /// + /// The absolute position in which the touch point was sourced. + /// + public Vector2 Position { init; get; } + + public override string ToString() + { + return $"Point #{TouchID}: {Position};"; + } + } +}