diff --git a/README.md b/README.md
index d7acb0f..d3daa46 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,156 @@
+
+[![All Releases](https://img.shields.io/github/downloads/Devocub/TabletDriver/total.svg?style=for-the-badge&logo=appveyor)](https://github.com/Devocub/TabletDriver/releases)
+[![All Releases](https://img.shields.io/github/downloads/Devocub/TabletDriver/latest/total.svg?style=for-the-badge&logo=appveyor)](https://github.com/Devocub/TabletDriver/releases/latest)
+![Alt text](https://img.shields.io/github/downloads/Devocub/TabletDriver/v0.1.5.5/TabletDriverV0.1.5.5.Devocub.Edition.zip)
+
+This is a my modified version of hawku's tablet driver.
+Intended for Wacom tablets but will work with other tablets but not expect everything to work correct.
+Comparison with original, added:
+* Antichatter
+* Prediction
+* Mouse Wheel
+
+## Download
+### https://github.com/Devocub/TabletDriver/releases
+### Installing
+1) If you have already Driver installed just: **close it, and unzip new version with replacing files**.
+Your config will be preserved. Same for updating.
+
+If you have not installed the driver then:
+follow instructions here: https://github.com/Devocub/TabletDriver#installation and after return to step 1.
+My version based on 0.1.5 version of driver.
+
+_____
+ # PRESETS
+
+### Smooth 2 + prediction = accurate with small overshot
+[Plot link](http://yotx.ru/#!1/3_8hTp/4/0@A9YW1PuH@xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x@ZD4TT9g/2tw72jRjC/9r@xh70YHd9Ywe8ubUL3oJu7R/sk2jYjZ1TxuPpFuNx6/Jid39rf2v79@@fv7Gxt3kAQZztgrcgO7vgLcgWdAtysX@wT6JhN0AHjMcd0BbjEXSwu7@1DwQ=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/Smooth%202%20%2B%20prediction%20%3D%20accurate%20with%20small%20overshot.png)
+Video:
+[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/aZWoWwSmlEw/0.jpg)](https://www.youtube.com/watch?v=aZWoWwSmlEw)
+Video - same but with prediction disabled:
+[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/vimw1kihegk/0.jpg)](https://www.youtube.com/watch?v=vimw1kihegk)
+
+____________________
+### Straight - Pretty good realtime accurate
+[Plot link](http://yotx.ru/#!1/3_8hTp/4/0@A9YW1PuH@xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x@ZD4TT9g/2tw72jRjC/9r@xh70AILYXd/YOgBvbu2CLw6gW/sH@yQadmPnlPF4usV43Lq82N3f2t/a/v37529s7G3uQA52wQcQ2C74AAKCHkDO9g/2STTsBuiA8bgD2mI8gg5297f2AQc=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/Straight%20-%20Pretty%20good%20realtime%20accurate.png)
+Video:
+[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/SMrSGq8IvaU/0.jpg)](https://www.youtube.com/watch?v=SMrSGq8IvaU)
+
+____________________
+### Big latency and big prediction
+[Plot link](http://yotx.ru/#!1/3_8hTp/4/0@A9YW1PuH@xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x@ZD4TT9g/2tw72jRjC/9r@xh70YHd9Ywe8ubUL3oJu7R/sk2jYjZ1TxuPpFuNx6/Jid39rf2v79@@fv7Gxt7kFOdsFH0AudsEX0API6f7BPomG3QAdMB53QFuMR9DB7v7WPgI=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/Big%20latency%20and%20big%20prediction.png)
+Video:
+[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/k_KMV3Ujvjo/0.jpg)](https://www.youtube.com/watch?v=k_KMV3Ujvjo)
+
+____________________
+### Fun
+[Plot link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/CRwBpTQn8JQP/4I8bpDXlLuPRQyatwWlk/uOf8PFf8fdH5gPhtMcNLCG1trt/sH%402b8QQ/tf2t7Z///75Gxt7m1u74AMI4mwXfAHd2j/YJ9GwG1swBOPxgPF4sLu/tb%40xB93aXd%404AG9u7YK3DqBb%40wf7JBp2Y%40eU8Xi6xXjcurzY3d/aBwU=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/Fun.png)
+Video:
+[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/KthrzJrCrGc/0.jpg)](https://www.youtube.com/watch?v=KthrzJrCrGc)
+
+
+
+_____
+ # Antichatter
+
+Antichatter feature is meant to prevent cursor chattering/rattling/shaking/trembling when it's almost doesn't moves and/or too high to prevent tablet noise.
+Antichatter in it's primary form is useful for tablets which doesn't have any smoothing (including hardware smoothing).
+Antichatter requires smoothing filter enabled for work. Latency and Rate values do affect on antichatter settings.
+
+Formula for smoothing is:
+y(x) = (x + OffsetX)^(Strength\*-1)\*Multiplier+OffsetY
+Where **x** is pen speed. And **y(x)** is value on which smoothing will be increased. Slower speed - more smoothing. Faster speed - less smoothing.
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/formula_example.png)
+[Link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u74BAm9u7YK3Dg6gW/sH%40yQadmPnlPF4usV43Lq82N3f2gcF)
+
+Strength, Multiplier, OffsetX and OffsetY is values which you can change in driver.
+
+**Strength**: is strength, useful values are from 1 up to 10. Higher values make smoothing more sharper, lower are smoother.
+
+**Multiplier**: zoomIns and zoomOuts the [plot](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u74BAm9u7YK3Dg6gW/sH%40yQadmPnlPF4usV43Lq82N3f2gcF). Useful values are from 1 up to 1000. Makes smoothing softer. Default value is 1, means nothing changed.
+
+**Offset X**: Moves the [plot](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u74BAm9u7YK3Dg6gW/sH%40yQadmPnlPF4usV43Lq82N3f2gcF) to the right. Negative values moves the [plot](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u74BAm9u7YK3Dg6gW/sH%40yQadmPnlPF4usV43Lq82N3f2gcF) to the left. Higher values make smoothing weaker, lower values stornger and activate stronger smoothing earlier (in terms of cursor speed). Useful values are from -1 to 2. Default values is 0.
+
+**Offset Y**: Moves the [plot](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u74BAm9u7YK3Dg6gW/sH%40yQadmPnlPF4usV43Lq82N3f2gcF) up. Useful values are from roughly -1 up to 10. Look at the plot, if strength of smoothing is near 0 then it provides almost raw data with lowest delay. If value is near 1 then it's usual smoothing. Also it defines minimal amount of smoothing. OffsetY 10 will make smoothing x10 (and latency). OffsetY 0.5 will make smoothing roughly twice weaker (and latency will be roughly twice less), 0.3 roughly triple weaker, etc. Default value is 1.
+
+
+## Presets
+**Simple**: Latency 5-50 ms, Strength 2-3, Multiplier 1, OffsetX 0, OffsetY 1.
+[Interactive link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh70YHd94wK8ubUL3oJu7R/sk2jYjZ1TxuPpFuNx6/Jid39rHwM=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/simple.png)
+_____
+**Smooth**: Latency ~10 ms, Strength 3, Multiplier 100, OffsetX 1.5, OffsetY 1.
+Change OffsetX between 0-2 to switch between stickyness and smooth.
+Increase Strength to 4-10 to get more sharp. Decrease Strength to 1-2 to get more smoothing.
+[Interactive link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/Cx3/F3x%40ZD4TT9g/2tw72jRjC/9r%40xh50C3K2u75xAd7c2gVvHRxAt/YP9kk07MbOKePxdIvxuHV5sbu/tQ8E)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/smooth.png)
+_____
+**Straight**: Latency 20-40ms, Strength 20, Multiplier 1, OffsetX 0.7, OffsetY 0.6. This preset aren't good for high hovering. Because of OffsetY < 1 actual latency is less.
+[Interactive link](http://yotx.ru/#!1/3_h/sH%401sH%400YM4X9t/2j/YH/rYN%40IIfyv7W/sQQ8giN31jZ0D8ObWLngLegA53T/YJ9GwGzunjMfTLcbj1uXF7v7WPgI=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/straight.png)
+_____
+
+**Low latency**: Set Offset Y to 0 (and could be useful to set Latency to 1-10 ms but with some settings it can break smoothing, usually OffsetY 0 is enough) to being able to go to lowest latency.
+
+_____
+
+# Prediction
+How it works: It adds a predicted point to smoothing algoritm. It helps to preserve sharpness of movement, help with small movements.
+Low values (~<10-15ms) of smoothing latency can cause problems for cursor movement. It's very preffered to use at least 10-15ms of smoothing latency, 20-40 ms is even better and recommended. In sum cursor can even outdistance real position (similar to Wacom 6.3.9w5 drivers).
+
+Formula for prediction is:
+y(x) = 1/cosh((x-OffsetX)\*Sharpness)\*Strength+OffsetY
+Where **x** is pen speed. And **y(x)** is strength of prediction.
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/prediction_formula_example.png)
+[Link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/CRwBpTQn8JQP/4PsH%401sH%400YM4X9tf2v79%40%40fv7Gxt7kFudgFb0G2dsEXkAPoAeRg/2CfRMNubMEQjMcDxuPB7v7WPgI=)
+
+Strength, Sharpness, OffsetX and OffsetY is values which you can change in driver.
+
+**Strength**: is max of peak of prediction. Useful values are from 0 to 2, or up to 3-4 depends of latency.
+
+**Sharpness**: changes how wide will be Strength.
+
+**OffsetX**: center of peak of prediction. Useful values are from 0.5 up to 5-7. Increase value to shift cursor speedup to bigger movements.
+
+**OffsetY**: Moves the plot up/down (positiove/negative values). Also defines minimal amount of prediction.
+
+### Presets:
+**Simple+**:
+Staright or Smooth preset of smoothing +
+Strength 1-3 (for 5-50 ms respectively), Sharpness 1, OffsetX 0.8, OffsetY 0
+[Interactive link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/CRwBpTQn8JQP/4PsH%402f7Rgzhf21/Y2v79%40%40fv7Gxt3kAge2CtyAHu%40AdyBn0AHKwu3%40wT6JhN7ZgCMbjAePxYHd/ax8D)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/prediction_simplesmooth.png)
+_____
+**Straight+**:
+Staright preset of smoothing +
+Strength 0.3, Sharpness 0.7, OffsetX 2, OffsetY 0.3
+[Interactive link](http://yotx.ru/#!1/3_8hTp/4/0@A9YW1PuH@xvHewbMYT/tX0PmbQGp5H5j3/CRwBpTQn8JQP/4PsH@2f7Rgzhf21/Y2v79@@fv7Gxt7kDOdgFH0AQu@ADyAX0AHKxu3@wT6JhN7ZgCMbjAePxYHd/ax8D)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/prediction_straight.png)
+_____
+**Fun**:
+Smoothing: Latency 40ms, Strength 3, Multiplier 10, OffsetX 1, OffsetY 1 +
+Prediction: Strength 4, Sharpness 0.75, OffsetX 2.5, OffsetY 1
+[Interactive link](http://yotx.ru/#!1/3_8hTp/4/0%40A9YW1PuH%40xvHewbMYT/tX0PmbQGp5H5j3/CRwBpTQn8JQP/4I8bpDXlLuPRQyatwWlk/uOf8PFf8fdH5gPhtMcNLCG1trt/sH%402b8QQ/tf2t7Z///75Gxt7m1u74AMI4mwXfAHd2j/YJ9GwG1swBOPxgPF4sLu/tb%40xB93aXd%404AG9u7YK3DqBb%40wf7JBp2Y%40eU8Xi6xXjcurzY3d/aBwU=)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/prediction_fun.png)
+
+
+______________________
+______________________
+
+
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/3.png)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/4.png)
+![Alt text](https://raw.githubusercontent.com/Devocub/TabletDriver/master/images/7.png)
+
+## Star History
+![Star History Chart](https://api.star-history.com/svg?repos=Devocub/TabletDriver&type=Date)
+
+
+
# TabletDriver
This is a low latency graphics tablet driver that is meant to be used with rhythm game [osu!](https://osu.ppy.sh/home)
@@ -12,7 +165,7 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
## Download
-### http://hwk.fi/TabletDriver/TabletDriverV0.1.0.zip
+### http://hwk.fi/TabletDriver/TabletDriverV0.1.5.zip
#
@@ -26,27 +179,34 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
### Supported tablets:
- Wacom CTL-470
+ - Wacom CTH-470
- Wacom CTL-471
- Wacom CTL-472
- Wacom CTL-480
- Wacom CTH-480
- Wacom CTL-490
- - XP Pen G430
- - XP Pen G640
- - Huion 420
- - Huion H640P
- - Gaomon S56K
-
-### Configured, but not properly tested:
- - Huion H420
- - Wacom CTH-470
- - Wacom CTH-670
+ - Wacom CTH-490
- Wacom CTL-671
- Wacom CTL-672
- Wacom CTL-680
- Wacom CTH-680
- - Wacom CTH-490
- Wacom PTH-451
+ - Wacom PTH-850
+ - XP-Pen G430 (New 2017+ "Model B")
+ - XP-Pen G540 Pro
+ - XP-Pen G640
+ - Huion 420
+ - Huion H420
+ - Huion H430P
+ - Huion H640P
+ - Gaomon S56K
+
+### Configured, but not properly tested:
+ - Huion osu!tablet
+ - XP-Pen Deco 01
+ - Wacom CTL-4100 USB
+ - Wacom CTL-4100 Bluetooth
+ - https://github.com/hawku/TabletDriver/blob/master/TabletDriverService/config/wacom.cfg
#
@@ -57,16 +217,18 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
* https://aka.ms/vs/15/release/vc_redist.x86.exe
2. Unzip the driver to a folder (Shorter path is recommended, for example `C:\Temp\TabletDriver`)
-3. Uninstall all other tablet drivers.
+3. Uninstall all other tablet drivers. If you have problems with uninstalling the Wacom drivers, check the GitHub issue [#1](https://github.com/hawku/TabletDriver/issues/1)
4. Run `install_vmulti_driver.bat`. It might need a restart if there is another vmulti driver installed.
5. If you have Huion or Gaomon tablet, you need to run `install_huion_64.bat`, which is in the `driver_huion` directory.
6. Start the TabletDriverGUI.exe
## Updating to a new version
+
1. Unzip the new version
2. Start the TabletDriverGUI.exe
## Uninstallation
+
1. Uncheck the "Run at Windows startup" option in the GUI.
2. Run `remove_vmulti_driver.bat`
3. Run `remove_huion_64.bat`, which is in the `driver_huion` directory.
@@ -86,6 +248,38 @@ If you want to compile the code and don't want to install anything from the Tabl
#
## Changelog
+>**v0.1.5:**
+> - New tablet configurations: Wacom CTL-4100 (USB only model), XP-Pen G540 Pro, XP-Pen Deco 01 and Huion osu!tablet
+> Thanks to /u/THEqrunt for capturing the XP-Pen Deco 01 USB data.
+> - Added `ResetDistance` command, it controls the relative mode position reset distance.
+> - Code refactoring.
+
+>**v0.1.4:**
+> - Modified the Wacom CTL-471 full area size (147.20 x 92.25 mm to 152 x 95 mm)
+> - New tablet configurations: Wacom PTH-850 and Huion H430P
+> The PTH-850 configuration is made by [mojobojo](https://github.com/mojobojo)
+> - Regenerated the wacom.cfg with new parameters, so it now includes PTH-450/650/850 and PTK-450/650
+
+>**v0.1.3:**
+> - Added left handed mode / tablet invert option.
+> - Added Wacom driver device support for the CTL-471 and 472
+> - Noise reduction filter improvement (`Noise` command)
+
+>**v0.1.2:**
+> - Added experimental support for leaving the Wacom drivers installed on the system.
+> Supported tablets: CTL-470, CTL-480, CTH-480, CTL-4100
+> - Added `disable_wacom_drivers.bat` and `enable_wacom_drivers.bat` to the `tools` folder.
+> These scripts are used to disable and enable Wacom drivers when using the experimental Wacom driver support.
+> - Added driver restart button.
+
+>**v0.1.1:**
+> - Added support for Wacom CTL-4100 (USB and Bluetooth)
+> - Added settings import / export to the main menu.
+> - Added Wacom backup reader to the Wacom area tool.
+> - Added tablet benchmark tools to the console output context menu (Right click).
+> - Moved the `config.xml` to the `config` folder.
+> - Added noise reduction filter (`Noise` command, not in the GUI)
+> - Code refactoring
>**v0.1.0:**
> - Added `Bench` / `Benchmark` command.
@@ -139,7 +333,7 @@ If you want to compile the code and don't want to install anything from the Tabl
> - New tablet configurations: Wacom CTH-470, CTH-670, PTH-451
> - Fix for the smoothing filter. The filter didn't turn on when the settings were applied.
> - Fix for the Huion H640P clicking problem and also added better data validation for Huion 420,
-> Gaomon S56K, XP Pen G430 and G640.
+> Gaomon S56K, XP-Pen G430 and G640.
> - Modified click detection on CTL-490 and CTH-490 (tablet.cfg ClickPressure).
>**v0.0.9:**
diff --git a/TabletDriver.sln b/TabletDriver.sln
index 0b37878..54020ab 100644
--- a/TabletDriver.sln
+++ b/TabletDriver.sln
@@ -22,8 +22,8 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|Any CPU.ActiveCfg = Release|Any CPU
+ {6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|Any CPU.Build.0 = Release|Any CPU
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|x64.ActiveCfg = Debug|Any CPU
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|x64.Build.0 = Debug|Any CPU
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -34,7 +34,8 @@ Global
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Release|x64.Build.0 = Release|Any CPU
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Release|x86.ActiveCfg = Release|Any CPU
{6793EBBD-0EC4-4254-9573-A19ECA79F2C8}.Release|x86.Build.0 = Release|Any CPU
- {3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|Any CPU.ActiveCfg = Release|Win32
+ {3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|Any CPU.Build.0 = Release|Win32
{3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|x64.ActiveCfg = Debug|x64
{3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|x64.Build.0 = Debug|x64
{3101CEC2-8F39-45FD-943B-79A488AD05EA}.Debug|x86.ActiveCfg = Debug|Win32
diff --git a/TabletDriverGUI/App.xaml.cs b/TabletDriverGUI/App.xaml.cs
index 9a061a2..1072268 100644
--- a/TabletDriverGUI/App.xaml.cs
+++ b/TabletDriverGUI/App.xaml.cs
@@ -49,6 +49,43 @@ public App()
}
}
+ //
+ // Check Wacom processes
+ //
+ string[] wacomProcessNames =
+ {
+ "Pen_Tablet",
+ "Wacom_Tablet"
+ };
+
+ processes = Process.GetProcesses();
+ foreach (Process process in processes)
+ {
+ foreach (string wacomProcessName in wacomProcessNames)
+ {
+ if (process.ProcessName.ToLower() == wacomProcessName.ToLower())
+ {
+ try
+ {
+ process.Kill();
+ }
+ catch (Exception)
+ {
+ MessageBox.Show(
+ "You have Wacom driver processes running in the background:\n " +
+ string.Join("\n ", wacomProcessNames) +
+ "\n\nPlease shutdown those before starting the GUI!",
+ "Error!", MessageBoxButton.OK, MessageBoxImage.Error
+ );
+ instanceMutex.ReleaseMutex();
+ Shutdown();
+ return;
+ }
+ }
+ }
+ }
+
+
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
Exit += App_Exit;
diff --git a/TabletDriverGUI/Configuration.cs b/TabletDriverGUI/Configuration.cs
index 9f91ed9..0cc9bc5 100644
--- a/TabletDriverGUI/Configuration.cs
+++ b/TabletDriverGUI/Configuration.cs
@@ -15,6 +15,7 @@ public class Configuration
public Area TabletFullArea;
public bool ForceAspectRatio;
public double Rotation;
+ public bool Invert;
public bool ForceFullArea;
public OutputModes OutputMode;
public enum OutputModes
@@ -26,9 +27,21 @@ public enum OutputModes
public Area ScreenArea;
- public double FilterLatency;
- public int FilterInterval;
- public bool FilterEnabled;
+ public double SmoothingLatency;
+ public int SmoothingInterval;
+ public bool SmoothingEnabled;
+
+ public bool AntichatterEnabled;
+ public double AntichatterStrength;
+ public double AntichatterMultiplier;
+ public double AntichatterOffsetX;
+ public double AntichatterOffsetY;
+
+ public bool PredictionEnabled;
+ public double PredictionSharpness;
+ public double PredictionStrength;
+ public double PredictionOffsetX;
+ public double PredictionOffsetY;
public Area DesktopSize;
public bool AutomaticDesktopSize;
@@ -37,6 +50,7 @@ public enum OutputModes
[XmlArrayItem("Button")]
public int[] ButtonMap;
public bool DisableButtons;
+ public int mouseWheelSpeed;
[XmlArray("CommandsAfter")]
[XmlArrayItem("Command")]
@@ -52,7 +66,7 @@ public enum OutputModes
public bool RunAtStartup;
public string DriverPath;
- public string DriverArguments;
+ public string DriverArguments;
public bool DeveloperMode;
@@ -77,21 +91,35 @@ public Configuration()
ButtonMap = new int[] { 1, 2, 3 };
DisableButtons = false;
+ mouseWheelSpeed = 50;
+
+ SmoothingEnabled = false;
+ SmoothingLatency = 0;
+ SmoothingInterval = 4;
- FilterEnabled = false;
- FilterLatency = 0;
- FilterInterval = 4;
+ AntichatterEnabled = true;
+ AntichatterStrength = 3.0;
+ AntichatterMultiplier = 1.0;
+ AntichatterOffsetX = 0.0;
+ AntichatterOffsetY = 1.0;
+
+ PredictionEnabled = true;
+ PredictionSharpness = 1.0;
+ PredictionStrength = 1.1;
+ PredictionOffsetX = 3.0;
+ PredictionOffsetY = 0.3;
CommandsAfter = new string[] { "" };
CommandsBefore = new string[] { "" };
WindowWidth = 800;
- WindowHeight = 690;
+ WindowHeight = 710;
RunAtStartup = false;
DriverPath = "bin/TabletDriverService.exe";
DriverArguments = "config/init.cfg";
+
DeveloperMode = false;
}
diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml
index 4bc8fae..6a8e036 100644
--- a/TabletDriverGUI/MainWindow.xaml
+++ b/TabletDriverGUI/MainWindow.xaml
@@ -5,35 +5,48 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TabletDriverGUI"
mc:Ignorable="d"
- Title="TabletDriverGUI" Height="670" Width="800"
+ Title="TabletDriverGUI" Height="786" Width="686"
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Screen Map
-
-
+
+
- You can drag the screen area with a mouse.
- Drag + Control = Move area in X direction.
@@ -41,13 +54,14 @@
- Drag + Shift = Move area Y direction.
- Use the "Set Area" to set the screen area to single monitor or full desktop.
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+ 1920
-
-
-
-
-
-
-
- px
+
+
+
+
+
+
+ 1080
-
-
-
-
-
-
-
- px
+
+
+
+
+
+
+ 0
-
-
-
-
-
-
-
- px
+
+
+
+
+
+
+ 0
-
-
-
+
+
+
-
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
Tablet Area
-
-
+
+
- You can drag the tablet area with a mouse.
- Drag + Control = Move area in X direction.
- Drag + Shift = Move area Y direction.
- - For left handed mode, use rotation value of 180 degrees.
+ - For left handed mode, enable the "Left handed (Inverted)" checkbox or use a rotation value of 180 degrees.
- Click Wacom Area button to type in the Wacom driver area settings.
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+ Left handed
+
+ (Inverted)
+
+
+
+
+
+
+ 0
-
-
-
+
+
+
+
-
-
-
-
+
- Full Area
-
-
-
-
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+ 80
-
-
-
-
-
-
-
- mm
+
+
+
+
+
+
+ 45
-
-
-
-
-
-
-
- mm
+
+
+
+
+
+
+ 0
-
-
-
-
-
-
-
- mm
+
+
+
+
+
+
+ 0
-
-
-
+
+
+
-
-
+
-
- Force
- Aspect Ratio
+
+ Force
+ Aspect Ratio
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
Button Mapping
-
-
- - You can disable single button by selecting "Disable" from the list.
+
+
+ - You can disable a single button by selecting "Disable" from the list.
- The "Disable buttons" checkbox will disable all tablet buttons.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ 50
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Disable buttons
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ Cursor position smoothing filter
+
+
+ - Smoothing filter adds latency to the input, so don't enable it if you want to have the lowest possible input lag.
+
+ - On Wacom tablets you can use latency value between 15 and 25 to have a similar smoothing as in the Wacom drivers.
+
+ - You can test out different filter values, but recommended maximum for osu! is around 50 milliseconds.
+
+ - Filter latency value lower than 4 milliseconds isn't recommended. It is just better to disable the smoothing filter.
+
+ - You don't have to change the filter rate, but you can use the highest rate your computer can run without performance problems.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+
+
+
+
+
+
+ xy(x)
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+ Strength
+
+
+ Multiplier
+
+
+ Offset X
+
+
+
+ Offset Y
+
+
+
+
+
+ How to setup
+ Simple
+
+ Straight
+
+
+ Smooth
+
+
+
+ Low latency
+
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+
+
+
+
+
+
+ xy(x)
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+ Strength
+
+
+ Sharpness
+
+
+ Offset X
+
+
+ Offset Y
+
+
+
+ Presets
+ Simple+
+
+
+
+
+ Straight+
+
+
+
+
+ Fun
+
+
+
+ Please, visit https://github.com/Devocub/TabletDriver for images
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- Cursor position smoothing filter
-
-
- - Smoothing filter adds latency to the input, so don't enable it if you want to have the lowest possible input lag.
-
- - On Wacom tablets you can use latency value between 15 and 25 to have a similar smoothing as in the Wacom drivers.
-
- - You can test out different filter values, but recommended maximum for osu! is around 50 milliseconds.
-
- - Filter latency value lower than 4 milliseconds isn't recommended. It is just better to disable the smoothing filter.
-
- - You don't have to change the filter rate, but you can use the highest rate your computer can run without performance problems.
-
-
-
-
-
-
-
-
-
- 0
-
-
-
-
-
-
-
-
-
- 500 Hz
- 333 Hz
- 250 Hz
- 125 Hz
-
-
-
-
-
- Enabled
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
Desktop Size
-
-
+
+
- In some cases you may have to disable the auto size and manually type in the main monitor resolution.
- If you don't have problems with screen mapping, leave the automatic desktop size enabled.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ 1920
-
-
-
-
-
-
-
- px
+
+
+
+
+
+
+ 1080
-
-
-
+
+
+
- Auto
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
These commands are sent when the driver is started and the settings haven't yet been applied.
-
-
-
+
+
+
-
-
+
-
-
+
+
These commands are sent when the driver is started and settings have been applied to the driver.
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+ Restart Driver
+
+
+ Run at Windows startup
-
-
-
-
-
-
-
-
-
- 0.0.0
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ 0.0.0
+
+
+
+
+
diff --git a/TabletDriverGUI/MainWindow.xaml.cs b/TabletDriverGUI/MainWindow.xaml.cs
index 0fe8ee7..845106c 100644
--- a/TabletDriverGUI/MainWindow.xaml.cs
+++ b/TabletDriverGUI/MainWindow.xaml.cs
@@ -24,7 +24,7 @@ public partial class MainWindow : Window
{
// Version
- public string Version = "0.1.0";
+ public string Version = "0.1.5.6 Devocub Edition";
// Console stuff
private List commandHistory;
@@ -45,6 +45,7 @@ public partial class MainWindow : Window
// Config
private Configuration config;
+ private string configFilename;
private bool isFirstStart = false;
private bool isLoadingSettings;
@@ -60,9 +61,6 @@ public partial class MainWindow : Window
private Polygon polygonTabletAreaArrow;
private TextBlock textTabletAspectRatio;
- // Culture
- CultureInfo cultureEnglish;
-
// Mouse drag
private class MouseDrag
{
@@ -99,10 +97,6 @@ public MainWindow()
// Set culture to en-US to force decimal format and etc.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
- cultureEnglish = new CultureInfo("en-US");
- cultureEnglish.NumberFormat.PerMilleSymbol = "";
- cultureEnglish.NumberFormat.NumberGroupSeparator = "";
- cultureEnglish.NumberFormat.PercentDecimalSeparator = ".";
// Create notify icon
@@ -130,7 +124,6 @@ public MainWindow()
commandHistory = new List { "" };
commandHistoryIndex = 0;
-
// Init tablet driver
driver = new TabletDriver("TabletDriverService.exe");
driver.MessageReceived += OnDriverMessageReceived;
@@ -181,20 +174,22 @@ public MainWindow()
comboBoxButton2.Items.Add("Mouse " + i);
comboBoxButton3.Items.Add("Mouse " + i);
}
+ comboBoxButton2.Items.Add("Mouse Wheel");
+ comboBoxButton3.Items.Add("Mouse Wheel");
comboBoxButton1.SelectedIndex = 0;
comboBoxButton2.SelectedIndex = 0;
comboBoxButton3.SelectedIndex = 0;
//
- // Filter rate ComboBox
+ // Smoothing rate ComboBox
//
- comboBoxFilterRate.Items.Clear();
- for (int i = 2; i <= 8; i++)
+ comboBoxSmoothingRate.Items.Clear();
+ for (int i = 1; i <= 8; i++)
{
- comboBoxFilterRate.Items.Add((1000.0 / i).ToString("0") + " Hz");
+ comboBoxSmoothingRate.Items.Add((1000.0 / i).ToString("0") + " Hz");
}
- comboBoxFilterRate.SelectedIndex = 2;
+ comboBoxSmoothingRate.SelectedIndex = 1;
// Process command line arguments
ProcessCommandLineArguments();
@@ -208,6 +203,8 @@ public MainWindow()
isLoadingSettings = false;
}
+
+
#region Window events
// Window is closing -> Stop driver
@@ -216,7 +213,7 @@ private void MainWindow_Closing(object sender, System.ComponentModel.CancelEvent
notifyIcon.Visible = false;
try
{
- config.Write("Config.xml");
+ config.Write(configFilename);
}
catch (Exception)
{
@@ -227,10 +224,14 @@ private void MainWindow_Closing(object sender, System.ComponentModel.CancelEvent
// Window loaded -> Start driver
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
+
+ // Configuration filename
+ configFilename = "config/config.xml";
+
// Load configuration
try
{
- config = Configuration.CreateFromFile("Config.xml");
+ config = Configuration.CreateFromFile(configFilename);
}
catch (Exception)
{
@@ -269,8 +270,6 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e)
// Update the settings back to the configuration
UpdateSettingsToConfiguration();
- // Console timer
- timerConsoleUpdate.Start();
// Set run at startup
SetRunAtStartup(config.RunAtStartup);
@@ -374,10 +373,10 @@ private void LoadSettingsFromConfiguration()
//
// Tablet area
//
- textTabletAreaWidth.Text = GetNumberString(config.TabletArea.Width);
- textTabletAreaHeight.Text = GetNumberString(config.TabletArea.Height);
- textTabletAreaX.Text = GetNumberString(config.TabletArea.X);
- textTabletAreaY.Text = GetNumberString(config.TabletArea.Y);
+ textTabletAreaWidth.Text = Utils.GetNumberString(config.TabletArea.Width);
+ textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height);
+ textTabletAreaX.Text = Utils.GetNumberString(config.TabletArea.X);
+ textTabletAreaY.Text = Utils.GetNumberString(config.TabletArea.Y);
checkBoxForceAspect.IsChecked = config.ForceAspectRatio;
checkBoxForceFullArea.IsChecked = config.ForceFullArea;
switch (config.OutputMode)
@@ -392,9 +391,8 @@ private void LoadSettingsFromConfiguration()
radioModeDigitizer.IsChecked = true;
break;
}
-
- textTabletAreaRotation.Text = GetNumberString(config.TabletArea.Rotation);
-
+ textTabletAreaRotation.Text = Utils.GetNumberString(config.TabletArea.Rotation);
+ checkBoxInvert.IsChecked = config.Invert;
//
@@ -419,10 +417,10 @@ private void LoadSettingsFromConfiguration()
//
// Screen area
//
- textScreenAreaWidth.Text = GetNumberString(config.ScreenArea.Width, "0");
- textScreenAreaHeight.Text = GetNumberString(config.ScreenArea.Height, "0");
- textScreenAreaX.Text = GetNumberString(config.ScreenArea.X, "0");
- textScreenAreaY.Text = GetNumberString(config.ScreenArea.Y, "0");
+ textScreenAreaWidth.Text = Utils.GetNumberString(config.ScreenArea.Width, "0");
+ textScreenAreaHeight.Text = Utils.GetNumberString(config.ScreenArea.Height, "0");
+ textScreenAreaX.Text = Utils.GetNumberString(config.ScreenArea.X, "0");
+ textScreenAreaY.Text = Utils.GetNumberString(config.ScreenArea.Y, "0");
//
@@ -430,8 +428,8 @@ private void LoadSettingsFromConfiguration()
//
if (config.AutomaticDesktopSize)
{
- textDesktopWidth.Text = GetNumberString(GetVirtualDesktopSize().Width);
- textDesktopHeight.Text = GetNumberString(GetVirtualDesktopSize().Height);
+ textDesktopWidth.Text = Utils.GetNumberString(GetVirtualDesktopSize().Width);
+ textDesktopHeight.Text = Utils.GetNumberString(GetVirtualDesktopSize().Height);
config.DesktopSize.Width = GetVirtualDesktopSize().Width;
config.DesktopSize.Height = GetVirtualDesktopSize().Height;
textDesktopWidth.IsEnabled = false;
@@ -439,8 +437,8 @@ private void LoadSettingsFromConfiguration()
}
else
{
- textDesktopWidth.Text = GetNumberString(config.DesktopSize.Width);
- textDesktopHeight.Text = GetNumberString(config.DesktopSize.Height);
+ textDesktopWidth.Text = Utils.GetNumberString(config.DesktopSize.Width);
+ textDesktopHeight.Text = Utils.GetNumberString(config.DesktopSize.Height);
}
checkBoxAutomaticDesktopSize.IsChecked = config.AutomaticDesktopSize;
@@ -449,7 +447,7 @@ private void LoadSettingsFromConfiguration()
if (config.ForceAspectRatio)
{
config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height);
- textTabletAreaHeight.Text = GetNumberString(config.TabletArea.Height);
+ textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height);
textTabletAreaHeight.IsEnabled = false;
}
@@ -458,8 +456,8 @@ private void LoadSettingsFromConfiguration()
// Move tablet area to a valid position
//
config.TabletArea.MoveInside(config.TabletFullArea);
- textTabletAreaX.Text = GetNumberString(config.TabletArea.X);
- textTabletAreaY.Text = GetNumberString(config.TabletArea.Y);
+ textTabletAreaX.Text = Utils.GetNumberString(config.TabletArea.X);
+ textTabletAreaY.Text = Utils.GetNumberString(config.TabletArea.Y);
//
@@ -467,6 +465,7 @@ private void LoadSettingsFromConfiguration()
//
if (config.ButtonMap.Count() == 3)
{
+ textMouseWheelSpeed.Text = Utils.GetNumberString(config.mouseWheelSpeed);
comboBoxButton1.SelectedIndex = config.ButtonMap[0];
comboBoxButton2.SelectedIndex = config.ButtonMap[1];
comboBoxButton3.SelectedIndex = config.ButtonMap[2];
@@ -479,24 +478,83 @@ private void LoadSettingsFromConfiguration()
//
- // Filter
+ // Smoothing filter
//
- checkBoxFilter.IsChecked = config.FilterEnabled;
- textFilterLatency.Text = GetNumberString(config.FilterLatency);
- comboBoxFilterRate.SelectedIndex = config.FilterInterval - 2;
- if (config.FilterEnabled)
- {
- textFilterLatency.IsEnabled = true;
- comboBoxFilterRate.IsEnabled = true;
+ checkBoxSmoothing.IsChecked = config.SmoothingEnabled;
+ textSmoothingLatency.Text = Utils.GetNumberString(config.SmoothingLatency);
+ comboBoxSmoothingRate.SelectedIndex = config.SmoothingInterval - 1;
+
+ checkBoxAntichatter.IsChecked = config.AntichatterEnabled;
+ textAntichatterStrength.Text = Utils.GetNumberString(config.AntichatterStrength);
+ textAntichatterMultiplier.Text = Utils.GetNumberString(config.AntichatterMultiplier);
+ textAntichatterOffsetX.Text = Utils.GetNumberString(config.AntichatterOffsetX);
+ textAntichatterOffsetY.Text = Utils.GetNumberString(config.AntichatterOffsetY);
+
+ checkBoxPrediction.IsChecked = config.PredictionEnabled;
+ textPredictionStrength.Text = Utils.GetNumberString(config.PredictionStrength);
+ textPredictionSharpness.Text = Utils.GetNumberString(config.PredictionSharpness);
+ textPredictionOffsetX.Text = Utils.GetNumberString(config.PredictionOffsetX);
+ textPredictionOffsetY.Text = Utils.GetNumberString(config.PredictionOffsetY);
+
+ if (config.SmoothingEnabled)
+ {
+ textSmoothingLatency.IsEnabled = true;
+ comboBoxSmoothingRate.IsEnabled = true;
+ checkBoxAntichatter.IsEnabled = true;
+ checkBoxPrediction.IsEnabled = true;
+
+ if (config.AntichatterEnabled)
+ {
+ textAntichatterStrength.IsEnabled = true;
+ textAntichatterMultiplier.IsEnabled = true;
+ textAntichatterOffsetX.IsEnabled = true;
+ textAntichatterOffsetY.IsEnabled = true;
+ }
+ else
+ {
+ textAntichatterStrength.IsEnabled = false;
+ textAntichatterMultiplier.IsEnabled = false;
+ textAntichatterOffsetX.IsEnabled = false;
+ textAntichatterOffsetY.IsEnabled = false;
+ }
+
+ if (config.PredictionEnabled)
+ {
+ textPredictionStrength.IsEnabled = true;
+ textPredictionSharpness.IsEnabled = true;
+ textPredictionOffsetX.IsEnabled = true;
+ textPredictionOffsetY.IsEnabled = true;
+ }
+ else
+ {
+ textPredictionStrength.IsEnabled = false;
+ textPredictionSharpness.IsEnabled = false;
+ textPredictionOffsetX.IsEnabled = false;
+ textPredictionOffsetY.IsEnabled = false;
+ }
}
else
{
- textFilterLatency.IsEnabled = false;
- comboBoxFilterRate.IsEnabled = false;
- }
+ textSmoothingLatency.IsEnabled = false;
+ comboBoxSmoothingRate.IsEnabled = false;
+
+ checkBoxAntichatter.IsEnabled = false;
+ textAntichatterStrength.IsEnabled = false;
+ textAntichatterMultiplier.IsEnabled = false;
+ textAntichatterOffsetX.IsEnabled = false;
+ textAntichatterOffsetY.IsEnabled = false;
+
+ checkBoxPrediction.IsEnabled = false;
+ textPredictionStrength.IsEnabled = false;
+ textPredictionSharpness.IsEnabled = false;
+ textPredictionOffsetX.IsEnabled = false;
+ textPredictionOffsetY.IsEnabled = false;
+ }
+ //
// Run at startup
+ //
checkRunAtStartup.IsChecked = config.RunAtStartup;
@@ -519,8 +577,11 @@ private void LoadSettingsFromConfiguration()
}
textCommandsAfter.Text = tmp;
+
+ // Update canvases
UpdateCanvasElements();
+
isLoadingSettings = false;
}
@@ -535,20 +596,22 @@ private void UpdateSettingsToConfiguration()
bool oldValue;
// Tablet area
- if (ParseNumber(textTabletAreaWidth.Text, out double value))
- config.TabletArea.Width = value;
- if (ParseNumber(textTabletAreaHeight.Text, out value))
- config.TabletArea.Height = value;
- if (ParseNumber(textTabletAreaX.Text, out value))
- config.TabletArea.X = value;
- if (ParseNumber(textTabletAreaY.Text, out value))
- config.TabletArea.Y = value;
- if (ParseNumber(textTabletAreaRotation.Text, out value))
- config.TabletArea.Rotation = value;
-
+ if (Utils.ParseNumber(textTabletAreaWidth.Text, out double val))
+ config.TabletArea.Width = val;
+ if (Utils.ParseNumber(textTabletAreaHeight.Text, out val))
+ config.TabletArea.Height = val;
+ if (Utils.ParseNumber(textTabletAreaX.Text, out val))
+ config.TabletArea.X = val;
+ if (Utils.ParseNumber(textTabletAreaY.Text, out val))
+ config.TabletArea.Y = val;
+ if (Utils.ParseNumber(textTabletAreaRotation.Text, out val))
+ config.TabletArea.Rotation = val;
+
+ config.Invert = (bool)checkBoxInvert.IsChecked;
config.ForceAspectRatio = (bool)checkBoxForceAspect.IsChecked;
config.ForceFullArea = (bool)checkBoxForceFullArea.IsChecked;
+ // Output Mode
if (radioModeAbsolute.IsChecked == true) config.OutputMode = Configuration.OutputModes.Absolute;
if (radioModeRelative.IsChecked == true) config.OutputMode = Configuration.OutputModes.Relative;
if (radioModeDigitizer.IsChecked == true) config.OutputMode = Configuration.OutputModes.Digitizer;
@@ -568,37 +631,35 @@ private void UpdateSettingsToConfiguration()
// Fit area to full area
config.TabletArea.ScaleInside(config.TabletFullArea);
- textTabletAreaWidth.Text = GetNumberString(config.TabletArea.Width);
- textTabletAreaHeight.Text = GetNumberString(config.TabletArea.Height);
+ textTabletAreaWidth.Text = Utils.GetNumberString(config.TabletArea.Width);
+ textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height);
- // Center
- //config.TabletArea.X = config.TabletFullArea.Width / 2.0;
- //config.TabletArea.Y = config.TabletFullArea.Height / 2.0;
}
+ // Force the tablet area to be inside of the full area
config.TabletArea.MoveInside(config.TabletFullArea);
// Screen area
- if (ParseNumber(textScreenAreaWidth.Text, out value))
- config.ScreenArea.Width = value;
- if (ParseNumber(textScreenAreaHeight.Text, out value))
- config.ScreenArea.Height = value;
- if (ParseNumber(textScreenAreaX.Text, out value))
- config.ScreenArea.X = value;
- if (ParseNumber(textScreenAreaY.Text, out value))
- config.ScreenArea.Y = value;
+ if (Utils.ParseNumber(textScreenAreaWidth.Text, out val))
+ config.ScreenArea.Width = val;
+ if (Utils.ParseNumber(textScreenAreaHeight.Text, out val))
+ config.ScreenArea.Height = val;
+ if (Utils.ParseNumber(textScreenAreaX.Text, out val))
+ config.ScreenArea.X = val;
+ if (Utils.ParseNumber(textScreenAreaY.Text, out val))
+ config.ScreenArea.Y = val;
// Desktop size
- if (ParseNumber(textDesktopWidth.Text, out value))
- config.DesktopSize.Width = value;
- if (ParseNumber(textDesktopHeight.Text, out value))
- config.DesktopSize.Height = value;
+ if (Utils.ParseNumber(textDesktopWidth.Text, out val))
+ config.DesktopSize.Width = val;
+ if (Utils.ParseNumber(textDesktopHeight.Text, out val))
+ config.DesktopSize.Height = val;
config.AutomaticDesktopSize = (bool)checkBoxAutomaticDesktopSize.IsChecked;
if (config.AutomaticDesktopSize == true)
{
- textDesktopWidth.Text = GetNumberString(GetVirtualDesktopSize().Width);
- textDesktopHeight.Text = GetNumberString(GetVirtualDesktopSize().Height);
+ textDesktopWidth.Text = Utils.GetNumberString(GetVirtualDesktopSize().Width);
+ textDesktopHeight.Text = Utils.GetNumberString(GetVirtualDesktopSize().Height);
config.DesktopSize.Width = GetVirtualDesktopSize().Width;
config.DesktopSize.Height = GetVirtualDesktopSize().Height;
}
@@ -608,11 +669,13 @@ private void UpdateSettingsToConfiguration()
if (config.ForceAspectRatio)
{
config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height);
- textTabletAreaHeight.Text = GetNumberString(config.TabletArea.Height);
+ textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height);
}
// Button map
+ if (Utils.ParseNumber(textMouseWheelSpeed.Text, out val))
+ config.mouseWheelSpeed = (int)val;
config.ButtonMap[0] = comboBoxButton1.SelectedIndex;
config.ButtonMap[1] = comboBoxButton2.SelectedIndex;
config.ButtonMap[2] = comboBoxButton3.SelectedIndex;
@@ -621,20 +684,86 @@ private void UpdateSettingsToConfiguration()
// Filter
- config.FilterEnabled = (bool)checkBoxFilter.IsChecked;
- config.FilterInterval = comboBoxFilterRate.SelectedIndex + 2;
- if (ParseNumber(textFilterLatency.Text, out value))
- config.FilterLatency = value;
+ config.SmoothingEnabled = (bool)checkBoxSmoothing.IsChecked;
+ config.SmoothingInterval = comboBoxSmoothingRate.SelectedIndex + 1;
+ if (Utils.ParseNumber(textSmoothingLatency.Text, out val))
+ config.SmoothingLatency = val;
+
+ config.AntichatterEnabled = (bool)checkBoxAntichatter.IsChecked;
+ double value;
+ if (Utils.ParseNumber(textAntichatterStrength.Text, out value))
+ config.AntichatterStrength = value;
+ if (Utils.ParseNumber(textAntichatterMultiplier.Text, out value))
+ config.AntichatterMultiplier = value;
+ if (Utils.ParseNumber(textAntichatterOffsetX.Text, out value))
+ config.AntichatterOffsetX = value;
+ if (Utils.ParseNumber(textAntichatterOffsetY.Text, out value))
+ config.AntichatterOffsetY = value;
+
+ config.PredictionEnabled = (bool)checkBoxPrediction.IsChecked;
+ if (Utils.ParseNumber(textPredictionStrength.Text, out value))
+ config.PredictionStrength = value;
+ if (Utils.ParseNumber(textPredictionSharpness.Text, out value))
+ config.PredictionSharpness = value;
+ if (Utils.ParseNumber(textPredictionOffsetX.Text, out value))
+ config.PredictionOffsetX = value;
+ if (Utils.ParseNumber(textPredictionOffsetY.Text, out value))
+ config.PredictionOffsetY = value;
+
+ if (config.SmoothingEnabled)
+ {
+ textSmoothingLatency.IsEnabled = true;
+ comboBoxSmoothingRate.IsEnabled = true;
+ checkBoxAntichatter.IsEnabled = true;
+ checkBoxPrediction.IsEnabled = true;
+
+ if (config.AntichatterEnabled)
+ {
+ textAntichatterStrength.IsEnabled = true;
+ textAntichatterMultiplier.IsEnabled = true;
+ textAntichatterOffsetX.IsEnabled = true;
+ textAntichatterOffsetY.IsEnabled = true;
+ }
+ else
+ {
+ textAntichatterStrength.IsEnabled = false;
+ textAntichatterMultiplier.IsEnabled = false;
+ textAntichatterOffsetX.IsEnabled = false;
+ textAntichatterOffsetY.IsEnabled = false;
+ }
- if (config.FilterEnabled)
- {
- textFilterLatency.IsEnabled = true;
- comboBoxFilterRate.IsEnabled = true;
+ if (config.PredictionEnabled)
+ {
+ textPredictionStrength.IsEnabled = true;
+ textPredictionSharpness.IsEnabled = true;
+ textPredictionOffsetX.IsEnabled = true;
+ textPredictionOffsetY.IsEnabled = true;
+ }
+ else
+ {
+ textPredictionStrength.IsEnabled = false;
+ textPredictionSharpness.IsEnabled = false;
+ textPredictionOffsetX.IsEnabled = false;
+ textPredictionOffsetY.IsEnabled = false;
+ }
}
else
{
- textFilterLatency.IsEnabled = false;
- comboBoxFilterRate.IsEnabled = false;
+ textSmoothingLatency.IsEnabled = false;
+ comboBoxSmoothingRate.IsEnabled = false;
+
+ checkBoxAntichatter.IsEnabled = false;
+ textAntichatterStrength.IsEnabled = false;
+ textAntichatterMultiplier.IsEnabled = false;
+ textAntichatterOffsetX.IsEnabled = false;
+ textAntichatterOffsetY.IsEnabled = false;
+
+ checkBoxPrediction.IsEnabled = false;
+ textPredictionStrength.IsEnabled = false;
+ textPredictionSharpness.IsEnabled = false;
+ textPredictionOffsetX.IsEnabled = false;
+ textPredictionOffsetY.IsEnabled = false;
+
}
//
@@ -665,33 +794,6 @@ private void UpdateSettingsToConfiguration()
}
- //
- // String to Number
- //
- private bool ParseNumber(string str, out double value)
- {
- value = 0;
- if (double.TryParse(str, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, cultureEnglish.NumberFormat, out double tmp))
- {
- value = tmp;
- return true;
- }
- return false;
- }
-
- //
- // Number to String
- //
- private string GetNumberString(double value)
- {
- return GetNumberString(value, "0.##");
- }
- private string GetNumberString(double value, string format)
- {
- return value.ToString(format, cultureEnglish.NumberFormat);
- }
-
-
//
// Set run at startup
//
@@ -939,7 +1041,7 @@ void UpdateScreenMapCanvas()
Canvas.SetTop(rectangleScreenMap, offsetY + config.ScreenArea.Y * scale);
// Screen aspect ratio text
- textScreenAspectRatio.Text = GetNumberString(config.ScreenArea.Width / config.ScreenArea.Height, "0.###") + ":1";
+ textScreenAspectRatio.Text = Utils.GetNumberString(config.ScreenArea.Width / config.ScreenArea.Height, "0.###") + ":1";
Canvas.SetLeft(textScreenAspectRatio, offsetX +
(config.ScreenArea.X + config.ScreenArea.Width / 2.0) * scale -
textScreenAspectRatio.ActualWidth / 2.0
@@ -1055,7 +1157,7 @@ void UpdateTabletAreaCanvas()
//
// Tablet area aspect ratio text
//
- textTabletAspectRatio.Text = GetNumberString(config.TabletArea.Width / config.TabletArea.Height, "0.###") + ":1";
+ textTabletAspectRatio.Text = Utils.GetNumberString(config.TabletArea.Width / config.TabletArea.Height, "0.###") + ":1";
Canvas.SetLeft(textTabletAspectRatio, offsetX + (config.TabletArea.X) * scale - textTabletAspectRatio.ActualWidth / 2.0);
Canvas.SetTop(textTabletAspectRatio, offsetY + (config.TabletArea.Y) * scale - textTabletAspectRatio.ActualHeight / 2.0);
@@ -1101,10 +1203,10 @@ private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
mouseDrag.IsMouseDown = false;
LoadSettingsFromConfiguration();
isLoadingSettings = true;
- textScreenAreaX.Text = GetNumberString(config.ScreenArea.X, "0");
- textScreenAreaY.Text = GetNumberString(config.ScreenArea.Y, "0");
- textTabletAreaX.Text = GetNumberString(config.TabletArea.X);
- textTabletAreaY.Text = GetNumberString(config.TabletArea.Y);
+ textScreenAreaX.Text = Utils.GetNumberString(config.ScreenArea.X, "0");
+ textScreenAreaY.Text = Utils.GetNumberString(config.ScreenArea.Y, "0");
+ textTabletAreaX.Text = Utils.GetNumberString(config.TabletArea.X);
+ textTabletAreaY.Text = Utils.GetNumberString(config.TabletArea.Y);
isLoadingSettings = false;
canvasScreenMap.ReleaseMouseCapture();
canvasTabletArea.ReleaseMouseCapture();
@@ -1216,8 +1318,8 @@ private void CheckboxChanged(object sender, RoutedEventArgs e)
// Disable desktop size settings when automatic is checked
if (checkBoxAutomaticDesktopSize.IsChecked == true)
{
- textDesktopWidth.Text = GetNumberString(GetVirtualDesktopSize().Width);
- textDesktopHeight.Text = GetNumberString(GetVirtualDesktopSize().Height);
+ textDesktopWidth.Text = Utils.GetNumberString(GetVirtualDesktopSize().Width);
+ textDesktopHeight.Text = Utils.GetNumberString(GetVirtualDesktopSize().Height);
textDesktopWidth.IsEnabled = false;
textDesktopHeight.IsEnabled = false;
}
@@ -1296,18 +1398,18 @@ private void ComboBoxMonitor_SelectionChanged(object sender, SelectionChangedEve
{
textScreenAreaX.Text = "0";
textScreenAreaY.Text = "0";
- textScreenAreaWidth.Text = GetNumberString(config.DesktopSize.Width);
- textScreenAreaHeight.Text = GetNumberString(config.DesktopSize.Height);
+ textScreenAreaWidth.Text = Utils.GetNumberString(config.DesktopSize.Width);
+ textScreenAreaHeight.Text = Utils.GetNumberString(config.DesktopSize.Height);
}
else if (index > 0)
{
index--;
if (index >= 0 && index < screens.Length)
{
- textScreenAreaX.Text = GetNumberString(screens[index].Bounds.X - minX);
- textScreenAreaY.Text = GetNumberString(screens[index].Bounds.Y - minY);
- textScreenAreaWidth.Text = GetNumberString(screens[index].Bounds.Width);
- textScreenAreaHeight.Text = GetNumberString(screens[index].Bounds.Height);
+ textScreenAreaX.Text = Utils.GetNumberString(screens[index].Bounds.X - minX);
+ textScreenAreaY.Text = Utils.GetNumberString(screens[index].Bounds.Y - minY);
+ textScreenAreaWidth.Text = Utils.GetNumberString(screens[index].Bounds.Width);
+ textScreenAreaHeight.Text = Utils.GetNumberString(screens[index].Bounds.Height);
}
}
UpdateSettingsToConfiguration();
@@ -1320,7 +1422,7 @@ private void SaveSettings(object sender, RoutedEventArgs e)
{
try
{
- config.Write("Config.xml");
+ config.Write(configFilename);
SendSettingsToDriver();
SetStatus("Settings saved!");
}
@@ -1343,6 +1445,74 @@ private void ApplySettings(object sender, RoutedEventArgs e)
SetStatus("Settings applied!");
}
+
+ //
+ // Main Menu Click
+ //
+ private void MainMenuClick(object sender, RoutedEventArgs e)
+ {
+
+ // Import
+ if (sender == mainMenuImport)
+ {
+ OpenFileDialog dialog = new OpenFileDialog
+ {
+ InitialDirectory = Directory.GetCurrentDirectory(),
+ Filter = "XML File|*.xml"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ try
+ {
+ Configuration tmpConfig = Configuration.CreateFromFile(dialog.FileName);
+ config = tmpConfig;
+ LoadSettingsFromConfiguration();
+ SetStatus("Settings imported!");
+ }
+ catch (Exception)
+ {
+ MessageBox.Show("Settings import failed!", "ERROR!",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+ }
+
+ // Export
+ else if (sender == mainMenuExport)
+ {
+ SaveFileDialog dialog = new SaveFileDialog
+ {
+ InitialDirectory = Directory.GetCurrentDirectory(),
+ AddExtension = true,
+ DefaultExt = "xml",
+ Filter = "XML File|*.xml"
+
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ try
+ {
+ UpdateSettingsToConfiguration();
+ config.Write(dialog.FileName);
+ SetStatus("Settings exported!");
+ }
+ catch (Exception)
+ {
+ MessageBox.Show("Settings export failed!", "ERROR!",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ }
+
+ // Exit
+ else if (sender == mainMenuExit)
+ {
+ Close();
+ }
+
+ }
+
#endregion
@@ -1505,8 +1675,18 @@ private void ParseDriverStatus(string line)
//
if (variableName == "tablet")
{
- Title = "TabletDriverGUI - " + stringValue;
- notifyIcon.Text = Title;
+ string title = "TabletDriverGUI - " + stringValue;
+ Title = title;
+
+ // Limit notify icon text length
+ if (title.Length > 63)
+ {
+ notifyIcon.Text = title.Substring(0, 63);
+ }
+ else
+ {
+ notifyIcon.Text = title;
+ }
SetStatus("Connected to " + stringValue);
}
@@ -1515,10 +1695,10 @@ private void ParseDriverStatus(string line)
//
if (variableName == "width")
{
- if (ParseNumber(stringValue, out double value))
+ if (Utils.ParseNumber(stringValue, out double val))
{
- config.TabletFullArea.Width = value;
- config.TabletFullArea.X = value / 2.0;
+ config.TabletFullArea.Width = val;
+ config.TabletFullArea.X = val / 2.0;
LoadSettingsFromConfiguration();
UpdateSettingsToConfiguration();
if (isFirstStart)
@@ -1531,10 +1711,10 @@ private void ParseDriverStatus(string line)
//
if (variableName == "height")
{
- if (ParseNumber(stringValue, out double value))
+ if (Utils.ParseNumber(stringValue, out double val))
{
- config.TabletFullArea.Height = value;
- config.TabletFullArea.Y = value / 2.0;
+ config.TabletFullArea.Height = val;
+ config.TabletFullArea.Y = val / 2.0;
LoadSettingsFromConfiguration();
UpdateSettingsToConfiguration();
if (isFirstStart)
@@ -1571,18 +1751,37 @@ private void SendSettingsToDriver()
// Screen area
driver.SendCommand("ScreenArea " +
- GetNumberString(config.ScreenArea.Width) + " " + GetNumberString(config.ScreenArea.Height) + " " +
- GetNumberString(config.ScreenArea.X) + " " + GetNumberString(config.ScreenArea.Y)
+ Utils.GetNumberString(config.ScreenArea.Width) + " " + Utils.GetNumberString(config.ScreenArea.Height) + " " +
+ Utils.GetNumberString(config.ScreenArea.X) + " " + Utils.GetNumberString(config.ScreenArea.Y)
);
+
+ //
// Tablet area
- driver.SendCommand("Area " +
- GetNumberString(config.TabletArea.Width) + " " + GetNumberString(config.TabletArea.Height) + " " +
- GetNumberString(config.TabletArea.X) + " " + GetNumberString(config.TabletArea.Y)
- );
+ //
+ // Inverted
+ if (config.Invert)
+ {
+ driver.SendCommand("TabletArea " +
+ Utils.GetNumberString(config.TabletArea.Width) + " " +
+ Utils.GetNumberString(config.TabletArea.Height) + " " +
+ Utils.GetNumberString(config.TabletFullArea.Width - config.TabletArea.X) + " " +
+ Utils.GetNumberString(config.TabletFullArea.Height - config.TabletArea.Y)
+ );
+ driver.SendCommand("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation + 180));
+ }
+ // Normal
+ else
+ {
+ driver.SendCommand("TabletArea " +
+ Utils.GetNumberString(config.TabletArea.Width) + " " +
+ Utils.GetNumberString(config.TabletArea.Height) + " " +
+ Utils.GetNumberString(config.TabletArea.X) + " " +
+ Utils.GetNumberString(config.TabletArea.Y)
+ );
+ driver.SendCommand("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation));
+ }
- // Rotation
- driver.SendCommand("Rotate " + GetNumberString(config.TabletArea.Rotation));
// Output Mode
switch (config.OutputMode)
@@ -1592,7 +1791,7 @@ private void SendSettingsToDriver()
break;
case Configuration.OutputModes.Relative:
driver.SendCommand("Mode Relative");
- driver.SendCommand("Sensitivity " + GetNumberString(config.ScreenArea.Width / config.TabletArea.Width));
+ driver.SendCommand("Sensitivity " + Utils.GetNumberString(config.ScreenArea.Width / config.TabletArea.Width));
break;
case Configuration.OutputModes.Digitizer:
driver.SendCommand("Mode Digitizer");
@@ -1610,15 +1809,40 @@ private void SendSettingsToDriver()
driver.SendCommand("ButtonMap " + String.Join(" ", config.ButtonMap));
}
- // Filter
- if (config.FilterEnabled)
+ // Mouse Wheel Speed
+ driver.SendCommand("MouseWheelSpeed " + Utils.GetNumberString(config.mouseWheelSpeed));
+
+ // Smoothing filter
+ if (config.SmoothingEnabled)
{
- driver.SendCommand("Filter " + GetNumberString(config.FilterLatency));
- driver.SendCommand("FilterInterval " + GetNumberString(config.FilterInterval));
+ driver.SendCommand("Smoothing " + Utils.GetNumberString(config.SmoothingLatency));
+ driver.SendCommand("SmoothingInterval " + Utils.GetNumberString(config.SmoothingInterval));
+
+ if (config.AntichatterEnabled)
+ {
+ driver.SendCommand("AntichatterEnabled 1");
+ driver.SendCommand("AntichatterStrength " + Utils.GetNumberString(config.AntichatterStrength));
+ driver.SendCommand("AntichatterMultiplier " + Utils.GetNumberString(config.AntichatterMultiplier));
+ driver.SendCommand("AntichatterOffsetX " + Utils.GetNumberString(config.AntichatterOffsetX));
+ driver.SendCommand("AntichatterOffsetY " + Utils.GetNumberString(config.AntichatterOffsetY));
+ }
+ else
+ driver.SendCommand("AntichatterEnabled 0");
+
+ if (config.PredictionEnabled)
+ {
+ driver.SendCommand("PredictionEnabled 1");
+ driver.SendCommand("PredictionStrength " + Utils.GetNumberString(config.PredictionStrength));
+ driver.SendCommand("PredictionSharpness " + Utils.GetNumberString(config.PredictionSharpness));
+ driver.SendCommand("PredictionOffsetX " + Utils.GetNumberString(config.PredictionOffsetX));
+ driver.SendCommand("PredictionOffsetY " + Utils.GetNumberString(config.PredictionOffsetY));
+ }
+ else
+ driver.SendCommand("PredictionEnabled 0");
}
else
{
- driver.SendCommand("Filter 0");
+ driver.SendCommand("Smoothing 0");
}
// Commands after settings
@@ -1646,6 +1870,10 @@ void Start()
try
{
running = true;
+
+ // Console timer
+ timerConsoleUpdate.Start();
+
driver.Start(config.DriverPath, config.DriverArguments);
if (!driver.IsRunning)
{
@@ -1674,6 +1902,18 @@ void Stop()
timerConsoleUpdate.Stop();
}
+ //
+ // Restart Driver button click
+ //
+ private void RestartDriverClick(object sender, RoutedEventArgs e)
+ {
+ if (running)
+ {
+ Stop();
+ }
+ Start();
+ }
+
#endregion
@@ -1902,6 +2142,42 @@ private void ConsoleMenuClick(object sender, RoutedEventArgs e)
}
}
+ // Run benchmark
+ else if (sender == menuRunBenchmark)
+ {
+ ConsoleSendCommand("Benchmark");
+ }
+
+ // Copy Benchmark
+ else if (sender == menuCopyBenchmark)
+ {
+ string clipboard = "";
+ List rows;
+ driver.ConsoleLock();
+ rows = SearchRows(driver.ConsoleBuffer, " [STATUS] BENCHMARK ", 0, 0);
+ driver.ConsoleUnlock();
+ foreach (string row in rows)
+ {
+ Match m = Regex.Match(row, " BENCHMARK ([0-9\\.]+) ([0-9\\.]+) ([0-9\\.]+) (.*)$");
+ if (m.Success)
+ {
+ string tabletName = m.Groups[4].ToString();
+ string totalPackets = m.Groups[1].ToString();
+ string noiseWidth = m.Groups[2].ToString();
+ string noiseHeight = m.Groups[3].ToString();
+ clipboard =
+ "Tablet(" + tabletName + ") " +
+ "Noise(" + noiseWidth + " mm x " + noiseHeight + " mm) " +
+ "Packets(" + totalPackets + ")\r\n";
+ }
+ }
+
+ if (clipboard.Length > 0)
+ {
+ Clipboard.SetText(clipboard);
+ SetStatus("Benchmark result copied to clipboard");
+ }
+ }
// Open startup log
else if (sender == menuOpenStartup)
@@ -1967,11 +2243,11 @@ private void ConsoleMenuClick(object sender, RoutedEventArgs e)
private void ButtonWacomArea_Click(object sender, RoutedEventArgs e)
{
WacomArea wacom = new WacomArea();
- wacom.textWacomLeft.Text = GetNumberString((config.TabletArea.X - config.TabletArea.Width / 2) * 100.0, "0");
- wacom.textWacomRight.Text = GetNumberString((config.TabletArea.X + config.TabletArea.Width / 2) * 100.0, "0");
+ wacom.textWacomLeft.Text = Utils.GetNumberString((config.TabletArea.X - config.TabletArea.Width / 2) * 100.0, "0");
+ wacom.textWacomRight.Text = Utils.GetNumberString((config.TabletArea.X + config.TabletArea.Width / 2) * 100.0, "0");
- wacom.textWacomTop.Text = GetNumberString((config.TabletArea.Y - config.TabletArea.Height / 2) * 100.0, "0");
- wacom.textWacomBottom.Text = GetNumberString((config.TabletArea.Y + config.TabletArea.Height / 2) * 100.0, "0");
+ wacom.textWacomTop.Text = Utils.GetNumberString((config.TabletArea.Y - config.TabletArea.Height / 2) * 100.0, "0");
+ wacom.textWacomBottom.Text = Utils.GetNumberString((config.TabletArea.Y + config.TabletArea.Height / 2) * 100.0, "0");
wacom.ShowDialog();
@@ -1979,10 +2255,10 @@ private void ButtonWacomArea_Click(object sender, RoutedEventArgs e)
if (wacom.DialogResult == true)
{
if (
- ParseNumber(wacom.textWacomLeft.Text, out double left) &&
- ParseNumber(wacom.textWacomRight.Text, out double right) &&
- ParseNumber(wacom.textWacomTop.Text, out double top) &&
- ParseNumber(wacom.textWacomBottom.Text, out double bottom)
+ Utils.ParseNumber(wacom.textWacomLeft.Text, out double left) &&
+ Utils.ParseNumber(wacom.textWacomRight.Text, out double right) &&
+ Utils.ParseNumber(wacom.textWacomTop.Text, out double top) &&
+ Utils.ParseNumber(wacom.textWacomBottom.Text, out double bottom)
)
{
double width, height;
@@ -2043,7 +2319,9 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b
return IntPtr.Zero;
}
+
#endregion
+
}
}
\ No newline at end of file
diff --git a/TabletDriverGUI/TabletDriver.cs b/TabletDriverGUI/TabletDriver.cs
index 11c0de7..979b23a 100644
--- a/TabletDriverGUI/TabletDriver.cs
+++ b/TabletDriverGUI/TabletDriver.cs
@@ -73,7 +73,7 @@ private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e)
}
processService.Refresh();
- switch(processService.PriorityClass)
+ switch (processService.PriorityClass)
{
case ProcessPriorityClass.High:
case ProcessPriorityClass.RealTime:
diff --git a/TabletDriverGUI/TabletDriverGUI.csproj b/TabletDriverGUI/TabletDriverGUI.csproj
index 3bb7241..ed30a36 100644
--- a/TabletDriverGUI/TabletDriverGUI.csproj
+++ b/TabletDriverGUI/TabletDriverGUI.csproj
@@ -77,6 +77,7 @@
MSBuild:Compile
Designer
+
diff --git a/TabletDriverGUI/Utils.cs b/TabletDriverGUI/Utils.cs
new file mode 100644
index 0000000..1481209
--- /dev/null
+++ b/TabletDriverGUI/Utils.cs
@@ -0,0 +1,60 @@
+using System.Globalization;
+
+namespace TabletDriverGUI
+{
+ public class Utils
+ {
+ public static CultureInfo cultureInfo = null;
+
+
+ //
+ // Check and create culture info
+ //
+ public static void CheckCultureInfo()
+ {
+ if (cultureInfo == null)
+ {
+ cultureInfo = new CultureInfo("en-US");
+ cultureInfo.NumberFormat.PerMilleSymbol = "";
+
+ cultureInfo.NumberFormat.NumberDecimalSeparator = ".";
+ cultureInfo.NumberFormat.NumberGroupSeparator = "";
+
+ cultureInfo.NumberFormat.PercentDecimalSeparator = ".";
+ cultureInfo.NumberFormat.PercentGroupSeparator = "";
+
+ cultureInfo.NumberFormat.CurrencyDecimalSeparator = ".";
+ cultureInfo.NumberFormat.CurrencyGroupSeparator = "";
+ }
+ }
+
+ //
+ // String to Number
+ //
+ public static bool ParseNumber(string str, out double val)
+ {
+ CheckCultureInfo();
+ val = 0;
+ if (double.TryParse(str, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, cultureInfo.NumberFormat, out double tmp))
+ {
+ val = tmp;
+ return true;
+ }
+ return false;
+ }
+
+ //
+ // Number to String
+ //
+ public static string GetNumberString(double val)
+ {
+ CheckCultureInfo();
+ return GetNumberString(val, "0.##");
+ }
+ public static string GetNumberString(double val, string format)
+ {
+ CheckCultureInfo();
+ return val.ToString(format, cultureInfo.NumberFormat);
+ }
+ }
+}
diff --git a/TabletDriverGUI/WacomArea.xaml b/TabletDriverGUI/WacomArea.xaml
index 3433270..7e8b267 100644
--- a/TabletDriverGUI/WacomArea.xaml
+++ b/TabletDriverGUI/WacomArea.xaml
@@ -45,6 +45,7 @@
+
diff --git a/TabletDriverGUI/WacomArea.xaml.cs b/TabletDriverGUI/WacomArea.xaml.cs
index 7be1c09..ee5be50 100644
--- a/TabletDriverGUI/WacomArea.xaml.cs
+++ b/TabletDriverGUI/WacomArea.xaml.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
+using Microsoft.Win32;
+using System;
+using System.IO;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Text.RegularExpressions;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
namespace TabletDriverGUI
{
@@ -34,5 +27,112 @@ private void ButtonCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
+
+ private void ButtonLoad_Click(object sender, RoutedEventArgs e)
+ {
+ OpenFileDialog dialog = new OpenFileDialog
+ {
+ InitialDirectory = Directory.GetCurrentDirectory(),
+ Filter = "Wacom Backup(*.wacomprefs;*.tabletprefs)|*.wacomprefs;*.tabletprefs"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ LoadFromBackup(dialog.FileName);
+ }
+ }
+
+ //
+ // Load setting from Wacom backup
+ //
+ private void LoadFromBackup(string filepath)
+ {
+ try
+ {
+ string data = File.ReadAllText(filepath);
+ data = data.Replace("<", "<");
+ data = data.Replace(">", ">");
+
+ double[] areaValues = new double[4] { 0, 0, 0, 0 };
+ double[] lastAreaValues = new double[4];
+ bool first = true;
+
+ /*
+
+
+ 15200
+ 9500
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ */
+
+ //
+ // Regular expression
+ //
+ MatchCollection matches = Regex.Matches(
+ data,
+ "]*>.*?" +
+ "([0-9]+).*?" +
+ "([0-9]+).*?" +
+ "([0-9]+).*?" +
+ "([0-9]+).*?" +
+ "",
+ RegexOptions.Singleline | RegexOptions.IgnoreCase
+ );
+
+ //
+ // Loop through regex matches
+ //
+ foreach (Match match in matches)
+ {
+ if (
+ Utils.ParseNumber(match.Groups[1].ToString(), out areaValues[0]) &&
+ Utils.ParseNumber(match.Groups[2].ToString(), out areaValues[1]) &&
+ Utils.ParseNumber(match.Groups[3].ToString(), out areaValues[2]) &&
+ Utils.ParseNumber(match.Groups[4].ToString(), out areaValues[3])
+ )
+ {
+ // Stop at first different area value set
+ if (!first && !areaValues.SequenceEqual(lastAreaValues))
+ break;
+
+ Array.Copy(areaValues, lastAreaValues, 4);
+ first = false;
+ }
+ }
+
+ // Set text fields
+ if (areaValues[0] != 0 && areaValues[1] != 0)
+ {
+ textWacomLeft.Text = Utils.GetNumberString(areaValues[2]);
+ textWacomRight.Text = Utils.GetNumberString(areaValues[2] + areaValues[0]);
+ textWacomTop.Text = Utils.GetNumberString(areaValues[3]);
+ textWacomBottom.Text = Utils.GetNumberString(areaValues[3] + areaValues[1]);
+ }
+
+ // Show error
+ else
+ {
+ MessageBox.Show("Couldn't read the backup file!", "ERROR!",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+
+ }
+
+ // Exception
+ catch (Exception)
+ {
+ MessageBox.Show("Couldn't read the backup file!", "ERROR!",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+
+ }
}
}
diff --git a/TabletDriverService/CommandLine.cpp b/TabletDriverService/CommandLine.cpp
index 376a9ab..1126e03 100644
--- a/TabletDriverService/CommandLine.cpp
+++ b/TabletDriverService/CommandLine.cpp
@@ -27,7 +27,7 @@ bool CommandLine::is(string command) {
transform(command.begin(), command.end(), command.begin(), ::tolower);
transform(match.begin(), match.end(), match.begin(), ::tolower);
- if(command.compare(match) == 0) {
+ if (command.compare(match) == 0) {
return true;
}
return false;
@@ -50,7 +50,7 @@ int CommandLine::Parse(string line) {
char currentChar;
char previousChar = 0;
char splitChars[] = " ,:(){}[]";
- char endChars[] = {'\r', '\n', ';', 0};
+ char endChars[] = { '\r', '\n', ';', 0 };
char commentChar = '#';
char escapeChar = '\\';
char enclosingChar = '"';
@@ -61,12 +61,12 @@ int CommandLine::Parse(string line) {
bool isLastChar = false;
bool isEnclosed = false;
- for(std::string::iterator it = line.begin(); it != line.end(); ++it) {
+ for (std::string::iterator it = line.begin(); it != line.end(); ++it) {
currentChar = *it;
// Comment char
- if(!isEnclosed && currentChar == commentChar) {
- if(itemLength > 0) {
+ if (!isEnclosed && currentChar == commentChar) {
+ if (itemLength > 0) {
items.push_back(item);
}
break;
@@ -74,8 +74,8 @@ int CommandLine::Parse(string line) {
// Is split char?
isSplitChar = false;
- for(int i = 0; i < (int)sizeof(splitChars); i++) {
- if(splitChars[i] && currentChar == splitChars[i]) {
+ for (int i = 0; i < (int)sizeof(splitChars); i++) {
+ if (splitChars[i] && currentChar == splitChars[i]) {
isSplitChar = true;
break;
}
@@ -83,8 +83,8 @@ int CommandLine::Parse(string line) {
// Is end char?
isEndChar = false;
- for(int i = 0; i < (int)sizeof(endChars); i++) {
- if(currentChar == endChars[i]) {
+ for (int i = 0; i < (int)sizeof(endChars); i++) {
+ if (currentChar == endChars[i]) {
isEndChar = true;
break;
}
@@ -92,20 +92,20 @@ int CommandLine::Parse(string line) {
// Is last char?
isLastChar = false;
- if(index == lineLength - 1) {
+ if (index == lineLength - 1) {
isLastChar = true;
}
// Toggle enclosing
isEnclosingChar = false;
- if(currentChar == enclosingChar && previousChar != escapeChar) {
+ if (currentChar == enclosingChar && previousChar != escapeChar) {
isEnclosed = !isEnclosed;
isEnclosingChar = true;
}
// New item
- if(
+ if (
!isEnclosed &&
(
isSplitChar ||
@@ -122,35 +122,40 @@ int CommandLine::Parse(string line) {
//INFO("itemLength = %d\n", itemLength);
// Last char
- if(isLastChar && !isEndChar && !isSplitChar) {
- if(!isEnclosingChar) {
+ if (isLastChar && !isEndChar && !isSplitChar) {
+ if (!isEnclosingChar) {
item.push_back(currentChar);
itemLength = 1;
}
}
// Create new item
- if(itemLength > 0) {
+ if (itemLength > 0) {
items.push_back(item);
item = "";
itemLength = 0;
itemCount++;
- } else {
+ }
+ else {
item = "";
itemLength = 0;
}
// Stop parsing at end of the line
- if(isEndChar) {
+ if (isEndChar) {
break;
}
// Add text to item
- } else if(currentChar >= 32) {
- if(itemCount == 0 && currentChar == '=') {
- } else if(isEnclosingChar) {
- } else if(currentChar == escapeChar && !isEnclosed) {
- } else {
+ }
+ else if (currentChar >= 32) {
+ if (itemCount == 0 && currentChar == '=') {
+ }
+ else if (isEnclosingChar) {
+ }
+ else if (currentChar == escapeChar && !isEnclosed) {
+ }
+ else {
item.push_back(currentChar);
itemLength++;
}
@@ -160,16 +165,17 @@ int CommandLine::Parse(string line) {
}
// Set command
- if(itemCount > 0) {
+ if (itemCount > 0) {
command = items[0];
isValid = true;
- } else {
+ }
+ else {
isValid = false;
}
// Set values
values.clear();
- for(int i = 1; i < (int)items.size(); i++) {
+ for (int i = 1; i < (int)items.size(); i++) {
values.push_back(items[i]);
}
@@ -182,11 +188,12 @@ int CommandLine::Parse(string line) {
// Parse hex string
//
string CommandLine::ParseHex(string str) {
- if(str.size() >= 3 && str[0] == '0' && str[1] == 'x') {
+ if (str.size() >= 3 && str[0] == '0' && str[1] == 'x') {
try {
string tmp = str.substr(2, str.size() - 2);
return to_string(stol(tmp, 0, 16));
- } catch(exception) {
+ }
+ catch (exception) {
}
}
return str;
@@ -197,7 +204,7 @@ string CommandLine::ParseHex(string str) {
// Get string value
//
string CommandLine::GetString(int index, string defaultValue) {
- if(index < valueCount) {
+ if (index < valueCount) {
return values[index];
}
return defaultValue;
@@ -218,11 +225,12 @@ string CommandLine::GetStringLower(int index, string defaultValue) {
// Get integer
//
int CommandLine::GetInt(int index, int defaultValue) {
- if(index < valueCount) {
+ if (index < valueCount) {
try {
auto value = stoi(ParseHex(values[index]));
return value;
- } catch(exception) {}
+ }
+ catch (exception) {}
}
return defaultValue;
}
@@ -232,11 +240,12 @@ int CommandLine::GetInt(int index, int defaultValue) {
// Get long integer
//
long CommandLine::GetLong(int index, long defaultValue) {
- if(index < valueCount) {
+ if (index < valueCount) {
try {
auto value = stol(ParseHex(values[index]));
return value;
- } catch(exception) {}
+ }
+ catch (exception) {}
}
return defaultValue;
}
@@ -246,11 +255,12 @@ long CommandLine::GetLong(int index, long defaultValue) {
// Get double precision floating point number
//
double CommandLine::GetDouble(int index, double defaultValue) {
- if(index < valueCount) {
+ if (index < valueCount) {
try {
auto value = stod(ParseHex(values[index]));
return value;
- } catch(exception) {}
+ }
+ catch (exception) {}
}
return defaultValue;
}
@@ -260,11 +270,12 @@ double CommandLine::GetDouble(int index, double defaultValue) {
// Get floating point number
//
float CommandLine::GetFloat(int index, float defaultValue) {
- if(index < valueCount) {
+ if (index < valueCount) {
try {
auto value = stof(ParseHex(values[index]));
return value;
- } catch(exception) {}
+ }
+ catch (exception) {}
}
return defaultValue;
}
@@ -274,12 +285,12 @@ float CommandLine::GetFloat(int index, float defaultValue) {
// Get boolean
//
bool CommandLine::GetBoolean(int index, bool defaultValue) {
- if(GetInt(index, 0) > 0) return true;
+ if (GetInt(index, 0) > 0) return true;
string str = GetStringLower(index, "");
- if(str == "true") return true;
- if(str == "on") return true;
- if(str == "false") return false;
- if(str == "off") return false;
- if(str == "0") return false;
+ if (str == "true") return true;
+ if (str == "on") return true;
+ if (str == "false") return false;
+ if (str == "off") return false;
+ if (str == "0") return false;
return defaultValue;
}
diff --git a/TabletDriverService/HIDDevice.cpp b/TabletDriverService/HIDDevice.cpp
index b77ee0b..c0ef1e7 100644
--- a/TabletDriverService/HIDDevice.cpp
+++ b/TabletDriverService/HIDDevice.cpp
@@ -9,7 +9,7 @@ HIDDevice::HIDDevice(USHORT VendorId, USHORT ProductId, USHORT UsagePage, USHORT
this->productId = ProductId;
this->usagePage = UsagePage;
this->usage = Usage;
- if(this->OpenDevice(&this->_deviceHandle, this->vendorId, this->productId, this->usagePage, this->usage)) {
+ if (this->OpenDevice(&this->_deviceHandle, this->vendorId, this->productId, this->usagePage, this->usage)) {
isOpen = true;
}
}
@@ -44,7 +44,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
// Setup device info
deviceInfo = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
- if(deviceInfo == INVALID_HANDLE_VALUE) {
+ if (deviceInfo == INVALID_HANDLE_VALUE) {
LOG_ERROR("Invalid device info!\n");
return false;
}
@@ -53,7 +53,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;
SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &hidGuid, dwMemberIdx, &deviceInterfaceData);
- while(GetLastError() != ERROR_NO_MORE_ITEMS) {
+ while (GetLastError() != ERROR_NO_MORE_ITEMS) {
deviceInfoData.cbSize = sizeof(deviceInfoData);
// Get the required buffer size for device interface detail data
@@ -64,7 +64,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Get interface detail
- if(SetupDiGetDeviceInterfaceDetail(deviceInfo, &deviceInterfaceData, deviceInterfaceDetailData, dwSize, &dwSize, &deviceInfoData)) {
+ if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &deviceInterfaceData, deviceInterfaceDetailData, dwSize, &dwSize, &deviceInfoData)) {
// Open HID
deviceHandle = CreateFile(
@@ -77,7 +77,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
NULL);
// HID handle valid?
- if(deviceHandle != INVALID_HANDLE_VALUE) {
+ if (deviceHandle != INVALID_HANDLE_VALUE) {
// HID Attributes
HidD_GetAttributes(deviceHandle, &hidAttributes);
@@ -89,28 +89,30 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
HidP_GetCaps(hidPreparsedData, &hidCapabilities);
// Debug logging
- if(this->debugEnabled) {
+ if (this->debugEnabled) {
string manufacturerName = "";
string productName = "";
// HID manufacturer string
- if(HidD_GetManufacturerString(deviceHandle, &stringBytes, sizeof(stringBytes))) {
- for(int i = 0; i < (int)sizeof(stringBytes); i += 2) {
- if(stringBytes[i]) {
+ if (HidD_GetManufacturerString(deviceHandle, &stringBytes, sizeof(stringBytes))) {
+ for (int i = 0; i < (int)sizeof(stringBytes); i += 2) {
+ if (stringBytes[i]) {
manufacturerName.push_back(stringBytes[i]);
- } else {
+ }
+ else {
break;
}
}
}
// HID product string
- if(HidD_GetProductString(deviceHandle, &stringBytes, sizeof(stringBytes))) {
- for(int i = 0; i < (int)sizeof(stringBytes); i += 2) {
- if(stringBytes[i]) {
+ if (HidD_GetProductString(deviceHandle, &stringBytes, sizeof(stringBytes))) {
+ for (int i = 0; i < (int)sizeof(stringBytes); i += 2) {
+ if (stringBytes[i]) {
productName.push_back(stringBytes[i]);
- } else {
+ }
+ else {
break;
}
}
@@ -134,16 +136,17 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
}
// Set the result handle if this is the correct device
- if(!resultHandle &&
+ if (!resultHandle &&
hidAttributes.VendorID == vendorId &&
hidAttributes.ProductID == productId &&
hidCapabilities.UsagePage == usagePage &&
hidCapabilities.Usage == usage
- ) {
+ ) {
resultHandle = deviceHandle;
- // Close the HID handle if the device is incorrect
- } else {
+ // Close the HID handle if the device is incorrect
+ }
+ else {
CloseHandle(deviceHandle);
}
@@ -163,7 +166,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
SetupDiDestroyDeviceInfoList(deviceInfo);
// Copy found handle
- if(resultHandle && resultHandle != INVALID_HANDLE_VALUE) {
+ if (resultHandle && resultHandle != INVALID_HANDLE_VALUE) {
memcpy(handle, &resultHandle, sizeof(HANDLE));
return true;
}
@@ -171,33 +174,42 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US
return false;
}
-
+// Read HID report
int HIDDevice::Read(void *buffer, int length) {
//return HidD_GetInputReport(_deviceHandle, buffer, length);
DWORD bytesRead;
- if(ReadFile(_deviceHandle, buffer, length, &bytesRead, 0)) {
+ if (ReadFile(_deviceHandle, buffer, length, &bytesRead, 0)) {
return bytesRead;
}
return 0;
}
+// Write HID report
int HIDDevice::Write(void *buffer, int length) {
DWORD bytesWritten;
- if(WriteFile(_deviceHandle, buffer, length, &bytesWritten, 0)) {
+ if (WriteFile(_deviceHandle, buffer, length, &bytesWritten, 0)) {
return bytesWritten;
}
return 0;
}
+// Set feature report
bool HIDDevice::SetFeature(void *buffer, int length) {
return HidD_SetFeature(_deviceHandle, buffer, length);
}
+// Get feature report
+bool HIDDevice::GetFeature(void *buffer, int length) {
+ return HidD_GetFeature(_deviceHandle, buffer, length);
+}
+
+// Close the device
void HIDDevice::CloseDevice() {
- if(isOpen && _deviceHandle != NULL && _deviceHandle != INVALID_HANDLE_VALUE) {
+ if (isOpen && _deviceHandle != NULL && _deviceHandle != INVALID_HANDLE_VALUE) {
try {
CloseHandle(_deviceHandle);
- } catch(exception) {}
+ }
+ catch (exception) {}
}
isOpen = false;
}
diff --git a/TabletDriverService/HIDDevice.h b/TabletDriverService/HIDDevice.h
index 4de3f44..ee0b917 100644
--- a/TabletDriverService/HIDDevice.h
+++ b/TabletDriverService/HIDDevice.h
@@ -31,5 +31,6 @@ class HIDDevice {
int Read(void *buffer, int length);
int Write(void *buffer, int length);
bool SetFeature(void *buffer, int length);
+ bool GetFeature(void *buffer, int length);
void CloseDevice();
};
\ No newline at end of file
diff --git a/TabletDriverService/Main.cpp b/TabletDriverService/Main.cpp
index b272c6e..74608f0 100644
--- a/TabletDriverService/Main.cpp
+++ b/TabletDriverService/Main.cpp
@@ -16,6 +16,7 @@
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "winusb.lib")
+#pragma comment(lib, "winmm.lib")
// Global variables...
@@ -23,6 +24,9 @@ Tablet *tablet;
VMulti *vmulti;
ScreenMapper *mapper;
thread *tabletThread;
+chrono::high_resolution_clock::time_point timeBegin = chrono::high_resolution_clock::now();
+chrono::high_resolution_clock::time_point lastMovement = chrono::high_resolution_clock::now();
+Vector2D prevPos;
//
// Init console parameters
@@ -46,43 +50,45 @@ void RunTabletThread() {
bool isFirstReport = true;
bool isResent = false;
double x, y;
+ TabletFilter *filter;
+ bool filterTimedEnabled;
- BYTE buttons;
-
-
- chrono::high_resolution_clock::time_point timeBegin = chrono::high_resolution_clock::now();
+ //chrono::high_resolution_clock::time_point timeBegin = chrono::high_resolution_clock::now();
chrono::high_resolution_clock::time_point timeNow = chrono::high_resolution_clock::now();
- chrono::high_resolution_clock::time_point timeLast = chrono::high_resolution_clock::now();
//
// Main Loop
//
- while(true) {
+ while (true) {
//
// Read tablet position
//
status = tablet->ReadPosition();
+
// Position OK
- if(status == Tablet::PacketValid) {
+ if (status == Tablet::PacketValid) {
isResent = false;
- // Position invalid
- // Invalid packet id
- } else if(status == Tablet::PacketInvalid) {
+ // Invalid packet id
+ }
+ else if (status == Tablet::PacketInvalid) {
continue;
- // Valid packet but position is not in-range or invalid
- } else if(status == Tablet::PacketPositionInvalid) {
- if(!isResent && tablet->state.isValid) {
+ // Valid packet but position is not in-range or invalid
+ }
+ else if (status == Tablet::PacketPositionInvalid) {
+ if (!isResent && tablet->state.isValid) {
isResent = true;
tablet->state.isValid = false;
- } else {
+ }
+ else {
continue;
}
// Reading failed
- } else {
+ }
+ else {
LOG_ERROR("Tablet Read Error!\n");
CleanupAndExit(1);
}
@@ -90,68 +96,113 @@ void RunTabletThread() {
//
// Don't send the first report
//
- if(isFirstReport) {
+ if (isFirstReport) {
isFirstReport = false;
continue;
}
- // Raw position
- // LOG_INFO("Raw position: x=%-5d y=%-5d\n", tablet->reportData.x, tablet->reportData.y);
-
// Debug messages
- if(tablet->debugEnabled) {
+ if (tablet->debugEnabled) {
timeNow = chrono::high_resolution_clock::now();
double delta = (timeNow - timeBegin).count() / 1000000.0;
- LOG_DEBUG("STATE: %0.3f, %d, %0.3f, %0.3f, %0.3f\n",
+ /*LOG_DEBUG("STATE: %0.3f, %d, %0.3f, %0.3f, %0.3f, %0.3f\n",
delta,
tablet->state.buttons,
- tablet->state.x,
- tablet->state.y,
- tablet->state.pressure
+ tablet->state.position.x,
+ tablet->state.position.y,
+ tablet->state.pressure,
+ tablet->state.z
+ );*/
+ LOG_DEBUG("RAW:%0.3f,%0.3f,%0.3f\n",
+ delta,
+ tablet->state.position.x,
+ tablet->state.position.y
);
- //timeLast = chrono::high_resolution_clock::now();
}
// Set output values
- if(status == 0) {
- buttons = 0;
- } else {
- buttons = tablet->state.buttons;
+ if (status == Tablet::PacketPositionInvalid) {
+ tablet->state.buttons = 0;
+ }
+
+ //
+ // Packet filters
+ //
+ // Is there any filters?
+ if (tablet->filterPacketCount > 0) {
+
+ // Loop through filters
+ for (int filterIndex = 0; filterIndex < tablet->filterPacketCount; filterIndex++) {
+
+ // Filter
+ filter = tablet->filterPacket[filterIndex];
+
+ // Enabled?
+ if (filter != NULL && filter->isEnabled) {
+
+ // Process
+ filter->SetTarget(tablet->state.position, tablet->state.z);
+ filter->Update();
+ filter->GetPosition(&tablet->state.position);
+ }
+
+ }
}
- // Do not write report when filter is enabled
- if(!tablet->filter.isEnabled) {
+ // Timed filter enabled?
+ filterTimedEnabled = false;
+ for (int filterIndex = 0; filterIndex < tablet->filterTimedCount; filterIndex++) {
+ if (tablet->filterTimed[filterIndex]->isEnabled)
+ filterTimedEnabled = true;
+ }
+
+ static Vector2D last;
+ if (
+ // Button binded to wheel + Binded button pressed + tip pressed
+ (tablet->buttonMap[1] == 6 and tablet->state.buttons == 33)
+ or
+ (tablet->buttonMap[2] == 6 and tablet->state.buttons == 5)
+ ) {
+ tablet->state.buttons &= ~(1 << 0);
+ mouse_event(MOUSEEVENTF_WHEEL, 0, 0, -(last.y - tablet->state.position.y) * tablet->settings.mouseWheelSpeed, 0);
+ }
+ last = tablet->state.position;
+
+
+ // Do not write report when timed filter is enabled
+ if (tablet->filterTimedCount == 0 || !filterTimedEnabled) {
// Relative mode
- if(vmulti->mode == VMulti::ModeRelativeMouse) {
+ if (vmulti->mode == VMulti::ModeRelativeMouse) {
- x = tablet->state.x;
- y = tablet->state.y;
+ x = tablet->state.position.x;
+ y = tablet->state.position.y;
// Map position to virtual screen (values between 0 and 1)
mapper->GetRotatedTabletPosition(&x, &y);
// Create VMulti report
- vmulti->CreateReport(buttons, x, y, tablet->state.pressure);
+ vmulti->CreateReport(tablet->state.buttons, x, y, tablet->state.pressure);
// Write report to VMulti device
vmulti->WriteReport();
- // Absolute / Digitizer mode
- } else {
+ // Absolute / Digitizer mode
+ }
+ else {
// Get x & y from the tablet state
- x = tablet->state.x;
- y = tablet->state.y;
+ x = tablet->state.position.x;
+ y = tablet->state.position.y;
// Map position to virtual screen (values betweeb 0->1)
mapper->GetScreenPosition(&x, &y);
// Create VMulti report
- vmulti->CreateReport(buttons, x, y, tablet->state.pressure);
+ vmulti->CreateReport(tablet->state.buttons, x, y, tablet->state.pressure);
// Write report to VMulti device
vmulti->WriteReport();
@@ -164,74 +215,130 @@ void RunTabletThread() {
//
// Tablet filter timer callback
//
-VOID CALLBACK FilterTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired) {
- double x, y;
- // Filter enabled?
- if(!tablet->filter.isEnabled) return;
+static VOID CALLBACK FilterTimerCallback(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
+{
+ Vector2D position, position_prev;
+ double z;
+ TabletFilter *filter;
- // Set filter targets
- tablet->filter.targetX = tablet->state.x;
- tablet->filter.targetY = tablet->state.y;
+ chrono::high_resolution_clock::time_point timeNow = chrono::high_resolution_clock::now();
- // First report?
- if(tablet->filter.targetX == 0 && tablet->filter.targetY == 0) {
- tablet->filter.x = tablet->filter.targetX;
- tablet->filter.y = tablet->filter.targetY;
+ // Set position
+ position.Set(tablet->state.position);
+ z = tablet->state.z;
+ // For debug
+ tablet->filterTimed[0]->GetPosition(&position_prev);
+
+ // Detect absence of movement
+ double noMovement = 0.0;
+ if (position.Distance(prevPos) > 0.000001)
+ {
+ lastMovement = timeNow;
+ prevPos = position;
+ }
+ else
+ {
+ noMovement = (timeNow - lastMovement).count() / 1000000.0;
}
- // Process filter
- tablet->ProcessFilter();
+ // Loop through filters
+ for (int filterIndex = 0; filterIndex < tablet->filterTimedCount; filterIndex++) {
- // Set filtered output
- x = tablet->filter.x;
- y = tablet->filter.y;
+ // Filter
+ filter = tablet->filterTimed[filterIndex];
+ // Filter enabled?
+ if (!filter->isEnabled) return;
- // Relative mode
- if(vmulti->mode == VMulti::ModeRelativeMouse) {
- // Map position to virtual screen (values between 0 and 1)
- mapper->GetRotatedTabletPosition(&x, &y);
+ if (noMovement > 35)
+ {
+ filter->Reset(position);
+ return;
+ }
- double dx = tablet->state.x - vmulti->relativeData.lastPosition.x;
- double dy = tablet->state.y - vmulti->relativeData.lastPosition.y;
- double distance = sqrt(dx * dx + dy * dy);
- if(distance > 10) {
- vmulti->ResetRelativeData(x, y);
+ // Set filter targets
+ filter->SetTarget(position, z);
+ //filter->z = tablet->state.z;
+
+ // Update filter position
+ filter->Update();
+
+ // Set output vector
+ filter->GetPosition(&position);
+
+ }
+
+
+ // Debug messages
+ if (tablet->debugEnabled) {
+ timeNow = chrono::high_resolution_clock::now();
+ double delta = (timeNow - timeBegin).count() / 1000000.0;
+
+ if (round(position.x*100) != round(position_prev.x * 100) or round(position.y * 100) != round(position_prev.y * 100)) {
+ LOG_DEBUG("FIL:%0.3f,%0.3f,%0.3f\n",
+ delta,
+ position.x,
+ position.y
+ );
}
+ }
+
+
+ //
+ // Relative mode
+ //
+ if (vmulti->mode == VMulti::ModeRelativeMouse) {
+
+ // Map position to virtual screen (values between 0 and 1)
+ mapper->GetRotatedTabletPosition(&position.x, &position.y);
+
// Create VMulti report
- vmulti->CreateReport(tablet->state.buttons, x, y, tablet->state.pressure);
+ vmulti->CreateReport(
+ tablet->state.buttons,
+ position.x,
+ position.y,
+ tablet->state.pressure
+ );
// Write report to VMulti device if report has changed
- if(vmulti->HasReportChanged()
+ if (vmulti->HasReportChanged()
||
vmulti->reportRelativeMouse.x != 0
||
vmulti->reportRelativeMouse.y != 0
- ) {
+ ) {
vmulti->WriteReport();
}
+
+ }
+
+ //
// Absolute / Digitizer mode
- } else {
+ //
+ else {
// Map position to virtual screen (values betweeb 0->1)
- mapper->GetScreenPosition(&x, &y);
+ mapper->GetScreenPosition(&position.x, &position.y);
// Create VMulti report
- vmulti->CreateReport(tablet->state.buttons, x, y, tablet->state.pressure);
+ vmulti->CreateReport(
+ tablet->state.buttons,
+ position.x,
+ position.y,
+ tablet->state.pressure
+ );
// Write report to VMulti device
- if(vmulti->HasReportChanged() && tablet->state.isValid) {
+ if (vmulti->HasReportChanged() && tablet->state.isValid) {
vmulti->WriteReport();
}
}
-
-
}
@@ -263,7 +370,7 @@ int main(int argc, char**argv) {
// VMulti Device
vmulti = new VMulti();
- if(!vmulti->isOpen) {
+ if (!vmulti->isOpen) {
LOG_ERROR("Can't open VMulti device!\n\n");
LOG_ERROR("Possible fixes:\n");
LOG_ERROR("1) Install VMulti driver\n");
@@ -275,10 +382,10 @@ int main(int argc, char**argv) {
// Read init file
filename = "init.cfg";
- if(argc > 1) {
+ if (argc > 1) {
filename = argv[1];
}
- if(!ReadCommandFile(filename)) {
+ if (!ReadCommandFile(filename)) {
LOG_ERROR("Can't open '%s'\n", filename.c_str());
}
@@ -286,42 +393,43 @@ int main(int argc, char**argv) {
//
// Main loop that reads input from the console.
//
- while(true) {
+ while (true) {
// Broken pipe
- if(!cin) break;
+ if (!cin) break;
// Read line from the console
try {
getline(cin, line);
- } catch(exception) {
+ }
+ catch (exception) {
break;
}
// Process valid lines
- if(line.length() > 0) {
+ if (line.length() > 0) {
cmd = new CommandLine(line);
//
// Start command
//
- if(cmd->is("start")) {
+ if (cmd->is("start")) {
LOG_INFO(">> %s\n", cmd->line.c_str());
- if(running) {
+ if (running) {
LOG_INFO("Driver is already started!\n");
continue;
}
// Unknown tablet
- if(tablet == NULL) {
+ if (tablet == NULL) {
LOG_ERROR("Tablet not found!\n");
CleanupAndExit(1);
}
// Tablet init
- if(!tablet->Init()) {
+ if (!tablet->Init()) {
LOG_ERROR("Tablet init failed!\n");
LOG_ERROR("Possible fixes:\n");
LOG_ERROR("1) Uninstall other tablet drivers.\n");
@@ -335,12 +443,11 @@ int main(int argc, char**argv) {
// Set running state
running = true;
-
-
- // Filter timer
- tablet->filter.callback = FilterTimerCallback;
- tablet->StartFilterTimer();
-
+ // Timed filter timer
+ if (tablet->filterPacketCount > 0) {
+ tablet->filterTimed[0]->callback = FilterTimerCallback;
+ tablet->filterTimed[0]->StartTimer();
+ }
// Start the tablet thread
tabletThread = new thread(RunTabletThread);
@@ -349,21 +456,24 @@ int main(int argc, char**argv) {
LogStatus();
- //
- // Echo
- //
- } else if(cmd->is("echo")) {
- if(cmd->valueCount > 0) {
+ //
+ // Echo
+ //
+ }
+ else if (cmd->is("echo")) {
+ if (cmd->valueCount > 0) {
LOG_INFO("%s\n", cmd->line.c_str() + 5);
- } else {
+ }
+ else {
LOG_INFO("\n");
}
-
- //
- // Process all other commands
- //
- } else {
+
+ //
+ // Process all other commands
+ //
+ }
+ else {
ProcessCommand(cmd);
}
delete cmd;
@@ -388,12 +498,14 @@ void CleanupAndExit(int code) {
delete vmulti;
*/
- // Delete filter timer
- if(tablet != NULL) {
- tablet->StopFilterTimer();
+ // Delete filter timer
+ if (tablet != NULL) {
+ if (tablet->filterTimedCount != 0) {
+ tablet->filterTimed[0]->StopTimer();
+ }
}
- if(vmulti != NULL) {
+ if (vmulti != NULL) {
vmulti->ResetReport();
}
LOGGER_STOP();
diff --git a/TabletDriverService/PositionRingBuffer.cpp b/TabletDriverService/PositionRingBuffer.cpp
new file mode 100644
index 0000000..7a5acbb
--- /dev/null
+++ b/TabletDriverService/PositionRingBuffer.cpp
@@ -0,0 +1,97 @@
+#include "stdafx.h"
+#include "PositionRingBuffer.h"
+
+
+//
+// Constructor
+//
+PositionRingBuffer::PositionRingBuffer() {
+ maxLength = sizeof(buffer) / sizeof(Vector2D);
+ length = 0;
+ count = 0;
+ index = 0;
+ isValid = false;
+}
+
+
+//
+// Destructor
+//
+PositionRingBuffer::~PositionRingBuffer() {
+}
+
+
+//
+// Set buffer length
+//
+void PositionRingBuffer::SetLength(int len) {
+ if(len > maxLength) {
+ length = maxLength;
+ } else {
+ length = len;
+ }
+}
+
+
+//
+// Add position to buffer
+//
+void PositionRingBuffer::Add(Vector2D vector) {
+ buffer[index].x = vector.x;
+ buffer[index].y = vector.y;
+ index++;
+ count++;
+ if(count > length) {
+ count = length;
+ }
+ if(index >= length) {
+ index = 0;
+ }
+ isValid = true;
+}
+
+
+//
+// Get position history from the buffer
+//
+bool PositionRingBuffer::GetLatest(Vector2D *output, int delta) {
+ int newIndex;
+
+ // Buffer empty?
+ if(count == 0) return false;
+
+ // Valid delta?
+ if(delta > 0 || delta <= -count) return false;
+
+ newIndex = index - 1 + delta;
+
+ // Limits
+ if(newIndex < 0) newIndex = count + newIndex;
+
+ if(newIndex < 0 || newIndex >= count) {
+ return false;
+ }
+
+ output->x = buffer[newIndex].x;
+ output->y = buffer[newIndex].y;
+ return true;
+}
+
+
+//
+// Reset buffer
+//
+void PositionRingBuffer::Reset() {
+ count = 0;
+ index = 0;
+ isValid = false;
+}
+
+
+
+//
+// [] operator
+//
+Vector2D *PositionRingBuffer::operator[](std::size_t index) {
+ return &(buffer[index]);
+}
\ No newline at end of file
diff --git a/TabletDriverService/PositionRingBuffer.h b/TabletDriverService/PositionRingBuffer.h
new file mode 100644
index 0000000..f41fe42
--- /dev/null
+++ b/TabletDriverService/PositionRingBuffer.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Vector2D.h"
+
+class PositionRingBuffer {
+public:
+ Vector2D buffer[100];
+ int maxLength;
+ int length;
+ int count;
+ int index;
+ bool isValid;
+
+ void SetLength(int length);
+ void Add(Vector2D vector);
+ bool GetLatest(Vector2D *output, int delta);
+ void Reset();
+
+ Vector2D *operator[](std::size_t index);
+
+
+ PositionRingBuffer();
+ ~PositionRingBuffer();
+};
+
diff --git a/TabletDriverService/ProcessCommand.cpp b/TabletDriverService/ProcessCommand.cpp
index 591b2e5..1e494c6 100644
--- a/TabletDriverService/ProcessCommand.cpp
+++ b/TabletDriverService/ProcessCommand.cpp
@@ -15,18 +15,19 @@ bool ProcessCommand(CommandLine *cmd) {
//
// Tablet
//
- if(cmd->is("Tablet")) {
+ if (cmd->is("Tablet")) {
// USB Tablet
- if(cmd->valueCount == 3) {
+ if (cmd->valueCount == 3) {
string guid = cmd->GetString(0, "");
int stringId = cmd->GetInt(1, 0);
string stringMatch = cmd->GetString(2, "");
- if(tablet == NULL) {
+ if (tablet == NULL) {
tablet = new Tablet(guid, stringId, stringMatch);
- if(tablet->isOpen) {
+ if (tablet->isOpen) {
LOG_INFO("Tablet found!\n");
- } else {
+ }
+ else {
LOG_WARNING("Can't open USB tablet '%s' %d '%s'\n", guid.c_str(), stringId, stringMatch.c_str());
delete tablet;
tablet = NULL;
@@ -35,31 +36,34 @@ bool ProcessCommand(CommandLine *cmd) {
}
// HID Tablet
- else if(cmd->valueCount == 4) {
+ else if (cmd->valueCount == 4) {
USHORT vendorID = cmd->GetInt(0, 0);
USHORT productID = cmd->GetInt(1, 0);
USHORT usagePage = cmd->GetInt(2, 0);
USHORT usage = cmd->GetInt(3, 0);
- if(tablet == NULL) {
+ if (tablet == NULL) {
tablet = new Tablet(vendorID, productID, usagePage, usage);
- if(tablet->isOpen) {
+ if (tablet->isOpen) {
LOG_INFO("Tablet found!\n");
- } else {
+ }
+ else {
LOG_WARNING("Can't open HID tablet 0x%04X 0x%04X 0x%04X 0x%04X\n", vendorID, productID, usagePage, usage);
delete tablet;
tablet = NULL;
}
}
- } else {
- if(tablet != NULL) {
- if(tablet->usbDevice != NULL) {
+ }
+ else {
+ if (tablet != NULL) {
+ if (tablet->usbDevice != NULL) {
USBDevice *usb = tablet->usbDevice;
LOG_INFO("Tablet = USB(\"%s\", %d, \"%s\")\n",
usb->guid.c_str(),
usb->stringId,
usb->stringMatch.c_str()
);
- } else if(tablet->hidDevice != NULL) {
+ }
+ else if (tablet->hidDevice != NULL) {
HIDDevice *hid = tablet->hidDevice;
LOG_INFO("Tablet = HID(0x%04X 0x%04X 0x%04X 0x%04X)\n",
hid->vendorId,
@@ -68,15 +72,16 @@ bool ProcessCommand(CommandLine *cmd) {
hid->usage
);
}
- } else {
+ }
+ else {
LOG_INFO("Tablet is not defined.\n");
}
}
}
// Check tablet
- else if(cmd->is("CheckTablet")) {
- if(!CheckTablet()) {
+ else if (cmd->is("CheckTablet")) {
+ if (!CheckTablet()) {
LOG_ERROR("Tablet not found!\n");
LOG_ERROR("Check the list of supported tablets from the GitHub page.\n");
LOG_ERROR("http://github.com/hawku/TabletDriver\n");
@@ -85,12 +90,12 @@ bool ProcessCommand(CommandLine *cmd) {
}
// HID List
- else if(cmd->is("HIDList")) {
+ else if (cmd->is("HIDList")) {
HANDLE hidHandle = 0;
HIDDevice *hid = new HIDDevice();
hid->debugEnabled = true;
hid->OpenDevice(&hidHandle, 1, 1, 1, 1);
- if(hidHandle > 0) {
+ if (hidHandle > 0) {
CloseHandle(hidHandle);
}
delete hid;
@@ -98,18 +103,19 @@ bool ProcessCommand(CommandLine *cmd) {
// HID Device 2
- else if(cmd->is("HID2")) {
+ else if (cmd->is("HID2")) {
- if(cmd->valueCount == 4) {
+ if (cmd->valueCount == 4) {
USHORT vendorID = cmd->GetInt(0, 0);
USHORT productID = cmd->GetInt(1, 0);
USHORT usagePage = cmd->GetInt(2, 0);
USHORT usage = cmd->GetInt(3, 0);
- if(tablet->hidDevice2 == NULL) {
+ if (tablet->hidDevice2 == NULL) {
tablet->hidDevice2 = new HIDDevice(vendorID, productID, usagePage, usage);
- if(tablet->hidDevice2->isOpen) {
+ if (tablet->hidDevice2->isOpen) {
LOG_INFO("HID Device found!\n");
- } else {
+ }
+ else {
LOG_ERROR("Can't open HID device 0x%04X 0x%04X 0x%04X 0x%04X\n", vendorID, productID, usagePage, usage);
delete tablet->hidDevice2;
tablet->hidDevice2 = NULL;
@@ -119,106 +125,126 @@ bool ProcessCommand(CommandLine *cmd) {
}
// Tablet Name
- else if(cmd->is("Name")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("Name")) {
+ if (tablet == NULL) return false;
tablet->name = cmd->GetString(0, tablet->name);
LOG_INFO("Tablet name = '%s'\n", tablet->name.c_str());
}
// Report Id
- else if(cmd->is("ReportId")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("ReportId")) {
+ if (tablet == NULL) return false;
tablet->settings.reportId = cmd->GetInt(0, tablet->settings.reportId);
LOG_INFO("Tablet report id = %d\n", tablet->settings.reportId);
}
// Report Length
- else if(cmd->is("ReportLength")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("ReportLength")) {
+ if (tablet == NULL) return false;
tablet->settings.reportLength = cmd->GetInt(0, tablet->settings.reportLength);
LOG_INFO("Tablet report length = %d\n", tablet->settings.reportLength);
}
- // Button Mask
- else if(cmd->is("ButtonMask")) {
- if(tablet == NULL) return false;
- tablet->settings.buttonMask = cmd->GetInt(0, tablet->settings.buttonMask);
- LOG_INFO("Tablet button mask = %02X\n", tablet->settings.buttonMask);
+ // Detect Mask
+ else if (cmd->is("DetectMask")) {
+ if (tablet == NULL) return false;
+ tablet->settings.detectMask = cmd->GetInt(0, tablet->settings.detectMask);
+ LOG_INFO("Tablet detect mask = %02X\n", tablet->settings.detectMask);
+ }
+
+ // Ignore Mask
+ else if (cmd->is("IgnoreMask")) {
+ if (tablet == NULL) return false;
+ tablet->settings.ignoreMask = cmd->GetInt(0, tablet->settings.ignoreMask);
+ LOG_INFO("Tablet ignore mask = %02X\n", tablet->settings.ignoreMask);
}
// Max X
- else if(cmd->is("MaxX")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("MaxX")) {
+ if (tablet == NULL) return false;
tablet->settings.maxX = cmd->GetInt(0, tablet->settings.maxX);
LOG_INFO("Tablet max X = %d\n", tablet->settings.maxX);
}
// Max Y
- else if(cmd->is("MaxY")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("MaxY")) {
+ if (tablet == NULL) return false;
tablet->settings.maxY = cmd->GetInt(0, tablet->settings.maxY);
LOG_INFO("Tablet max Y = %d\n", tablet->settings.maxY);
}
// Max Pressure
- else if(cmd->is("MaxPressure")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("MaxPressure")) {
+ if (tablet == NULL) return false;
tablet->settings.maxPressure = cmd->GetInt(0, tablet->settings.maxPressure);
LOG_INFO("Tablet max pressure = %d\n", tablet->settings.maxPressure);
}
// Click Pressure
- else if(cmd->is("ClickPressure")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("ClickPressure")) {
+ if (tablet == NULL) return false;
tablet->settings.clickPressure = cmd->GetInt(0, tablet->settings.clickPressure);
LOG_INFO("Tablet click pressure = %d\n", tablet->settings.clickPressure);
}
// Keep pen tip down
- else if(cmd->is("KeepTipDown")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("KeepTipDown")) {
+ if (tablet == NULL) return false;
tablet->settings.keepTipDown = cmd->GetInt(0, tablet->settings.keepTipDown);
LOG_INFO("Tablet pen tip keep down = %d packets\n", tablet->settings.keepTipDown);
}
// Width
- else if(cmd->is("Width")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("Width")) {
+ if (tablet == NULL) return false;
tablet->settings.width = cmd->GetDouble(0, tablet->settings.width);
LOG_INFO("Tablet width = %0.2f mm\n", tablet->settings.width);
}
// Height
- else if(cmd->is("Height")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("Height")) {
+ if (tablet == NULL) return false;
tablet->settings.height = cmd->GetDouble(0, tablet->settings.height);
LOG_INFO("Tablet height = %0.2f mm\n", tablet->settings.height);
}
// Skew
- else if(cmd->is("Skew")) {
- if(tablet == NULL) return false;
+ else if (cmd->is("Skew")) {
+ if (tablet == NULL) return false;
tablet->settings.skew = cmd->GetDouble(0, tablet->settings.skew);
LOG_INFO("Tablet skew = Shift X-axis %0.2f mm per Y-axis mm\n", tablet->settings.skew);
}
- // Skew
- else if(cmd->is("Type")) {
- if(tablet == NULL) return false;
- if(cmd->GetStringLower(0, "") == "wacomintuos") {
- tablet->settings.type = tablet->TypeWacomIntuos;
+ // Type
+ else if (cmd->is("Type")) {
+ if (tablet == NULL) return false;
+
+ // Wacom Intuos (490)
+ if (cmd->GetStringLower(0, "") == "wacomintuos") {
+ tablet->settings.type = TabletSettings::TypeWacomIntuos;
}
+
+ // Wacom CTL-4100
+ else if (cmd->GetStringLower(0, "") == "wacom4100") {
+ tablet->settings.type = TabletSettings::TypeWacom4100;
+ }
+
+ // Wacom Drivers
+ else if (cmd->GetStringLower(0, "") == "wacomdrivers") {
+ tablet->settings.type = TabletSettings::TypeWacomDrivers;
+ }
+
LOG_INFO("Tablet type = %d\n", tablet->settings.type);
}
// Init Feature Report
- else if(cmd->is("InitFeature") && cmd->valueCount > 0) {
- if(tablet == NULL) return false;
+ else if (cmd->is("InitFeature") && cmd->valueCount > 0) {
+ if (tablet == NULL) return false;
tablet->initFeature = new BYTE[cmd->valueCount];
- for(int i = 0; i < (int)cmd->valueCount; i++) {
+ for (int i = 0; i < (int)cmd->valueCount; i++) {
tablet->initFeature[i] = cmd->GetInt(i, 0);
}
tablet->initFeatureLength = cmd->valueCount;
@@ -226,10 +252,10 @@ bool ProcessCommand(CommandLine *cmd) {
}
// Init Output Report
- else if(cmd->is("InitReport") && cmd->valueCount > 0) {
- if(tablet == NULL) return false;
+ else if (cmd->is("InitReport") && cmd->valueCount > 0) {
+ if (tablet == NULL) return false;
tablet->initReport = new BYTE[cmd->valueCount];
- for(int i = 0; i < (int)cmd->valueCount; i++) {
+ for (int i = 0; i < (int)cmd->valueCount; i++) {
tablet->initReport[i] = cmd->GetInt(i, 0);
}
tablet->initReportLength = cmd->valueCount;
@@ -239,42 +265,62 @@ bool ProcessCommand(CommandLine *cmd) {
//
- // Send Feature Report
+ // Set Feature Report
//
- else if((cmd->is("FeatureReport") || cmd->is("Feature")) && cmd->valueCount > 0) {
- if(tablet == NULL) return false;
- if(tablet->hidDevice == NULL) return false;
- int length = cmd->valueCount;
+ else if ((cmd->is("SetFeature") || cmd->is("Feature")) && cmd->valueCount > 1) {
+ if (tablet == NULL) return false;
+ if (tablet->hidDevice == NULL) return false;
+ int length = cmd->GetInt(0, 1);
BYTE *buffer = new BYTE[length];
- for(int i = 0; i < length; i++) {
- buffer[i] = cmd->GetInt(i, 0);
+ for (int i = 0; i < length; i++) {
+ buffer[i] = cmd->GetInt(i + 1, 0);
}
+ LOG_INFOBUFFER(buffer, length, "Set Feature Report (%d): ", length);
tablet->hidDevice->SetFeature(buffer, length);
- LOG_INFOBUFFER(buffer, length, "Tablet HID Feature Report: ");
+ LOG_INFO("HID Feature set!\n");
+ delete buffer;
+ }
+
+ //
+ // Get Feature Report
+ //
+ else if (cmd->is("GetFeature") && cmd->valueCount > 1) {
+ if (tablet == NULL) return false;
+ if (tablet->hidDevice == NULL) return false;
+ int length = cmd->GetInt(0, 1);
+ BYTE *buffer = new BYTE[length];
+ for (int i = 0; i < length; i++) {
+ buffer[i] = cmd->GetInt(i + 1, 0);
+ }
+ LOG_INFOBUFFER(buffer, length, "Get Feature Report (%d): ", length);
+ tablet->hidDevice->GetFeature(buffer, length);
+ LOG_INFOBUFFER(buffer, length, "Result Feature Report (%d): ", length);
delete buffer;
}
+
//
// Send Output Report
//
- else if((cmd->is("OutputReport") || cmd->is("Report")) && cmd->valueCount > 0) {
- if(tablet == NULL) return false;
- if(tablet->hidDevice == NULL) return false;
- int length = cmd->valueCount;
+ else if ((cmd->is("OutputReport") || cmd->is("Report")) && cmd->valueCount > 1) {
+ if (tablet == NULL) return false;
+ if (tablet->hidDevice == NULL) return false;
+ int length = cmd->GetInt(0, 1);
BYTE *buffer = new BYTE[length];
- for(int i = 0; i < length; i++) {
- buffer[i] = cmd->GetInt(i, 0);
+ for (int i = 0; i < length; i++) {
+ buffer[i] = cmd->GetInt(i + 1, 0);
}
+ LOG_INFOBUFFER(buffer, length, "Sending HID Report: ");
tablet->hidDevice->Write(buffer, length);
- LOG_INFOBUFFER(buffer, length, "Tablet HID Output Report: ");
+ LOG_INFO("Report sent!\n");
delete buffer;
}
//
// Tablet Area
//
- else if(cmd->is("TabletArea") || cmd->is("Area")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("TabletArea") || cmd->is("Area")) {
+ if (!CheckTablet()) return true;
mapper->areaTablet.width = cmd->GetDouble(0, mapper->areaTablet.width);
mapper->areaTablet.height = cmd->GetDouble(1, mapper->areaTablet.height);
mapper->areaTablet.x = cmd->GetDouble(2, mapper->areaTablet.x);
@@ -284,21 +330,29 @@ bool ProcessCommand(CommandLine *cmd) {
}
// Button Map
- else if(cmd->is("ButtonMap") || cmd->is("Buttons")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("ButtonMap") || cmd->is("Buttons")) {
+ if (!CheckTablet()) return true;
char buttonMapBuffer[32];
int index = 0;
- for(int i = 0; i < 8; i++) {
+ for (int i = 0; i < 8; i++) {
tablet->buttonMap[i] = cmd->GetInt(i, tablet->buttonMap[i]);
index += sprintf_s(buttonMapBuffer + index, 32 - index, "%d ", tablet->buttonMap[i]);
}
LOG_INFO("Button Map = %s\n", buttonMapBuffer);
}
+ // Mouse Wheel Speed
+ else if (cmd->is("MouseWheelSpeed")) {
+ if (!CheckTablet()) return true;
+ int MouseWheelSpeed = (int)cmd->GetInt(0, tablet->settings.mouseWheelSpeed);
+ tablet->settings.mouseWheelSpeed = MouseWheelSpeed;
+ LOG_INFO("Mouse Wheel Speed = %d\n", MouseWheelSpeed);
+ }
+
// Screen Map Area
- else if(cmd->is("ScreenArea") || cmd->is("Screen")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("ScreenArea") || cmd->is("Screen")) {
+ if (!CheckTablet()) return true;
mapper->areaScreen.width = cmd->GetDouble(0, mapper->areaScreen.width);
mapper->areaScreen.height = cmd->GetDouble(1, mapper->areaScreen.height);
mapper->areaScreen.x = cmd->GetDouble(2, mapper->areaScreen.x);
@@ -312,8 +366,8 @@ bool ProcessCommand(CommandLine *cmd) {
}
// Screen Map Area
- else if(cmd->is("DesktopSize") || cmd->is("Desktop")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("DesktopSize") || cmd->is("Desktop")) {
+ if (!CheckTablet()) return true;
mapper->areaVirtualScreen.width = cmd->GetDouble(0, mapper->areaVirtualScreen.width);
mapper->areaVirtualScreen.height = cmd->GetDouble(1, mapper->areaVirtualScreen.height);
LOG_INFO("Desktop size = (%0.2f px x %0.2f px)\n",
@@ -323,30 +377,34 @@ bool ProcessCommand(CommandLine *cmd) {
}
// Move tablet area to border
- else if(cmd->is("TabletMove") || cmd->is("Move") && cmd->valueCount > 0) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("TabletMove") || cmd->is("Move") && cmd->valueCount > 0) {
+ if (!CheckTablet()) return true;
string border;
double offset;
bool moved = true;
- for(int i = 0; i < cmd->valueCount; i += 2) {
+ for (int i = 0; i < cmd->valueCount; i += 2) {
border = cmd->GetStringLower(i, "");
offset = cmd->GetDouble(i + 1, 0.0);
moved = true;
- if(border == "top") {
+ if (border == "top") {
mapper->areaTablet.y = offset;
- } else if(border == "bottom") {
+ }
+ else if (border == "bottom") {
mapper->areaTablet.y = tablet->settings.height - mapper->areaTablet.height - offset;
- } else if(border == "left") {
+ }
+ else if (border == "left") {
mapper->areaTablet.x = offset;
- } else if(border == "right") {
+ }
+ else if (border == "right") {
mapper->areaTablet.x = tablet->settings.width - mapper->areaTablet.width - offset;
- } else {
+ }
+ else {
moved = false;
}
- if(moved) {
+ if (moved) {
LOG_INFO("Tablet area moved to %s border with %0.2f mm margin.\n", border.c_str(), offset);
LogTabletArea(" New tablet area");
}
@@ -356,8 +414,8 @@ bool ProcessCommand(CommandLine *cmd) {
//
// Rotate tablet area
//
- else if(cmd->is("Rotate")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("Rotate")) {
+ if (!CheckTablet()) return true;
double value = cmd->GetDouble(0, 0);
mapper->SetRotation(value);
LOG_INFO("Rotation matrix = [%f,%f,%f,%f]\n",
@@ -372,43 +430,62 @@ bool ProcessCommand(CommandLine *cmd) {
//
// Relative mode sensitivity
//
- else if(cmd->is("Sensitivity")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("Sensitivity")) {
+ if (!CheckTablet()) return true;
vmulti->relativeData.sensitivity = cmd->GetDouble(0, vmulti->relativeData.sensitivity);
LOG_INFO("Relative mode sensitivity = %0.2f px/mm\n", vmulti->relativeData.sensitivity);
}
+ //
+ // Relative mode reset distance
+ //
+ else if (cmd->is("ResetDistance")) {
+ if (!CheckTablet()) return true;
+ vmulti->relativeData.resetDistance = cmd->GetDouble(0, vmulti->relativeData.resetDistance);
+ LOG_INFO("Relative mode reset distance = %0.2f mm\n", vmulti->relativeData.resetDistance);
+ }
+
//
// VMulti output mode
//
- else if(cmd->is("Mode")) {
+ else if (cmd->is("Mode")) {
string mode = cmd->GetStringLower(0, "");
// Absolute mouse
- if(mode.compare(0, 3, "abs") == 0) {
- if(vmulti->mode != VMulti::ModeAbsoluteMouse)
+ if (mode.compare(0, 3, "abs") == 0) {
+ if (vmulti->mode != VMulti::ModeAbsoluteMouse)
vmulti->ResetReport();
vmulti->mode = VMulti::ModeAbsoluteMouse;
LOG_INFO("Output Mode = Absolute\n");
}
// Relative mouse
- else if(mode.compare(0, 3, "rel") == 0) {
- if(vmulti->mode != VMulti::ModeRelativeMouse)
+ else if (mode.compare(0, 3, "rel") == 0) {
+ if (vmulti->mode != VMulti::ModeRelativeMouse)
vmulti->ResetReport();
vmulti->mode = VMulti::ModeRelativeMouse;
LOG_INFO("Output Mode = Relative\n");
}
// Digitizer
- else if(mode.compare(0, 3, "dig") == 0 || mode.compare(0, 3, "pen") == 0) {
- if(vmulti->mode != VMulti::ModeDigitizer)
+ else if (mode.compare(0, 3, "dig") == 0 || mode.compare(0, 3, "pen") == 0) {
+ if (vmulti->mode != VMulti::ModeDigitizer)
vmulti->ResetReport();
vmulti->mode = VMulti::ModeDigitizer;
LOG_INFO("Output Mode = Digitizer\n");
+ }
+
+ // SendInput
+ else if (mode.compare(0, 4, "send") == 0) {
+ if (vmulti->mode != VMulti::ModeSendInput)
+ vmulti->ResetReport();
+ vmulti->mode = VMulti::ModeSendInput;
+ vmulti->UpdateMonitorInfo();
+ LOG_INFO("Output Mode = SendInput\n");
+ }
- } else {
+ else {
LOG_ERROR("Unknown output mode '%s'\n", mode.c_str());
}
@@ -418,70 +495,183 @@ bool ProcessCommand(CommandLine *cmd) {
//
// Smoothing filter
//
- else if(cmd->is("Filter")) {
- if(!CheckTablet()) return true;
- double latency = cmd->GetDouble(0, tablet->GetFilterLatency());
- double threshold = cmd->GetDouble(1, tablet->filter.threshold * 100);
-
- threshold /= 100;
+ else if (cmd->is("Smoothing")) {
+ if (!CheckTablet()) return true;
+ double latency = cmd->GetDouble(0, tablet->smoothing.GetLatency());
+ double threshold = cmd->GetDouble(1, tablet->smoothing.threshold);
string stringValue = cmd->GetStringLower(0, "");
// Off / False
- if(stringValue == "off" || stringValue == "false") {
+ if (stringValue == "off" || stringValue == "false") {
latency = 0;
}
// Limits
- if(latency < 0) latency = 1;
- if(latency > 1000) latency = 1000;
- if(threshold < 0.1) threshold = 0.1;
- if(threshold > 0.99) threshold = 0.99;
+ if (latency < 0) latency = 1;
+ if (latency > 1000) latency = 1000;
+ if (threshold < 0.001) threshold = 0.001;
+ if (threshold > 0.999) threshold = 0.999;
// Set threshold
- tablet->filter.threshold = threshold;
+ tablet->smoothing.threshold = threshold;
- // Set filter latency
- tablet->SetFilterLatency(latency);
+ // Set smoothing filter latency
+ tablet->smoothing.SetLatency(latency);
// Print output
- if(tablet->filter.weight < 1.0) {
- tablet->filter.isEnabled = true;
- LOG_INFO("Filter = %0.2f ms to reach %0.0f%% (weight = %f)\n", latency, tablet->filter.threshold * 100, tablet->filter.weight);
- } else {
- tablet->filter.isEnabled = false;
- LOG_INFO("Filter = off\n");
+ if (tablet->smoothing.weight < 1.0) {
+ tablet->smoothing.isEnabled = true;
+ LOG_INFO("Smoothing = %0.2f ms to reach %0.0f%% (weight = %f)\n", latency, tablet->smoothing.threshold * 100, tablet->smoothing.weight);
}
+ else {
+ tablet->smoothing.isEnabled = false;
+ LOG_INFO("Smoothing = off\n");
+ }
+ }
+
+
+ //
+ // Antichatter filter
+ //
+ else if (cmd->is("AntichatterEnabled")) {
+ bool AntichatterEnabled = (bool)cmd->GetInt(0, tablet->smoothing.AntichatterEnabled);
+ tablet->smoothing.AntichatterEnabled = AntichatterEnabled;
+ LOG_INFO("Filter Antichatter Enabled = %d \n", tablet->smoothing.AntichatterEnabled);
+ }
+ else if (cmd->is("AntichatterStrength")) {
+ double antichatterStrength = cmd->GetDouble(0, tablet->smoothing.antichatterStrength);
+ tablet->smoothing.antichatterStrength = antichatterStrength;
+ LOG_INFO("Filter Antichatter Stregth = %0.2f \n", tablet->smoothing.antichatterStrength);
+ }
+ else if (cmd->is("AntichatterMultiplier")) {
+ double antichatterMultiplier = cmd->GetDouble(0, tablet->smoothing.antichatterMultiplier);
+ tablet->smoothing.antichatterMultiplier = antichatterMultiplier;
+ LOG_INFO("Filter Antichatter Multiplier = %0.2f \n", tablet->smoothing.antichatterMultiplier);
+ }
+ else if (cmd->is("AntichatterOffsetX")) {
+ double antichatterOffsetX = cmd->GetDouble(0, tablet->smoothing.antichatterOffsetX);
+ tablet->smoothing.antichatterOffsetX = antichatterOffsetX;
+ LOG_INFO("Filter Antichatter Offset X = %0.2f cm\n", tablet->smoothing.antichatterOffsetX);
+ }
+ else if (cmd->is("AntichatterOffsetY")) {
+ double antichatterOffsetY = cmd->GetDouble(0, tablet->smoothing.antichatterOffsetY);
+ tablet->smoothing.antichatterOffsetY = antichatterOffsetY;
+ LOG_INFO("Filter Antichatter Offset Y = %0.2f cm\n", tablet->smoothing.antichatterOffsetY);
+ }
+
+ //
+ // Prediction
+ //
+ else if (cmd->is("PredictionEnabled")) {
+ bool PredictionEnabled = (bool)cmd->GetInt(0, tablet->smoothing.PredictionEnabled);
+ tablet->smoothing.PredictionEnabled = PredictionEnabled;
+ LOG_INFO("Filter Prediction Enabled = %d \n", tablet->smoothing.PredictionEnabled);
+ }
+ else if (cmd->is("PredictionSharpness")) {
+ double PredictionSharpness = cmd->GetDouble(0, tablet->smoothing.PredictionSharpness);
+ tablet->smoothing.PredictionSharpness = PredictionSharpness;
+ LOG_INFO("Filter Prediction Sharpness = %0.2f \n", tablet->smoothing.PredictionSharpness);
+ }
+ else if (cmd->is("PredictionStrength")) {
+ double PredictionStrength = cmd->GetDouble(0, tablet->smoothing.PredictionStrength);
+ tablet->smoothing.PredictionStrength = PredictionStrength;
+ LOG_INFO("Filter Prediction Strength = %0.2f x\n", tablet->smoothing.PredictionStrength);
}
+ else if (cmd->is("PredictionOffsetX")) {
+ double PredictionOffsetX = cmd->GetDouble(0, tablet->smoothing.PredictionOffsetX);
+ tablet->smoothing.PredictionOffsetX = PredictionOffsetX;
+ LOG_INFO("Filter Prediction Offset X = %0.2f cm\n", tablet->smoothing.PredictionOffsetX);
+ }
+ else if (cmd->is("PredictionOffsetY")) {
+ double PredictionOffsetY = cmd->GetDouble(0, tablet->smoothing.PredictionOffsetY);
+ tablet->smoothing.PredictionOffsetY = PredictionOffsetY;
+ LOG_INFO("Filter Prediction Offset Y = %0.2f cm\n", tablet->smoothing.PredictionOffsetY);
+ }
+
+
//
// Smoothing filter interval
//
- else if(cmd->is("FilterInterval")) {
- int interval = cmd->GetInt(0, (int)round(tablet->filter.interval));
+ else if (cmd->is("SmoothingInterval")) {
+ int interval = cmd->GetInt(0, (int)round(tablet->smoothing.timerInterval));
// 10 Hz
- if(interval > 100) interval = 100;
+ if (interval > 100) interval = 100;
// 1000 Hz
- if(interval < 1) interval = 1;
+ if (interval < 1) interval = 1;
// Interval changed?
- if(interval != (int)round(tablet->filter.interval)) {
- tablet->filter.interval = interval;
- tablet->SetFilterLatency(tablet->filter.latency);
- if(tablet->StopFilterTimer()) {
- tablet->StartFilterTimer();
+ if (interval != (int)round(tablet->smoothing.timerInterval)) {
+ tablet->smoothing.timerInterval = interval;
+ tablet->smoothing.SetLatency(tablet->smoothing.latency);
+ if (tablet->smoothing.StopTimer()) {
+ tablet->smoothing.StartTimer();
+ }
+ }
+
+ LOG_INFO("Smoothing Interval = %d (%0.2f Hz, %0.2f ms, %f)\n", interval, 1000.0 / interval, tablet->smoothing.latency, tablet->smoothing.weight);
+
+ }
+
+
+ //
+ // Noise reduction filter
+ //
+ else if (cmd->is("Noise")) {
+
+ string stringValue = cmd->GetStringLower(0, "");
+
+ // Off / False
+ if (stringValue == "off" || stringValue == "false") {
+ tablet->noise.isEnabled = false;
+ LOG_INFO("Noise Reduction = off\n");
+
+ // Set parameters
+ }
+ else {
+
+ int length = cmd->GetInt(0, tablet->noise.buffer.length);
+ double distanceThreshold = cmd->GetDouble(1, tablet->noise.distanceThreshold);
+ int iterations = cmd->GetInt(2, tablet->noise.iterations);
+
+ // Limits
+ if (length < 0) length = 0;
+ else if (length > 50) length = 50;
+
+ if (distanceThreshold < 0) distanceThreshold = 0;
+ else if (distanceThreshold > 100) distanceThreshold = 100;
+
+ if (iterations < 1) iterations = 1;
+ else if (iterations > 100) iterations = 100;
+
+ // Set
+ tablet->noise.buffer.SetLength(length);
+ tablet->noise.distanceThreshold = distanceThreshold;
+ tablet->noise.iterations = iterations;
+
+ // Enable filter
+ if (tablet->noise.buffer.length > 0) {
+ tablet->noise.isEnabled = true;
+ LOG_INFO("Noise Reduction = %d packets, %0.3f mm threshold, %d iterations\n", length, distanceThreshold, iterations);
}
+ else {
+ tablet->noise.isEnabled = false;
+ LOG_INFO("Noise Reduction = off\n");
+ }
+
}
- LOG_INFO("Filter Interval = %d (%0.2f Hz, %0.2f ms, %f)\n", interval, 1000.0 / interval, tablet->filter.latency, tablet->filter.weight);
+
+
}
// Debug
- else if(cmd->is("Debug")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("Debug")) {
+ if (!CheckTablet()) return true;
tablet->debugEnabled = cmd->GetBoolean(0, tablet->debugEnabled);
//vmulti->debugEnabled = tablet->debugEnabled;
LOG_INFO("Tablet debug = %s\n", tablet->debugEnabled ? "True" : "False");
@@ -489,27 +679,29 @@ bool ProcessCommand(CommandLine *cmd) {
// Log
- else if(cmd->is("Log") && cmd->valueCount > 0) {
+ else if (cmd->is("Log") && cmd->valueCount > 0) {
string logPath = cmd->GetString(0, "log.txt");
- if(!cmd->GetBoolean(0, true)) {
+ if (!cmd->GetBoolean(0, true)) {
logger.CloseLogFile();
LOG_INFO("Log file '%s' closed.\n", logger.logFilename.c_str());
- } else if(logger.OpenLogFile(logPath)) {
+ }
+ else if (logger.OpenLogFile(logPath)) {
LOG_INFO("Log file '%s' opened.\n", logPath.c_str());
- } else {
+ }
+ else {
LOG_ERROR("Cant open log file!\n");
}
}
// Wait
- else if(cmd->is("Wait")) {
+ else if (cmd->is("Wait")) {
int waitTime = cmd->GetInt(0, 0);
Sleep(waitTime);
}
- // Log
- else if(cmd->is("LogDirect")) {
+ // Direct logging
+ else if (cmd->is("LogDirect")) {
logger.ProcessMessages();
logger.directPrint = cmd->GetBoolean(0, logger.directPrint);
logger.ProcessMessages();
@@ -519,67 +711,83 @@ bool ProcessCommand(CommandLine *cmd) {
// Output
- else if(cmd->is("Output")) {
+ else if (cmd->is("Output")) {
vmulti->outputEnabled = cmd->GetBoolean(0, vmulti->outputEnabled);
LOG_INFO("Output enabled = %s\n", vmulti->outputEnabled ? "True" : "False");
}
// Info
- else if(cmd->is("Info")) {
- if(!CheckTablet()) return true;
+ else if (cmd->is("Info")) {
+ if (!CheckTablet()) return true;
LogInformation();
}
- // Info
- else if(cmd->is("Status")) {
- if(!CheckTablet()) return true;
+ // Status
+ else if (cmd->is("Status")) {
+ if (!CheckTablet()) return true;
LogStatus();
}
- // Info
- else if(cmd->is("Benchmark") || cmd->is("Bench")) {
- if(!CheckTablet()) return true;
+ // Benchmark
+ else if (cmd->is("Benchmark") || cmd->is("Bench")) {
+ if (!CheckTablet()) return true;
int timeLimit;
int packetCount = cmd->GetInt(0, 200);
- if(packetCount < 10) packetCount = 10;
- if(packetCount > 1000) packetCount = 1000;
+
+ // Limit packet count
+ if (packetCount < 10) packetCount = 10;
+ if (packetCount > 1000) packetCount = 1000;
+
+ // Time limit
timeLimit = packetCount * 10;
- if(timeLimit < 1000) timeLimit = 1000;
+ if (timeLimit < 1000) timeLimit = 1000;
- LOG_INFO("Tablet benchmark starting in 3 seconds!\n");
- LOG_INFO("Keep the pen stationary on top of the tablet!\n");
+ // Log
+ LOG_DEBUG("Tablet benchmark starting in 3 seconds!\n");
+ LOG_DEBUG("Keep the pen stationary on top of the tablet!\n");
Sleep(3000);
- LOG_INFO("Benchmark started!\n");
- tablet->StartBenchmark(packetCount);
+ LOG_DEBUG("Benchmark started!\n");
+
+ // Benchmark
+ tablet->benchmark.Start(packetCount);
- // Log the benchmark result
- for(int i = 0; i < timeLimit / 100; i++) {
+ // Wait for the benchmark to finish
+ for (int i = 0; i < timeLimit / 100; i++) {
Sleep(100);
- if(tablet->benchmark.packetCounter <= 0) {
+
+ // Benchmark result
+ if (tablet->benchmark.packetCounter <= 0) {
double width = tablet->benchmark.maxX - tablet->benchmark.minX;
double height = tablet->benchmark.maxY - tablet->benchmark.minY;
- LOG_INFO("Benchmark done!\n");
- LOG_INFO("Results from %d tablet positions:\n", tablet->benchmark.totalPackets);
- LOG_INFO(" X range: %0.3f mm <-> %0.3f mm\n", tablet->benchmark.minX, tablet->benchmark.maxX);
- LOG_INFO(" Y range: %0.3f mm <-> %0.3f mm\n", tablet->benchmark.minY, tablet->benchmark.maxY);
- LOG_INFO(" Width: %0.3f mm, %0.2f pixels @ %0.0f px, %0.2f mm\n",
- width,
- mapper->areaScreen.width / mapper->areaTablet.width * width,
+ LOG_DEBUG("\n");
+ LOG_DEBUG("Benchmark result (%d positions):\n", tablet->benchmark.totalPackets);
+ LOG_DEBUG(" Tablet: %s\n", tablet->name.c_str());
+ LOG_DEBUG(" Area: %0.2f mm x %0.2f mm (%0.0f px x %0.0f px)\n",
+ mapper->areaTablet.width,
+ mapper->areaTablet.height,
mapper->areaScreen.width,
- mapper->areaTablet.width
+ mapper->areaScreen.height
+ );
+ LOG_DEBUG(" X range: %0.3f mm <-> %0.3f mm\n", tablet->benchmark.minX, tablet->benchmark.maxX);
+ LOG_DEBUG(" Y range: %0.3f mm <-> %0.3f mm\n", tablet->benchmark.minY, tablet->benchmark.maxY);
+ LOG_DEBUG(" Width: %0.3f mm (%0.2f px)\n",
+ width,
+ mapper->areaScreen.width / mapper->areaTablet.width * width
);
- LOG_INFO(" Height: %0.3f mm, %0.2f pixels @ %0.0f px, %0.2f mm\n",
+ LOG_DEBUG(" Height: %0.3f mm (%0.2f px)\n",
height,
- mapper->areaScreen.height / mapper->areaTablet.height* height,
- mapper->areaScreen.height,
- mapper->areaTablet.height
+ mapper->areaScreen.height / mapper->areaTablet.height* height
);
+ LOG_DEBUG("\n");
+ LOG_STATUS("BENCHMARK %d %0.3f %0.3f %s\n", tablet->benchmark.totalPackets, width, height, tablet->name.c_str());
break;
}
}
- if(tablet->benchmark.packetCounter > 0) {
+
+ // Benchmark failed
+ if (tablet->benchmark.packetCounter > 0) {
LOG_ERROR("Benchmark failed!\n");
LOG_ERROR("Not enough packets captured in %0.2f seconds!\n",
timeLimit / 1000.0
@@ -590,14 +798,16 @@ bool ProcessCommand(CommandLine *cmd) {
}
- // Info
- else if(cmd->is("Include")) {
+ // Include
+ else if (cmd->is("Include")) {
string filename = cmd->GetString(0, "");
- if(filename == "") {
+ if (filename == "") {
LOG_ERROR("Invalid filename '%s'!\n", filename.c_str());
- } else {
- if(ReadCommandFile(filename)) {
- } else {
+ }
+ else {
+ if (ReadCommandFile(filename)) {
+ }
+ else {
LOG_ERROR("Can't open file '%s'\n", filename.c_str());
}
}
@@ -605,14 +815,14 @@ bool ProcessCommand(CommandLine *cmd) {
// Exit
- else if(cmd->is("Exit") || cmd->is("Quit")) {
+ else if (cmd->is("Exit") || cmd->is("Quit")) {
LOG_INFO("Bye!\n");
CleanupAndExit(0);
}
// Unknown
- else if(cmd->isValid) {
+ else if (cmd->isValid) {
LOG_WARNING("Unknown command: %s\n", cmd->line.c_str());
}
@@ -631,7 +841,7 @@ bool ReadCommandFile(string filename) {
// Open config file
file.open(filename);
- if(!file.is_open()) {
+ if (!file.is_open()) {
return false;
}
@@ -639,15 +849,15 @@ bool ReadCommandFile(string filename) {
LOG_INFO("\\ Reading '%s'\n", filename.c_str());
// Loop through lines
- while(!file.eof()) {
+ while (!file.eof()) {
getline(file, line);
- if(line.length() == 0) continue;
+ if (line.length() == 0) continue;
cmd = new CommandLine(line);
//
// Do not redefine tablet if one is already open
//
- if(cmd->is("Tablet") && tablet != NULL && tablet->IsConfigured()) {
+ if (cmd->is("Tablet") && tablet != NULL && tablet->IsConfigured()) {
LOG_INFO(">> %s\n", cmd->line.c_str());
LOG_INFO("Tablet is already defined!\n");
delete cmd;
@@ -684,18 +894,19 @@ void LogInformation() {
LOG_INFO(" Keep Tip Down = %d packets\n", tablet->settings.keepTipDown);
LOG_INFO(" Report Id = %02X\n", tablet->settings.reportId);
LOG_INFO(" Report Length = %d bytes\n", tablet->settings.reportLength);
- LOG_INFO(" Button Mask = 0x%02X\n", tablet->settings.buttonMask);
+ LOG_INFO(" Detect Mask = 0x%02X\n", tablet->settings.detectMask);
+ LOG_INFO(" Ignore Mask = 0x%02X\n", tablet->settings.ignoreMask);
- for(int i = 0; i < 8; i++) {
+ for (int i = 0; i < 8; i++) {
stringIndex += sprintf_s(stringBuffer + stringIndex, maxLength - stringIndex, "%d ", tablet->buttonMap[i]);
}
LOG_INFO(" Button Map = %s\n", stringBuffer);
- if(tablet->initFeatureLength > 0) {
+ if (tablet->initFeatureLength > 0) {
LOG_INFOBUFFER(tablet->initFeature, tablet->initFeatureLength, " Tablet init feature report: ");
}
- if(tablet->initReportLength > 0) {
+ if (tablet->initReportLength > 0) {
LOG_INFOBUFFER(tablet->initReport, tablet->initReportLength, " Tablet init report: ");
}
LOG_INFO("\n");
@@ -727,14 +938,15 @@ void LogInformation() {
void LogStatus() {
LOG_STATUS("TABLET %s\n", tablet->name.c_str());
- if(tablet->hidDevice != NULL) {
+ if (tablet->hidDevice != NULL) {
LOG_STATUS("HID %04X %04X %04X %04X\n",
tablet->hidDevice->vendorId,
tablet->hidDevice->productId,
tablet->hidDevice->usagePage,
tablet->hidDevice->usage
);
- } else if(tablet->usbDevice != NULL) {
+ }
+ else if (tablet->usbDevice != NULL) {
LOG_STATUS("USB %d %s\n",
tablet->usbDevice->stringId,
tablet->usbDevice->stringMatch.c_str()
@@ -762,7 +974,7 @@ void LogTabletArea(string text) {
bool CheckTablet() {
- if(tablet == NULL) {
+ if (tablet == NULL) {
return false;
}
return true;
diff --git a/TabletDriverService/ScreenMapper.cpp b/TabletDriverService/ScreenMapper.cpp
index 0af5318..28421e5 100644
--- a/TabletDriverService/ScreenMapper.cpp
+++ b/TabletDriverService/ScreenMapper.cpp
@@ -121,10 +121,10 @@ bool ScreenMapper::GetScreenPosition(double *x, double *y) {
mapY += areaScreen.y;
// Limit cursor to screen area
- if(mapX < areaScreen.x + 1) mapX = areaScreen.x + 1;
- if(mapY < areaScreen.y + 1) mapY = areaScreen.y + 1;
- if(mapX > areaScreen.x + areaScreen.width) mapX = areaScreen.x + areaScreen.width;
- if(mapY > areaScreen.y + areaScreen.height) mapY = areaScreen.y + areaScreen.height;
+ if (mapX < areaScreen.x + 1) mapX = areaScreen.x + 1;
+ if (mapY < areaScreen.y + 1) mapY = areaScreen.y + 1;
+ if (mapX > areaScreen.x + areaScreen.width) mapX = areaScreen.x + areaScreen.width;
+ if (mapY > areaScreen.y + areaScreen.height) mapY = areaScreen.y + areaScreen.height;
// Normalize screen area
@@ -132,8 +132,8 @@ bool ScreenMapper::GetScreenPosition(double *x, double *y) {
mapY /= areaVirtualScreen.height;
// Limit values
- if(mapX > 1) mapX = 1;
- if(mapY > 1) mapY = 1;
+ if (mapX > 1) mapX = 1;
+ if (mapY > 1) mapY = 1;
// Set pointer values
*x = mapX;
diff --git a/TabletDriverService/Tablet.cpp b/TabletDriverService/Tablet.cpp
index 3c37f0c..27ff94f 100644
--- a/TabletDriverService/Tablet.cpp
+++ b/TabletDriverService/Tablet.cpp
@@ -9,12 +9,12 @@
// USB Device Constructor
//
Tablet::Tablet(string usbGUID, int stringId, string stringMatch) : Tablet() {
- //_construct();
usbDevice = new USBDevice(usbGUID, stringId, stringMatch);
- if(usbDevice->isOpen) {
+ if (usbDevice->isOpen) {
this->isOpen = true;
usbPipeId = 0x81;
- } else {
+ }
+ else {
delete usbDevice;
usbDevice = NULL;
}
@@ -24,11 +24,11 @@ Tablet::Tablet(string usbGUID, int stringId, string stringMatch) : Tablet() {
// HID Device Constructor
//
Tablet::Tablet(USHORT vendorId, USHORT productId, USHORT usagePage, USHORT usage) : Tablet() {
- //_construct();
hidDevice = new HIDDevice(vendorId, productId, usagePage, usage);
- if(hidDevice->isOpen) {
+ if (hidDevice->isOpen) {
this->isOpen = true;
- } else {
+ }
+ else {
delete hidDevice;
hidDevice = NULL;
}
@@ -46,31 +46,6 @@ Tablet::Tablet() {
usbPipeId = 0;
- // Initial settings
- settings.reportId = 0;
- settings.reportLength = 8;
- settings.buttonMask = 0x00;
- settings.maxX = 1;
- settings.maxY = 1;
- settings.maxPressure = 1;
- settings.clickPressure = 0;
- settings.width = 1;
- settings.height = 1;
- settings.skew = 0;
- settings.type = TabletNormal;
-
- // Initial filter settings
- filter.timer = NULL;
- filter.interval = 2.0;
- filter.latency = 2.0;
- filter.weight = 1.000;
- filter.threshold = 0.9;
- filter.isEnabled = false;
- filter.targetX = 0;
- filter.targetY = 0;
- filter.x = 0;
- filter.y = 0;
-
// Init reports
initFeature = NULL;
initFeatureLength = 0;
@@ -80,8 +55,14 @@ Tablet::Tablet() {
// Reset state
memset(&state, 0, sizeof(state));
- // Reset benchmark
- memset(&benchmark, 0, sizeof(benchmark));
+ // Filters
+ filterTimed[0] = &smoothing;
+ filterTimedCount = 1;
+ filterPacket[0] = &noise;
+ //filterPacket[1] = &peak;
+ filterPacketCount = 1;
+
+ peak.isEnabled = true;
// Button map
memset(&buttonMap, 0, sizeof(buttonMap));
@@ -108,15 +89,15 @@ Tablet::Tablet() {
//
Tablet::~Tablet() {
CloseDevice();
- if(usbDevice != NULL)
+ if (usbDevice != NULL)
delete usbDevice;
- if(hidDevice != NULL)
+ if (hidDevice != NULL)
delete hidDevice;
- if(hidDevice2 != NULL)
+ if (hidDevice2 != NULL)
delete hidDevice2;
- if(initReport != NULL)
+ if (initReport != NULL)
delete initReport;
- if(initFeature != NULL)
+ if (initFeature != NULL)
delete initFeature;
}
@@ -127,31 +108,38 @@ Tablet::~Tablet() {
bool Tablet::Init() {
// Feature report
- if(initFeature != NULL) {
- if(hidDevice->SetFeature(initFeature, initFeatureLength)) {
+ if (initFeature != NULL) {
+ if (hidDevice->SetFeature(initFeature, initFeatureLength)) {
return true;
}
return false;
}
// Output report
- if(initReport != NULL) {
- if(hidDevice->Write(initReport, initReportLength)) {
+ if (initReport != NULL) {
+ if (hidDevice->Write(initReport, initReportLength)) {
return true;
}
return false;
}
// USB (Huion)
- if(usbDevice != NULL) {
+ if (usbDevice != NULL) {
BYTE buffer[64];
- if(usbDevice->ControlTransfer(0x80, 0x06, (0x03 << 8) | 100, 0x0409, buffer, 64) > 0) {
+ int status;
+
+ // String Id 200
+ status = usbDevice->ControlTransfer(0x80, 0x06, (0x03 << 8) | 200, 0x0409, buffer, 64);
+
+ // String Id 100
+ status += usbDevice->ControlTransfer(0x80, 0x06, (0x03 << 8) | 100, 0x0409, buffer, 64);
+
+ if (status > 0) {
return true;
}
return false;
}
-
return true;
}
@@ -160,232 +148,154 @@ bool Tablet::Init() {
// Check if the tablet has enough configuration parameters set
//
bool Tablet::IsConfigured() {
- if(
+ if (
settings.maxX > 1 &&
settings.maxY > 1 &&
settings.maxPressure > 1 &&
settings.width > 1 &&
settings.height > 1
- ) return true;
+ ) return true;
return false;
}
-
-//
-// Calculate filter latency
-//
-double Tablet::GetFilterLatency(double filterWeight, double interval, double threshold) {
- double target = 1 - threshold;
- double stepCount = -log(1 / target) / log(1 - filterWeight);
- return stepCount * interval;
-}
-double Tablet::GetFilterLatency(double filterWeight) {
- return this->GetFilterLatency(filterWeight, filter.interval, filter.threshold);
-}
-double Tablet::GetFilterLatency() {
- return this->GetFilterLatency(filter.weight, filter.interval, filter.threshold);
-}
-
-
-//
-// Calculate filter weight
-//
-double Tablet::GetFilterWeight(double latency, double interval, double threshold) {
- double stepCount = latency / interval;
- double target = 1 - threshold;
- return 1 - 1 / pow(1 / target, 1 / stepCount);
-}
-double Tablet::GetFilterWeight(double latency) {
- return this->GetFilterWeight(latency, filter.interval, filter.threshold);
-}
-
-// Set filter values
-void Tablet::SetFilterLatency(double latency) {
- tablet->filter.weight = tablet->GetFilterWeight(latency);
- tablet->filter.latency = latency;
-}
-
-//
-// Process filter
-//
-void Tablet::ProcessFilter() {
-
- double deltaX, deltaY, distance;
-
- deltaX = filter.targetX - filter.x;
- deltaY = filter.targetY - filter.y;
- distance = sqrt(deltaX*deltaX + deltaY * deltaY);
-
- // Distance large enough?
- if(distance > 0.01) {
- filter.x += deltaX * filter.weight;
- filter.y += deltaY * filter.weight;
-
- // Too small distance -> set output values as target values
- } else {
- filter.x = filter.targetX;
- filter.y = filter.targetY;
- }
-
-}
-
-
-//
-// Start Filter Timer
-//
-bool Tablet::StartFilterTimer() {
- return CreateTimerQueueTimer(
- &filter.timer,
- NULL, filter.callback,
- NULL,
- 0,
- (int)filter.interval,
- WT_EXECUTEDEFAULT
- );
-}
-
-
-//
-// Stop Filter Timer
-//
-bool Tablet::StopFilterTimer() {
- if(tablet->filter.timer == NULL) return false;
- bool result = DeleteTimerQueueTimer(NULL, filter.timer, NULL);
- if(result) {
- filter.timer = NULL;
- }
- return result;
-}
-
-
-//
-// Start tablet benchmark
-//
-void Tablet::StartBenchmark(int packetCount) {
- tablet->benchmark.maxX = -10000;
- tablet->benchmark.maxY = -10000;
- tablet->benchmark.minX = 10000;
- tablet->benchmark.minY = 10000;
- tablet->benchmark.totalPackets = packetCount;
- tablet->benchmark.packetCounter = packetCount;
-}
-
-
-
//
// Read Position
//
int Tablet::ReadPosition() {
- UCHAR buffer[256];
+ UCHAR buffer[1024];
+ UCHAR *data;
int buttonIndex;
// Read report
- if(!this->Read(buffer, settings.reportLength)) {
+ if (!this->Read(buffer, settings.reportLength)) {
return -1;
}
// Skip packets
- if(skipPackets > 0) {
+ if (skipPackets > 0) {
skipPackets--;
return Tablet::PacketInvalid;
}
- // Validate packet id
- if(settings.reportId > 0 && buffer[0] != settings.reportId) {
- return Tablet::PacketInvalid;
+
+ // Set data pointer
+ if (settings.type == TabletSettings::TypeWacomDrivers) {
+ data = buffer + 1;
+ }
+ else {
+ data = buffer;
}
//
// Wacom Intuos data format
//
- if(settings.type == TypeWacomIntuos) {
- reportData.x = ((buffer[2] * 0x100 + buffer[3]) << 1) | ((buffer[9] >> 1) & 1);
- reportData.y = ((buffer[4] * 0x100 + buffer[5]) << 1) | (buffer[9] & 1);
- reportData.pressure = (buffer[6] << 3) | ((buffer[7] & 0xC0) >> 5) | (buffer[1] & 1);
- reportData.reportId = buffer[0];
- reportData.buttons = buffer[1] & ~0x01;
+ if (settings.type == TabletSettings::TypeWacomIntuos) {
+ reportData.reportId = data[0];
+ reportData.buttons = data[1] & ~0x01;
+ reportData.x = ((data[2] * 0x100 + data[3]) << 1) | ((data[9] >> 1) & 1);
+ reportData.y = ((data[4] * 0x100 + data[5]) << 1) | (data[9] & 1);
+ reportData.pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5) | (data[1] & 1);
//distance = buffer[9] >> 2;
//
- // Copy buffer to struct
+ // Wacom 4100 data format
//
- } else {
- memcpy(&reportData, buffer, sizeof(reportData));
+ }
+ else if (settings.type == TabletSettings::TypeWacom4100) {
+
+ // Wacom driver device
+ if (settings.reportLength == 193) {
+ data = buffer + 1;
+ }
+
+ reportData.reportId = data[0];
+ reportData.buttons = data[1] & ~0x01;
+ reportData.x = (data[2] | (data[3] << 8) | (data[4] << 16));
+ reportData.y = (data[5] | (data[6] << 8) | (data[7] << 16));
+ reportData.pressure = (data[8] | (data[9] << 8));
+
+ //
+ // Copy buffer to struct
+ //
+ }
+ else {
+ memcpy(&reportData, data, sizeof(reportData));
}
- // Validate position
- if(settings.buttonMask > 0 && (reportData.buttons & settings.buttonMask) != settings.buttonMask) {
+ // Validate packet id
+ if (settings.reportId > 0 && reportData.reportId != settings.reportId) {
+ return Tablet::PacketInvalid;
+ }
+
+
+
+ // Detect mask
+ if (settings.detectMask > 0 && (reportData.buttons & settings.detectMask) != settings.detectMask) {
+ return Tablet::PacketPositionInvalid;
+ }
+
+ // Ignore mask
+ if (settings.ignoreMask > 0 && (reportData.buttons & settings.ignoreMask) == settings.ignoreMask) {
return Tablet::PacketPositionInvalid;
}
//
// Use pen pressure to detect the pen tip click
//
- if(settings.clickPressure > 0) {
+ if (settings.clickPressure > 0) {
reportData.buttons &= ~1;
- if(reportData.pressure > settings.clickPressure) {
+ if (reportData.pressure > settings.clickPressure) {
reportData.buttons |= 1;
}
- // Force tip button if pressure is detected
- } else if(reportData.pressure > 10) {
+ // Force tip button down if pressure is detected
+ }
+ else if (reportData.pressure > 10) {
reportData.buttons |= 1;
}
// Keep pen tip button down for a few packets
- if(settings.keepTipDown > 0) {
- if(reportData.buttons & 0x01) {
+ if (settings.keepTipDown > 0) {
+ if (reportData.buttons & 0x01) {
tipDownCounter = settings.keepTipDown;
}
- if(tipDownCounter-- >= 0) {
+ if (tipDownCounter-- >= 0) {
reportData.buttons |= 1;
}
}
-
-
// Set valid
state.isValid = true;
// Button map
reportData.buttons = reportData.buttons & 0x0F;
state.buttons = 0;
- for(buttonIndex = 0; buttonIndex < sizeof(buttonMap); buttonIndex++) {
+ for (buttonIndex = 0; buttonIndex < sizeof(buttonMap); buttonIndex++) {
// Button is set
- if(buttonMap[buttonIndex] > 0) {
+ if (buttonMap[buttonIndex] > 0) {
// Button is pressed
- if((reportData.buttons & (1 << buttonIndex)) > 0) {
+ if ((reportData.buttons & (1 << buttonIndex)) > 0) {
state.buttons |= (1 << (buttonMap[buttonIndex] - 1));
}
}
}
// Convert report data to state
- state.x = ((double)reportData.x / (double)settings.maxX) * settings.width;
- state.y = ((double)reportData.y / (double)settings.maxY) * settings.height;
- if(settings.skew != 0) {
- state.x += state.y * settings.skew;
- }
+ state.position.x = ((double)reportData.x / (double)settings.maxX) * settings.width;
+ //state.position.x = ((double)data[8] / 256) * settings.width;
+ state.position.y = ((double)reportData.y / (double)settings.maxY) * settings.height;
+ state.z = (double)data[8];
+ if (settings.skew != 0)
+ state.position.x += state.position.y * settings.skew;
state.pressure = ((double)reportData.pressure / (double)settings.maxPressure);
- //
- // Tablet benchmark
- //
- if(benchmark.packetCounter > 0) {
-
- // Set min & max
- if(state.x < benchmark.minX) benchmark.minX = state.x;
- if(state.x > benchmark.maxX) benchmark.maxX = state.x;
- if(state.y < benchmark.minY) benchmark.minY = state.y;
- if(state.y > benchmark.maxY) benchmark.maxY = state.y;
-
- benchmark.packetCounter--;
- }
+ // Tablet benchmark update
+ benchmark.Update(state.position);
// Packet and position is valid
return Tablet::PacketValid;
@@ -396,14 +306,15 @@ int Tablet::ReadPosition() {
// Read report from tablet
//
bool Tablet::Read(void *buffer, int length) {
- if(!isOpen) return false;
+ if (!isOpen) return false;
bool status = false;
- if(usbDevice != NULL) {
+ if (usbDevice != NULL) {
status = usbDevice->Read(usbPipeId, buffer, length) > 0;
- } else if(hidDevice != NULL) {
+ }
+ else if (hidDevice != NULL) {
status = hidDevice->Read(buffer, length);
}
- if(debugEnabled && status) {
+ if (debugEnabled && status) {
LOG_DEBUGBUFFER(buffer, length, "Read: ");
}
return status;
@@ -413,10 +324,11 @@ bool Tablet::Read(void *buffer, int length) {
// Write report to the tablet
//
bool Tablet::Write(void *buffer, int length) {
- if(!isOpen) return false;
- if(usbDevice != NULL) {
+ if (!isOpen) return false;
+ if (usbDevice != NULL) {
return usbDevice->Write(usbPipeId, buffer, length) > 0;
- } else if(hidDevice != NULL) {
+ }
+ else if (hidDevice != NULL) {
return hidDevice->Write(buffer, length);
}
return false;
@@ -426,10 +338,11 @@ bool Tablet::Write(void *buffer, int length) {
// Close tablet
//
void Tablet::CloseDevice() {
- if(isOpen) {
- if(usbDevice != NULL) {
+ if (isOpen) {
+ if (usbDevice != NULL) {
usbDevice->CloseDevice();
- } else if(hidDevice != NULL) {
+ }
+ else if (hidDevice != NULL) {
hidDevice->CloseDevice();
}
}
diff --git a/TabletDriverService/Tablet.h b/TabletDriverService/Tablet.h
index 257d9cd..225f731 100644
--- a/TabletDriverService/Tablet.h
+++ b/TabletDriverService/Tablet.h
@@ -4,6 +4,12 @@
#include "USBDevice.h"
#include "HIDDevice.h"
+#include "TabletSettings.h"
+#include "TabletFilterSmoothing.h"
+#include "TabletFilterNoiseReduction.h"
+#include "TabletFilterPeak.h"
+#include "TabletBenchmark.h"
+#include "Vector2D.h"
using namespace std;
@@ -18,39 +24,18 @@ class Tablet {
//
// Enums
//
- enum TabletType {
- TabletNormal,
- TypeWacomIntuos
- };
enum TabletButtons {
Button1, Button2, Button3, Button4,
Button5, Button6, Button7, Button8
};
- enum TabletState {
+
+ // Tablet packet state
+ enum TabletPacketState {
PacketPositionInvalid = 0,
PacketValid = 1,
PacketInvalid = 2
};
- //
- // Settings
- //
- struct {
- BYTE buttonMask;
- int maxX;
- int maxY;
- int maxPressure;
- int clickPressure;
- int keepTipDown;
- double width;
- double height;
- BYTE reportId;
- int reportLength;
- double skew;
- TabletType type;
- } settings;
-
-
//
// Position report data
//
@@ -61,6 +46,7 @@ class Tablet {
USHORT x;
USHORT y;
USHORT pressure;
+ USHORT z;
} reportData;
//
@@ -69,39 +55,33 @@ class Tablet {
struct {
bool isValid;
BYTE buttons;
- double x;
- double y;
+ Vector2D position;
double pressure;
+ double z;
} state;
- //
- // Filter
- //
- struct {
- HANDLE timer;
- WAITORTIMERCALLBACK callback;
- double interval;
- double latency;
- double weight;
- double threshold;
- bool isEnabled;
- double targetX;
- double targetY;
- double x;
- double y;
- } filter;
+ // Settings
+ TabletSettings settings;
+
+ // Smoothing filter
+ TabletFilterSmoothing smoothing;
+
+ // Noise reduction filter
+ TabletFilterNoiseReduction noise;
+
+ // Peak filter
+ TabletFilterPeak peak;
+
+ // Timed filter
+ TabletFilter *filterTimed[10];
+ int filterTimedCount;
+
+ // Packet filter
+ TabletFilter *filterPacket[10];
+ int filterPacketCount;
- //
// Benchmark
- //
- struct {
- double minX;
- double maxX;
- double minY;
- double maxY;
- int totalPackets;
- int packetCounter;
- } benchmark;
+ TabletBenchmark benchmark;
// Button map
BYTE buttonMap[16];
@@ -129,18 +109,6 @@ class Tablet {
bool Init();
bool IsConfigured();
- double GetFilterLatency(double filterWeight, double interval, double threshold);
- double GetFilterLatency(double filterWeight);
- double GetFilterLatency();
- double GetFilterWeight(double latency, double interval, double threshold);
- double GetFilterWeight(double latency);
- void SetFilterLatency(double latency);
- void ProcessFilter();
- bool StartFilterTimer();
- bool StopFilterTimer();
-
- void StartBenchmark(int packetCount);
-
int ReadPosition();
bool Write(void *buffer, int length);
bool Read(void *buffer, int length);
diff --git a/TabletDriverService/TabletBenchmark.cpp b/TabletDriverService/TabletBenchmark.cpp
new file mode 100644
index 0000000..a2cc5dd
--- /dev/null
+++ b/TabletDriverService/TabletBenchmark.cpp
@@ -0,0 +1,49 @@
+#include "stdafx.h"
+#include "TabletBenchmark.h"
+
+//
+// Constructor
+//
+TabletBenchmark::TabletBenchmark() {
+ maxX = 0;
+ minX = 0;
+ maxY = 0;
+ minY = 0;
+ totalPackets = 0;
+ packetCounter = 0;
+}
+
+//
+// Destructor
+//
+TabletBenchmark::~TabletBenchmark() {
+}
+
+
+//
+// Start tablet benchmark
+//
+void TabletBenchmark::Start(int packetCount) {
+ maxX = -10000;
+ maxY = -10000;
+ minX = 10000;
+ minY = 10000;
+ totalPackets = packetCount;
+ packetCounter = packetCount;
+ isRunning = true;
+}
+
+void TabletBenchmark::Update(Vector2D position) {
+ if (isRunning) {
+ if (packetCounter > 0) {
+ if (position.x < minX) minX = position.x;
+ if (position.x > maxX) maxX = position.x;
+ if (position.y < minY) minY = position.y;
+ if (position.y > maxY) maxY = position.y;
+ packetCounter--;
+ }
+ else {
+ isRunning = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TabletDriverService/TabletBenchmark.h b/TabletDriverService/TabletBenchmark.h
new file mode 100644
index 0000000..500da44
--- /dev/null
+++ b/TabletDriverService/TabletBenchmark.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Vector2D.h"
+
+class TabletBenchmark {
+public:
+ double minX;
+ double maxX;
+ double minY;
+ double maxY;
+ int totalPackets;
+ int packetCounter;
+ bool isRunning;
+
+ TabletBenchmark();
+ ~TabletBenchmark();
+ void Start(int packetCount);
+ void Update(Vector2D position);
+};
+
diff --git a/TabletDriverService/TabletDriverService.vcxproj b/TabletDriverService/TabletDriverService.vcxproj
index b176edc..b6c014d 100644
--- a/TabletDriverService/TabletDriverService.vcxproj
+++ b/TabletDriverService/TabletDriverService.vcxproj
@@ -23,7 +23,7 @@
{3101CEC2-8F39-45FD-943B-79A488AD05EA}
Win32Proj
TabletDriverService
- 10.0.16299.0
+ 10.0.17763.0
@@ -171,21 +171,36 @@ xcopy /C /Y "$(ProjectDir)config\init.cfg" "$(SolutionDir)TabletDriverGUI\bin\De
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create
@@ -194,6 +209,7 @@ xcopy /C /Y "$(ProjectDir)config\init.cfg" "$(SolutionDir)TabletDriverGUI\bin\De
Create
+
diff --git a/TabletDriverService/TabletDriverService.vcxproj.filters b/TabletDriverService/TabletDriverService.vcxproj.filters
index 5f7116d..4b88f4e 100644
--- a/TabletDriverService/TabletDriverService.vcxproj.filters
+++ b/TabletDriverService/TabletDriverService.vcxproj.filters
@@ -45,6 +45,30 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -77,6 +101,30 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
diff --git a/TabletDriverService/TabletFilter.cpp b/TabletDriverService/TabletFilter.cpp
new file mode 100644
index 0000000..5b21383
--- /dev/null
+++ b/TabletDriverService/TabletFilter.cpp
@@ -0,0 +1,50 @@
+#include "stdafx.h"
+#include "TabletFilter.h"
+
+
+TabletFilter::TabletFilter() {
+ timer = NULL;
+ timerInterval = 2;
+ isValid = false;
+ isEnabled = false;
+}
+
+//
+// Start Timer
+//
+bool TabletFilter::StartTimer() {
+ if (timer == NULL) {
+ MMRESULT result = timeSetEvent(
+ (UINT)timerInterval,//UINT uDelay,
+ 0,//UINT uResolution,
+ callback, //LPTIMECALLBACK lpTimeProc,
+ NULL, //DWORD_PTR dwUser,
+ TIME_PERIODIC | TIME_KILL_SYNCHRONOUS //UINT fuEvent
+ );
+ if (result == NULL) {
+ return false;
+ }
+ else {
+ timer = (HANDLE)1; // for code compatibility purposes
+ uTimerID = result;
+ }
+ }
+ return true;
+}
+
+
+//
+// Stop Timer
+//
+bool TabletFilter::StopTimer() {
+ if (timer == NULL) return false;
+
+ MMRESULT result = timeKillEvent(uTimerID);
+ // Returns TIMERR_NOERROR if successful or MMSYSERR_INVALPARAM if the specified timer event does not exist.
+ if (result == TIMERR_NOERROR)
+ {
+ timer = NULL;
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/TabletDriverService/TabletFilter.h b/TabletDriverService/TabletFilter.h
new file mode 100644
index 0000000..1991acf
--- /dev/null
+++ b/TabletDriverService/TabletFilter.h
@@ -0,0 +1,24 @@
+#pragma once
+class TabletFilter {
+public:
+ virtual void SetTarget(Vector2D vector, double h) = 0;
+ virtual void Reset(Vector2D vector) = 0;
+ virtual void SetPosition(Vector2D vector, double h) = 0;
+ virtual bool GetPosition(Vector2D *vector) = 0;
+ virtual void Update() = 0;
+
+ HANDLE timer;
+ UINT uTimerID;
+ LPTIMECALLBACK callback;
+ double timerInterval;
+
+ bool isEnabled;
+ bool isValid;
+
+ TabletFilter();
+
+ bool StartTimer();
+ bool StopTimer();
+
+};
+
diff --git a/TabletDriverService/TabletFilterNoiseReduction.cpp b/TabletDriverService/TabletFilterNoiseReduction.cpp
new file mode 100644
index 0000000..c29aced
--- /dev/null
+++ b/TabletDriverService/TabletFilterNoiseReduction.cpp
@@ -0,0 +1,156 @@
+#include "stdafx.h"
+#include "TabletFilterNoiseReduction.h"
+
+
+//
+// Constructor
+//
+TabletFilterNoiseReduction::TabletFilterNoiseReduction() {
+ distanceThreshold = 0;
+ iterations = 10;
+}
+
+//
+// Destructor
+//
+TabletFilterNoiseReduction::~TabletFilterNoiseReduction() {
+}
+
+//
+// TabletFilter methods
+//
+
+// Reset
+void TabletFilterNoiseReduction::Reset(Vector2D position) {
+ lastTarget.Set(position);
+ buffer.Reset();
+}
+
+// Set target position
+void TabletFilterNoiseReduction::SetTarget(Vector2D targetVector, double h) {
+ lastTarget.Set(targetVector);
+ buffer.Add(targetVector);
+}
+
+// Set position
+void TabletFilterNoiseReduction::SetPosition(Vector2D vector, double h) {
+ position.x = vector.x;
+ position.y = vector.y;
+}
+
+// Get position
+bool TabletFilterNoiseReduction::GetPosition(Vector2D *outputVector) {
+ outputVector->x = position.x;
+ outputVector->y = position.y;
+ return true;
+}
+
+// Update
+void TabletFilterNoiseReduction::Update() {
+
+ // One position in the buffer?
+ if (buffer.count == 1) {
+ position.x = buffer[0]->x;
+ position.y = buffer[0]->y;
+ return;
+ }
+
+ // Calculate geometric median from the buffer positions
+ GetGeometricMedianVector(&position, iterations);
+
+ // Reset the buffer when distance to last target position is larger than the threshold
+ if (buffer.isValid) {
+ double distance = lastTarget.Distance(position);
+ if (distance > distanceThreshold) {
+ buffer.Reset();
+ position.Set(lastTarget);
+ }
+ }
+}
+
+//
+// Average Position Vector
+//
+bool TabletFilterNoiseReduction::GetAverageVector(Vector2D *output) {
+ double x, y;
+ if (!buffer.isValid) return false;
+
+ x = y = 0;
+ for (int i = 0; i < buffer.count; i++) {
+ x += buffer[i]->x;
+ y += buffer[i]->y;
+ }
+ output->x = x / buffer.count;
+ output->y = y / buffer.count;
+ return true;
+}
+
+//
+// Geometric Median Position Vector
+//
+bool TabletFilterNoiseReduction::GetGeometricMedianVector(Vector2D *output, int iterations) {
+
+ Vector2D candidate, next;
+ double minimumDistance = 0.001;
+
+ double denominator, dx, dy, distance, weight;
+ int i;
+
+ // Calculate the starting position
+ if (GetAverageVector(&candidate)) {
+ }
+ else {
+ return false;
+ }
+
+ // Iterate
+ for (int iteration = 0; iteration < iterations; iteration++) {
+
+ denominator = 0;
+
+ // Loop through the buffer and calculate a denominator.
+ for (i = 0; i < buffer.count; i++) {
+ dx = candidate.x - buffer[i]->x;
+ dy = candidate.y - buffer[i]->y;
+ distance = sqrt(dx*dx + dy * dy);
+ if (distance > minimumDistance) {
+ denominator += 1.0 / distance;
+ }
+ else {
+ denominator += 1.0 / minimumDistance;
+ }
+ }
+
+ // Reset the next vector
+ next.x = 0;
+ next.y = 0;
+
+ // Loop through the buffer and calculate a weighted average
+ for (i = 0; i < buffer.count; i++) {
+ dx = candidate.x - buffer[i]->x;
+ dy = candidate.y - buffer[i]->y;
+ distance = sqrt(dx*dx + dy * dy);
+ if (distance > minimumDistance) {
+ weight = 1.0 / distance;
+ }
+ else {
+ weight = 1.0 / minimumDistance;
+ }
+
+ next.x += buffer[i]->x * weight / denominator;
+ next.y += buffer[i]->y * weight / denominator;
+ }
+
+ // Set the new candidate vector
+ candidate.x = next.x;
+ candidate.y = next.y;
+ }
+
+ // Set output
+ output->x = candidate.x;
+ output->y = candidate.y;
+
+ return true;
+
+}
+
diff --git a/TabletDriverService/TabletFilterNoiseReduction.h b/TabletDriverService/TabletFilterNoiseReduction.h
new file mode 100644
index 0000000..4afb7d9
--- /dev/null
+++ b/TabletDriverService/TabletFilterNoiseReduction.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "PositionRingBuffer.h"
+
+class TabletFilterNoiseReduction : public TabletFilter {
+public:
+
+ PositionRingBuffer buffer;
+ Vector2D position;
+ Vector2D lastTarget;
+
+ int iterations;
+ double distanceThreshold;
+
+ void Reset(Vector2D position);
+ void SetTarget(Vector2D targetVector, double h);
+ void SetPosition(Vector2D vector, double h);
+ bool GetPosition(Vector2D *outputVector);
+ void Update();
+
+ bool GetAverageVector(Vector2D *output);
+ bool GetGeometricMedianVector(Vector2D *output, int iterations);
+
+ TabletFilterNoiseReduction();
+ ~TabletFilterNoiseReduction();
+};
+
diff --git a/TabletDriverService/TabletFilterPeak.cpp b/TabletDriverService/TabletFilterPeak.cpp
new file mode 100644
index 0000000..9f69cc3
--- /dev/null
+++ b/TabletDriverService/TabletFilterPeak.cpp
@@ -0,0 +1,81 @@
+#include "stdafx.h"
+#include "TabletFilterPeak.h"
+
+#define LOG_MODULE "Peak"
+#include "Logger.h"
+
+//
+// Constructor
+//
+TabletFilterPeak::TabletFilterPeak() {
+
+ // Buffer length
+ buffer.SetLength(3);
+
+ // Default distance threshold
+ distanceThreshold = 10;
+}
+
+
+//
+// Destructor
+//
+TabletFilterPeak::~TabletFilterPeak() {
+}
+
+
+//
+// TabletFilter methods
+//
+// reset
+void TabletFilterPeak::Reset(Vector2D targetVector) {
+ buffer.Reset();
+}
+// Set target position
+void TabletFilterPeak::SetTarget(Vector2D targetVector, double h) {
+ buffer.Add(targetVector);
+}
+// Set position
+void TabletFilterPeak::SetPosition(Vector2D vector, double h) {
+ position.x = vector.x;
+ position.y = vector.y;
+}
+// Get position
+bool TabletFilterPeak::GetPosition(Vector2D *outputVector) {
+ outputVector->x = position.x;
+ outputVector->y = position.y;
+ return true;
+}
+// Update
+void TabletFilterPeak::Update() {
+ Vector2D oldPosition;
+ double distance;
+
+ // Buffer valid?
+ if (
+ buffer.GetLatest(&oldPosition, -1)
+ &&
+ buffer.GetLatest(&position, 0)
+ ) {
+
+ // Jump longer than the distance threshold?
+ distance = oldPosition.Distance(position);
+ if (distance > distanceThreshold) {
+
+ /*
+ LOG_DEBUG("PEAK! %0.2f,%0.2f -> %0.2f,%0.2f = %0.2f mm\n",
+ oldPosition.x, oldPosition.y,
+ position.x, position.y,
+ distance
+ );
+ */
+
+ position.x = oldPosition.x;
+ position.y = oldPosition.y;
+
+ // Reset buffer
+ buffer.Reset();
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/TabletDriverService/TabletFilterPeak.h b/TabletDriverService/TabletFilterPeak.h
new file mode 100644
index 0000000..e0ab8e5
--- /dev/null
+++ b/TabletDriverService/TabletFilterPeak.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "PositionRingBuffer.h"
+#include "Vector2D.h"
+
+class TabletFilterPeak : public TabletFilter {
+public:
+
+ PositionRingBuffer buffer;
+ Vector2D position;
+ double distanceThreshold;
+
+ void Reset(Vector2D targetVector);
+ void SetTarget(Vector2D targetVector, double h);
+ void SetPosition(Vector2D vector, double h);
+ bool GetPosition(Vector2D *outputVector);
+ void Update();
+
+
+ TabletFilterPeak();
+ ~TabletFilterPeak();
+};
+
diff --git a/TabletDriverService/TabletFilterSmoothing.cpp b/TabletDriverService/TabletFilterSmoothing.cpp
new file mode 100644
index 0000000..edd94c9
--- /dev/null
+++ b/TabletDriverService/TabletFilterSmoothing.cpp
@@ -0,0 +1,193 @@
+#include "stdafx.h"
+#include "TabletFilterSmoothing.h"
+
+//
+// Constructor
+//
+TabletFilterSmoothing::TabletFilterSmoothing() {
+ latency = 2.0;
+ weight = 1.000;
+ threshold = 0.9;
+
+ AntichatterEnabled = true;
+ antichatterStrength = 3;
+ antichatterMultiplier = 1;
+ antichatterOffsetX = 0.0;
+ antichatterOffsetY = 0.0;
+
+ prev_target.x = 0.0;
+ prev_target.y = 0.0;
+
+ PredictionEnabled = true;
+ PredictionSharpness = 1.0;
+ PredictionStrength = 1.1;
+ PredictionOffsetX = 3.0;
+ PredictionOffsetY = 0.3;
+}
+
+
+//
+// Destructor
+//
+TabletFilterSmoothing::~TabletFilterSmoothing() {
+}
+
+
+//
+// TabletFilter methods
+//
+
+// Reset
+void TabletFilterSmoothing::Reset(Vector2D position) {
+ target.Set(position);
+ prev_target.Set(position);
+ calculated_target.Set(position);
+ position.Set(position);
+}
+
+// Set target position
+void TabletFilterSmoothing::SetTarget(Vector2D vector, double h) {
+ this->target.x = vector.x;
+ this->target.y = vector.y;
+ this->z = h;
+}
+
+// Set current position
+void TabletFilterSmoothing::SetPosition(Vector2D vector, double h) {
+ position.x = vector.x;
+ position.y = vector.y;
+ z = h;
+}
+
+// Get current position
+bool TabletFilterSmoothing::GetPosition(Vector2D *outputVector) {
+ outputVector->x = position.x;
+ outputVector->y = position.y;
+ return true;
+}
+
+// Update
+void TabletFilterSmoothing::Update() {
+
+ double deltaX, deltaY, distance, weightModifier, predictionModifier;
+
+ // Prediction
+ if (PredictionEnabled)
+ {
+ // Calculate predicted position onNewPacket
+ if (prev_target.x != target.x or prev_target.y != target.y)
+ {
+ // Calculate distance between last 2 packets and prediction
+ deltaX = target.x - prev_target.x;
+ deltaY = target.y - prev_target.y;
+ distance = sqrt(deltaX * deltaX + deltaY * deltaY);
+ predictionModifier = 1 / cosh((distance - PredictionOffsetX) * PredictionSharpness) * PredictionStrength + PredictionOffsetY;
+
+ // Apply prediction
+ deltaX *= predictionModifier;
+ deltaY *= predictionModifier;
+
+ // Update predicted position
+ calculated_target.x = target.x + deltaX;
+ calculated_target.y = target.y + deltaY;
+
+ // Update old position for further prediction
+ prev_target.x = target.x;
+ prev_target.y = target.y;
+ }
+ }
+ else {
+ calculated_target.x = target.x;
+ calculated_target.y = target.y;
+ }
+
+ // Smoothing
+ deltaX = calculated_target.x - position.x;
+ deltaY = calculated_target.y - position.y;
+ distance = sqrt(deltaX * deltaX + deltaY * deltaY);
+
+ /*if (distance <= 0.01) {
+ position.x = target.x;
+ position.y = target.y;
+ }
+ // Regular smoothing
+ else*/ if (!AntichatterEnabled) {
+ position.x += deltaX * weight;
+ position.y += deltaY * weight;
+ }
+ // Devocub smoothing
+ else {
+
+ // Increase weight of filter in {formula} times
+ weightModifier = pow((distance + antichatterOffsetX), antichatterStrength*-1)*antichatterMultiplier;
+
+ // Limit minimum
+ if (weightModifier + antichatterOffsetY < 0)
+ weightModifier = 0;
+ else
+ weightModifier = pow((distance + antichatterOffsetX), antichatterStrength*-1)*antichatterMultiplier + antichatterOffsetY;
+
+ // Limit maximum
+ /*if (weightModifier > 1000)
+ weightModifier = 1000;*/
+
+ weightModifier = weight / weightModifier;
+ if (weightModifier > 1) weightModifier = 1;
+ else if (weightModifier < 0) weightModifier = 0;
+
+ position.x += deltaX * weightModifier;
+ position.y += deltaY * weightModifier;
+
+ // z ~= height, strength of signal from pen
+ // 480 z is 14-41
+ // 470 z is 0-30
+ }
+}
+
+
+
+
+
+// Set position
+double TabletFilterSmoothing::SetPosition(double x, double y, double h) {
+ this->position.x = x;
+ this->position.y = y;
+ this->z = h;
+}
+
+
+//
+// Calculate filter latency
+//
+double TabletFilterSmoothing::GetLatency(double filterWeight, double interval, double threshold) {
+ double target = 1 - threshold;
+ double stepCount = -log(1 / target) / log(1 - filterWeight);
+ return stepCount * interval;
+}
+double TabletFilterSmoothing::GetLatency(double filterWeight) {
+ return this->GetLatency(filterWeight, timerInterval, threshold);
+}
+double TabletFilterSmoothing::GetLatency() {
+ return this->GetLatency(weight, timerInterval, threshold);
+}
+
+
+//
+// Calculate filter weight
+//
+double TabletFilterSmoothing::GetWeight(double latency, double interval, double threshold) {
+ double stepCount = latency / interval;
+ double target = 1 - threshold;
+ return 1 - 1 / pow(1 / target, 1 / stepCount);
+}
+double TabletFilterSmoothing::GetWeight(double latency) {
+ return this->GetWeight(latency, this->timerInterval, this->threshold);
+}
+
+// Set Latency
+void TabletFilterSmoothing::SetLatency(double latency) {
+ this->weight = GetWeight(latency);
+ this->latency = latency;
+}
+
+
diff --git a/TabletDriverService/TabletFilterSmoothing.h b/TabletDriverService/TabletFilterSmoothing.h
new file mode 100644
index 0000000..22ab64a
--- /dev/null
+++ b/TabletDriverService/TabletFilterSmoothing.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "Vector2D.h"
+#include "TabletFilter.h"
+
+class TabletFilterSmoothing : public TabletFilter {
+public:
+ double latency;
+ double weight;
+ double threshold;
+
+ bool AntichatterEnabled;
+ double antichatterStrength;
+ double antichatterMultiplier;
+ double antichatterOffsetX;
+ double antichatterOffsetY;
+
+ bool PredictionEnabled;
+ double PredictionSharpness;
+ double PredictionStrength;
+ double PredictionOffsetX;
+ double PredictionOffsetY;
+
+ Vector2D target;
+ Vector2D prev_target;
+ Vector2D calculated_target;
+ Vector2D position;
+ double z;
+
+ TabletFilterSmoothing();
+ ~TabletFilterSmoothing();
+
+ void SetTarget(Vector2D vector, double h);
+ void SetPosition(Vector2D vector, double h);
+ bool GetPosition(Vector2D *outputVector);
+ void Update();
+
+ void Reset(Vector2D position);
+ double SetPosition(double x, double y, double h);
+ double GetLatency(double filterWeight, double interval, double threshold);
+ double GetLatency(double filterWeight);
+ double GetLatency();
+ double GetWeight(double latency, double interval, double threshold);
+ double GetWeight(double latency);
+ void SetLatency(double latency);
+};
+
diff --git a/TabletDriverService/TabletSettings.cpp b/TabletDriverService/TabletSettings.cpp
new file mode 100644
index 0000000..9b99474
--- /dev/null
+++ b/TabletDriverService/TabletSettings.cpp
@@ -0,0 +1,29 @@
+#include "stdafx.h"
+#include "TabletSettings.h"
+
+
+//
+// Constructor
+//
+TabletSettings::TabletSettings() {
+ // Initial settings
+ reportId = 0;
+ reportLength = 8;
+ detectMask = 0x00;
+ ignoreMask = 0x00;
+ maxX = 1;
+ maxY = 1;
+ maxPressure = 1;
+ clickPressure = 0;
+ width = 1;
+ height = 1;
+ skew = 0;
+ type = TabletNormal;
+ mouseWheelSpeed = 50;
+}
+
+//
+// Destructor
+//
+TabletSettings::~TabletSettings() {
+}
diff --git a/TabletDriverService/TabletSettings.h b/TabletDriverService/TabletSettings.h
new file mode 100644
index 0000000..a961534
--- /dev/null
+++ b/TabletDriverService/TabletSettings.h
@@ -0,0 +1,30 @@
+#pragma once
+class TabletSettings {
+public:
+
+ enum TabletType {
+ TabletNormal,
+ TypeWacomIntuos,
+ TypeWacom4100,
+ TypeWacomDrivers
+ };
+
+ BYTE detectMask;
+ BYTE ignoreMask;
+ int maxX;
+ int maxY;
+ int maxPressure;
+ int clickPressure;
+ int keepTipDown;
+ double width;
+ double height;
+ BYTE reportId;
+ int reportLength;
+ double skew;
+ TabletType type;
+ int mouseWheelSpeed;
+
+ TabletSettings();
+ ~TabletSettings();
+};
+
diff --git a/TabletDriverService/USBDevice.cpp b/TabletDriverService/USBDevice.cpp
index 908d13d..b3fca4a 100644
--- a/TabletDriverService/USBDevice.cpp
+++ b/TabletDriverService/USBDevice.cpp
@@ -9,7 +9,7 @@ USBDevice::USBDevice(string Guid, int StringId, string StringMatch) {
this->stringId = StringId;
this->stringMatch = StringMatch;
isOpen = false;
- if(this->OpenDevice(guid, stringId, stringMatch)) {
+ if (this->OpenDevice(guid, stringId, stringMatch)) {
isOpen = true;
}
}
@@ -43,14 +43,14 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
_usbHandle = NULL;
HRESULT hr = CLSIDFromString(wstringGUID, (LPCLSID)&usbDeviceGUID);
- if(hr != S_OK) {
+ if (hr != S_OK) {
LOG_ERROR("Can't create the USB Device GUID!\n");
return false;
}
// Setup device Info
deviceInfo = SetupDiGetClassDevs(&usbDeviceGUID, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
- if(deviceInfo == INVALID_HANDLE_VALUE) {
+ if (deviceInfo == INVALID_HANDLE_VALUE) {
LOG_ERROR("Device info invalid!\n");
SetupDiDestroyDeviceInfoList(deviceInfo);
return false;
@@ -60,7 +60,7 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;
SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &usbDeviceGUID, dwMemberIdx, &deviceInterfaceData);
- while(GetLastError() != ERROR_NO_MORE_ITEMS) {
+ while (GetLastError() != ERROR_NO_MORE_ITEMS) {
// Get the required buffer size.
deviceInfoData.cbSize = sizeof(deviceInfoData);
@@ -71,27 +71,27 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Get device interface detail data
- if(SetupDiGetDeviceInterfaceDetail(deviceInfo, &deviceInterfaceData, deviceInterfaceDetailData, dwSize, &dwSize, &deviceInfoData)) {
+ if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &deviceInterfaceData, deviceInterfaceDetailData, dwSize, &dwSize, &deviceInfoData)) {
//LOG_DEBUG("USB Device path: %S\n", deviceInterfaceDetailData->DevicePath);
// Create File
deviceHandle = CreateFile(deviceInterfaceDetailData->DevicePath,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_OVERLAPPED,
- NULL);
-
- if(deviceHandle != INVALID_HANDLE_VALUE) {
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (deviceHandle != INVALID_HANDLE_VALUE) {
//LOG_DEBUG("Handle: %lu\n", (ULONG)deviceHandle);
// Init WinUsb
WinUsb_Initialize(deviceHandle, &usbHandle);
- if(!usbHandle) {
+ if (!usbHandle) {
LOG_ERROR("ERROR! Unable to start WinUSB for the device!\n");
- if(deviceHandle != INVALID_HANDLE_VALUE)
+ if (deviceHandle != INVALID_HANDLE_VALUE)
CloseHandle(deviceHandle);
return false;
}
@@ -99,7 +99,7 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
// Query interface settings
ZeroMemory(&usbInterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR));
- if(WinUsb_QueryInterfaceSettings(usbHandle, 0, &usbInterfaceDescriptor)) {
+ if (WinUsb_QueryInterfaceSettings(usbHandle, 0, &usbInterfaceDescriptor)) {
WINUSB_SETUP_PACKET setupPacket;
ULONG bytesRead;
@@ -113,32 +113,33 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
setupPacket.Length = 64;
// String request
- if(WinUsb_ControlTransfer(usbHandle, setupPacket, buffer, 64, &bytesRead, NULL)) {
-
+ if (WinUsb_ControlTransfer(usbHandle, setupPacket, buffer, 64, &bytesRead, NULL)) {
+
// Create string from bytes
- for(int i = 2; i < (int)bytesRead; i += 2) {
+ for (int i = 2; i < (int)bytesRead; i += 2) {
str.push_back(buffer[i]);
}
LOG_DEBUG("USB String (%d): %s\n", stringId, str.c_str());
// Device string longer than the search string
- if(bytesRead >= stringSearch.length() * 2) {
+ if (bytesRead >= stringSearch.length() * 2) {
// Match!
- if(str.compare(0, stringSearch.size(), stringSearch) == 0) {
+ if (str.compare(0, stringSearch.size(), stringSearch) == 0) {
_deviceHandle = deviceHandle;
_usbHandle = usbHandle;
}
}
}
- } else {
+ }
+ else {
LOG_ERROR("ERROR! Can't query interface settings!\n");
}
- if(_usbHandle == NULL && usbHandle && usbHandle != INVALID_HANDLE_VALUE)
+ if (_usbHandle == NULL && usbHandle && usbHandle != INVALID_HANDLE_VALUE)
WinUsb_Free(usbHandle);
- if(_deviceHandle == NULL && deviceHandle && deviceHandle != INVALID_HANDLE_VALUE)
+ if (_deviceHandle == NULL && deviceHandle && deviceHandle != INVALID_HANDLE_VALUE)
CloseHandle(deviceHandle);
}
}
@@ -152,10 +153,10 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
SetupDiDestroyDeviceInfoList(deviceInfo);
- if(_deviceHandle && _deviceHandle != INVALID_HANDLE_VALUE
- &&
- _usbHandle && _usbHandle != INVALID_HANDLE_VALUE
- )
+ if (_deviceHandle && _deviceHandle != INVALID_HANDLE_VALUE
+ &&
+ _usbHandle && _usbHandle != INVALID_HANDLE_VALUE
+ )
return true;
return false;
@@ -165,10 +166,11 @@ bool USBDevice::OpenDevice(string usbDeviceGUIDString, int stringId, string stri
int USBDevice::Read(UCHAR pipeId, void *buffer, int length) {
ULONG bytesRead;
try {
- if(WinUsb_ReadPipe(_usbHandle, pipeId, (UCHAR *)buffer, length, &bytesRead, 0)) {
+ if (WinUsb_ReadPipe(_usbHandle, pipeId, (UCHAR *)buffer, length, &bytesRead, 0)) {
return (int)bytesRead;
}
- } catch(exception &e) {
+ }
+ catch (exception &e) {
LOG_ERROR("Exception USB: %s\n", e.what());
}
return 0;
@@ -176,7 +178,7 @@ int USBDevice::Read(UCHAR pipeId, void *buffer, int length) {
int USBDevice::Write(UCHAR pipeId, void *buffer, int length) {
ULONG bytesWritten;
- if(WinUsb_WritePipe(_usbHandle, pipeId, (UCHAR *)buffer, length, &bytesWritten, 0)) {
+ if (WinUsb_WritePipe(_usbHandle, pipeId, (UCHAR *)buffer, length, &bytesWritten, 0)) {
return (int)bytesWritten;
}
return 0;
@@ -191,26 +193,28 @@ int USBDevice::ControlTransfer(UCHAR requestType, UCHAR request, USHORT value, U
usbSetupPacket.Length = length;
usbSetupPacket.Value = value;
usbSetupPacket.Index = index;
- if(WinUsb_ControlTransfer(_usbHandle, usbSetupPacket, (UCHAR*)buffer, length, &bytesRead, NULL)) {
+ if (WinUsb_ControlTransfer(_usbHandle, usbSetupPacket, (UCHAR*)buffer, length, &bytesRead, NULL)) {
return bytesRead;
}
return 0;
}
void USBDevice::CloseDevice() {
- if(_usbHandle != NULL && _usbHandle != INVALID_HANDLE_VALUE) {
+ if (_usbHandle != NULL && _usbHandle != INVALID_HANDLE_VALUE) {
try {
printf("ASD\n");
WinUsb_Free(_usbHandle);
- } catch(exception &e) {
+ }
+ catch (exception &e) {
printf("WinUsb ERROR! %s\n", e.what());
}
}
- if(_deviceHandle != NULL && _deviceHandle != INVALID_HANDLE_VALUE) {
+ if (_deviceHandle != NULL && _deviceHandle != INVALID_HANDLE_VALUE) {
try {
printf("DAS\n");
CloseHandle(_deviceHandle);
- } catch(exception &e) {
+ }
+ catch (exception &e) {
printf("HID ERROR! %s\n", e.what());
}
}
diff --git a/TabletDriverService/VMulti.cpp b/TabletDriverService/VMulti.cpp
index 9272509..5554057 100644
--- a/TabletDriverService/VMulti.cpp
+++ b/TabletDriverService/VMulti.cpp
@@ -8,6 +8,7 @@
VMulti::VMulti() {
isOpen = false;
debugEnabled = false;
+ lastButtons = 0;
mode = ModeAbsoluteMouse;
@@ -37,17 +38,22 @@ VMulti::VMulti() {
// Relative mouse data
memset(&relativeData, 0, sizeof(relativeData));
relativeData.sensitivity = 1;
+ relativeData.resetDistance = 50;
+
+ // Monitor info
+ UpdateMonitorInfo();
// Report buffers
memset(reportBuffer, 0, 65);
memset(lastReportBuffer, 0, 65);
hidDevice = new HIDDevice(0x00FF, 0xBACC, 0xFF00, 0x0001);
- if(hidDevice->isOpen) {
+ if (hidDevice->isOpen) {
isOpen = true;
outputEnabled = true;
- } else {
+ }
+ else {
delete hidDevice;
hidDevice = NULL;
}
@@ -55,7 +61,7 @@ VMulti::VMulti() {
// Destructor
VMulti::~VMulti() {
- if(hidDevice != NULL)
+ if (hidDevice != NULL)
delete hidDevice;
}
@@ -64,22 +70,36 @@ VMulti::~VMulti() {
//
bool VMulti::HasReportChanged() {
- if(memcmp(reportBuffer, lastReportBuffer, 65) == 0) {
+ if (memcmp(reportBuffer, lastReportBuffer, 65) == 0) {
return false;
}
return true;
}
-
+//
+// Reset relative mouse data
+//
void VMulti::ResetRelativeData(double x, double y) {
- relativeData.targetPosition.x = x;
- relativeData.targetPosition.y = y;
- relativeData.lastPosition.x = x;
- relativeData.lastPosition.y = y;
+ relativeData.targetPosition.Set(x, y);
+ relativeData.lastPosition.Set(x, y);
relativeData.currentPosition.x = (int)x;
relativeData.currentPosition.y = (int)y;
}
+
+//
+// Update monitor information
+//
+void VMulti::UpdateMonitorInfo() {
+ monitorInfo.primaryWidth = GetSystemMetrics(SM_CXSCREEN);
+ monitorInfo.primaryHeight = GetSystemMetrics(SM_CYSCREEN);
+ monitorInfo.virtualWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ monitorInfo.virtualHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ monitorInfo.virtualX = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ monitorInfo.virtualY = GetSystemMetrics(SM_YVIRTUALSCREEN);
+}
+
+
//
// Create report
//
@@ -89,13 +109,13 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
//
// Absolute mouse
//
- if(mode == VMulti::ModeAbsoluteMouse) {
+ if (mode == VMulti::ModeAbsoluteMouse) {
reportAbsoluteMouse.buttons = buttons;
reportAbsoluteMouse.x = (USHORT)round(x * 32767.0);
reportAbsoluteMouse.y = (USHORT)round(y * 32767.0);
memcpy(reportBuffer, &reportAbsoluteMouse, sizeof(reportAbsoluteMouse));
- if(debugEnabled) {
+ if (debugEnabled) {
LOG_DEBUGBUFFER(&reportAbsoluteMouse, 9, "VMulti Absolute: ");
}
}
@@ -103,7 +123,7 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
//
// Relative mouse
//
- else if(mode == VMulti::ModeRelativeMouse) {
+ else if (mode == VMulti::ModeRelativeMouse) {
// Buttons
reportRelativeMouse.buttons = buttons;
@@ -113,8 +133,8 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
dy = y - relativeData.lastPosition.y;
distance = sqrt(dx * dx + dy * dy);
- // Reset position when position jumps 10 millimeters
- if(distance > 10) {
+ // Reset relative position when the move distance is long enough
+ if (distance > relativeData.resetDistance) {
ResetRelativeData(x, y);
dx = 0;
dy = 0;
@@ -125,8 +145,7 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
dy *= relativeData.sensitivity;
// Move target position
- relativeData.targetPosition.x += dx;
- relativeData.targetPosition.y += dy;
+ relativeData.targetPosition.Add(dx, dy);
// Send difference of current position and target position
reportRelativeMouse.x = (BYTE)(relativeData.targetPosition.x - relativeData.currentPosition.x);
@@ -137,11 +156,10 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
relativeData.currentPosition.y += reportRelativeMouse.y;
// Last position
- relativeData.lastPosition.x = x;
- relativeData.lastPosition.y = y;
+ relativeData.lastPosition.Set(x, y);
memcpy(reportBuffer, &reportRelativeMouse, sizeof(reportRelativeMouse));
- if(debugEnabled) {
+ if (debugEnabled) {
LOG_DEBUGBUFFER(&reportRelativeMouse, 10, "VMulti Relative: ");
}
}
@@ -149,20 +167,63 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
//
// Digitizer
//
- else if(mode == VMulti::ModeDigitizer) {
+ else if (mode == VMulti::ModeDigitizer) {
reportDigitizer.buttons = buttons | 0x20;
reportDigitizer.x = (USHORT)floor(x * 32767.0 - 5);
reportDigitizer.y = (USHORT)floor(y * 32767.0 - 5);
reportDigitizer.pressure = (USHORT)floor(pressure * 2047.0);
- if(reportDigitizer.pressure > 2047) {
+ if (reportDigitizer.pressure > 2047) {
reportDigitizer.pressure = 2047;
}
memcpy(reportBuffer, &reportDigitizer, sizeof(reportDigitizer));
- if(debugEnabled) {
+ if (debugEnabled) {
LOG_DEBUGBUFFER(&reportDigitizer, 10, "VMulti Digitizer: ");
}
}
+ //
+ // SendInput
+ //
+ else if (mode == ModeSendInput) {
+
+ if (debugEnabled) {
+ LOG_DEBUG("%0.0f,%0.0f | %0.0f,%0.0f | %0.0f,%0.0f\n",
+ monitorInfo.primaryWidth, monitorInfo.primaryHeight,
+ monitorInfo.virtualWidth, monitorInfo.virtualHeight,
+ monitorInfo.virtualX, monitorInfo.virtualY
+ );
+ }
+ INPUT input = { 0 };
+ input.type = INPUT_MOUSE;
+ input.mi.mouseData = 0;
+ input.mi.dx = (LONG)floor(
+ x * 65535.0 * (monitorInfo.virtualWidth / monitorInfo.primaryWidth)
+ + (monitorInfo.virtualX / monitorInfo.primaryWidth * 65535.0)
+ );
+ input.mi.dy = (LONG)floor(
+ y * 65535.0 * (monitorInfo.virtualHeight / monitorInfo.primaryHeight)
+ + (monitorInfo.virtualY / monitorInfo.primaryHeight * 65535.0)
+ );
+ input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
+
+ // Mouse 1
+ if ((buttons & 0x01) && !(lastButtons & 0x01)) input.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
+ else if (!(buttons & 0x01) && (lastButtons & 0x01)) input.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
+
+ // Mouse 2
+ if ((buttons & 0x02) && !(lastButtons & 0x02)) input.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
+ else if (!(buttons & 0x02) && (lastButtons & 0x02)) input.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
+
+ // Mouse 3
+ if ((buttons & 0x04) && !(lastButtons & 0x04)) input.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
+ else if (!(buttons & 0x04) && (lastButtons & 0x04)) input.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
+
+ lastButtons = buttons;
+
+ memcpy(reportBuffer, &input, sizeof(INPUT));
+ }
+
+
}
@@ -171,28 +232,45 @@ void VMulti::CreateReport(BYTE buttons, double x, double y, double pressure) {
// Send reset report
//
int VMulti::ResetReport() {
- if(!outputEnabled) return true;
+ if (!outputEnabled) return true;
// Absolute
- if(mode == ModeAbsoluteMouse) {
+ if (mode == ModeAbsoluteMouse) {
reportAbsoluteMouse.buttons = 0;
reportAbsoluteMouse.wheel = 0;
memcpy(reportBuffer, &reportAbsoluteMouse, sizeof(reportAbsoluteMouse));
- // Relative
- } else if(mode == ModeRelativeMouse) {
+ // Relative
+ }
+ else if (mode == ModeRelativeMouse) {
reportRelativeMouse.buttons = 0;
reportRelativeMouse.x = 0;
reportRelativeMouse.y = 0;
reportRelativeMouse.wheel = 0;
memcpy(reportBuffer, &reportRelativeMouse, sizeof(reportRelativeMouse));
- // Digitizer
- } else if(mode == ModeDigitizer) {
+ // Digitizer
+ }
+ else if (mode == ModeDigitizer) {
reportDigitizer.buttons = 0;
reportDigitizer.pressure = 0;
memcpy(reportBuffer, &reportDigitizer, sizeof(reportDigitizer));
+
+ // Send Input
}
+ else if (mode == ModeSendInput) {
+ INPUT input = { 0 };
+ input.type = INPUT_MOUSE;
+ input.mi.mouseData = 0;
+ input.mi.dx = 0;
+ input.mi.dy = 0;
+ input.mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_MIDDLEUP;
+ memcpy(reportBuffer, &input, sizeof(INPUT));
+ memcpy(lastReportBuffer, reportBuffer, 65);
+ return 0;
+ }
+
+ memcpy(lastReportBuffer, reportBuffer, 65);
return hidDevice->Write(reportBuffer, 65);
}
@@ -201,9 +279,14 @@ int VMulti::ResetReport() {
// Write report
//
int VMulti::WriteReport() {
- if(!outputEnabled) return true;
+ if (!outputEnabled) return true;
memcpy(lastReportBuffer, reportBuffer, 65);
- return hidDevice->Write(reportBuffer, 65);
+ if (mode == ModeSendInput) {
+ return SendInput(1, (LPINPUT)reportBuffer, sizeof(INPUT));
+ }
+ else {
+ return hidDevice->Write(reportBuffer, 65);
+ }
}
\ No newline at end of file
diff --git a/TabletDriverService/VMulti.h b/TabletDriverService/VMulti.h
index 3df124e..cf6fadb 100644
--- a/TabletDriverService/VMulti.h
+++ b/TabletDriverService/VMulti.h
@@ -1,6 +1,7 @@
#pragma once
#include "HIDDevice.h"
+#include "Vector2D.h"
class VMulti {
private:
@@ -12,7 +13,8 @@ class VMulti {
enum VMultiMode {
ModeAbsoluteMouse,
ModeRelativeMouse,
- ModeDigitizer
+ ModeDigitizer,
+ ModeSendInput
};
struct {
@@ -46,12 +48,6 @@ class VMulti {
} reportDigitizer;
- // Position Double
- typedef struct {
- double x;
- double y;
- } PositionDouble;
-
// Position Integer
typedef struct {
int x;
@@ -61,15 +57,27 @@ class VMulti {
// Relative mouse data
struct {
PositionInt currentPosition;
- PositionDouble lastPosition;
- PositionDouble targetPosition;
+ Vector2D lastPosition;
+ Vector2D targetPosition;
double sensitivity;
+ double resetDistance;
} relativeData;
+
+ struct {
+ double primaryWidth = GetSystemMetrics(SM_CXSCREEN);
+ double primaryHeight = GetSystemMetrics(SM_CYSCREEN);
+ double virtualWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ double virtualHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ double virtualX = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ double virtualY = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ } monitorInfo;
+
VMultiMode mode;
bool isOpen;
bool debugEnabled;
bool outputEnabled;
+ int lastButtons;
@@ -77,6 +85,7 @@ class VMulti {
~VMulti();
bool HasReportChanged();
void ResetRelativeData(double x, double y);
+ void UpdateMonitorInfo();
void CreateReport(BYTE buttons, double x, double y, double pressure);
int ResetReport();
int WriteReport();
diff --git a/TabletDriverService/Vector2D.cpp b/TabletDriverService/Vector2D.cpp
new file mode 100644
index 0000000..6f86f63
--- /dev/null
+++ b/TabletDriverService/Vector2D.cpp
@@ -0,0 +1,46 @@
+#include "stdafx.h"
+#include "Vector2D.h"
+
+//
+// Constructor
+//
+Vector2D::Vector2D() {
+ x = 0;
+ y = 0;
+}
+
+//
+// Destructor
+//
+Vector2D::~Vector2D() {
+}
+
+void Vector2D::Set(double x, double y) {
+ this->x = x;
+ this->y = y;
+}
+
+void Vector2D::Set(Vector2D vector) {
+ Set(vector.x, vector.y);
+}
+
+void Vector2D::Add(double x, double y) {
+ this->x += x;
+ this->y += y;
+}
+
+void Vector2D::Add(Vector2D vector) {
+ Add(vector.x, vector.y);
+}
+
+void Vector2D::Multiply(double value) {
+ this->x *= value;
+ this->y *= value;
+}
+
+double Vector2D::Distance(Vector2D target) {
+ double dx = target.x - this->x;
+ double dy = target.y - this->y;
+ return sqrt(dx * dx + dy * dy);
+}
+
diff --git a/TabletDriverService/Vector2D.h b/TabletDriverService/Vector2D.h
new file mode 100644
index 0000000..6548a10
--- /dev/null
+++ b/TabletDriverService/Vector2D.h
@@ -0,0 +1,17 @@
+#pragma once
+class Vector2D {
+public:
+ double x;
+ double y;
+ Vector2D();
+ ~Vector2D();
+
+ void Set(double x, double y);
+ void Set(Vector2D vector);
+ void Add(double x, double y);
+ void Add(Vector2D vector);
+ void Multiply(double value);
+ double Distance(Vector2D target);
+ void CopyTo(Vector2D target);
+};
+
diff --git a/TabletDriverService/config/tablet.cfg b/TabletDriverService/config/tablet.cfg
index a5148b2..8966573 100644
--- a/TabletDriverService/config/tablet.cfg
+++ b/TabletDriverService/config/tablet.cfg
@@ -1,16 +1,38 @@
#
-# Wacom CTL-470
+# Example tablet definition:
+# Tablet 0x056a 0x00dd 0x0D 0x01
#
# VID: 0x056a
# PID: 0x00dd
# HID Usage Page: 0x0D
# HID Usage: 0x01
#
+
+
+#
+# Wacom CTL-470 (Wacom drivers installed)
+#
+Tablet 0x056a 0x00dd 0xFF00 0x000A
+Name "Wacom CTL-470 (Wacom drivers installed)"
+ReportId 0x02
+ReportLength 11
+DetectMask 0x40
+MaxX 14720
+MaxY 9200
+MaxPressure 1023
+Width 147.2
+Height 92.0
+Type WacomDrivers
+
+
+#
+# Wacom CTL-470
+#
Tablet 0x056a 0x00dd 0x0D 0x01
Name "Wacom CTL-470"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -26,7 +48,7 @@ Tablet 0x056a 0x00de 0x0D 0x01
Name "Wacom CTH-470"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -42,7 +64,7 @@ Tablet 0x056a 0x00df 0x0D 0x01
Name "Wacom CTH-670"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -51,18 +73,33 @@ Height 137.0
InitFeature 0x02 0x02
+#
+# Wacom CTL-471 (Wacom drivers installed)
+#
+Tablet 0x056a 0x0300 0xFF00 0x000A
+Name "Wacom CTL-471 (Wacom drivers installed)"
+ReportLength 11
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 1023
+Width 152.00
+Height 95.00
+Type WacomDrivers
+
+
#
# Wacom CTL-471
#
Tablet 0x056a 0x0300 0xFF0D 0x01
Name "Wacom CTL-471"
ReportLength 10
-ButtonMask 0x40
-MaxX 14720
-MaxY 9225
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
MaxPressure 1023
-Width 147.2
-Height 92.25
+Width 152.00
+Height 95.00
InitFeature 0x02 0x02
@@ -72,7 +109,7 @@ InitFeature 0x02 0x02
Tablet 0x056a 0x0301 0xFF0D 0x01
Name "Wacom CTL-671"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13530
MaxPressure 1023
@@ -81,6 +118,22 @@ Height 135.30
InitFeature 0x02 0x02
+#
+# Wacom CTL-472
+#
+Tablet 0x056a 0x037a 0xFF00 0x000A
+Name "Wacom CTL-472 (Wacom drivers installed)"
+ReportId 0x02
+ReportLength 11
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 2047
+Width 152.0
+Height 95.0
+Type WacomDrivers
+
+
#
# Wacom CTL-472
#
@@ -88,7 +141,7 @@ Tablet 0x056a 0x037a 0xFF0D 0x0001
Name "Wacom CTL-472"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 2047
@@ -104,7 +157,7 @@ Tablet 0x056a 0x037b 0xFF0D 0x0001
Name "Wacom CTL-672"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 2047
@@ -113,6 +166,22 @@ Height 135.00
InitFeature 0x02 0x02
+#
+# Wacom CTL-480 (Wacom drivers installed)
+#
+Tablet 0x056a 0x030e 0xFF00 0x000A
+Name "Wacom CTL-480 (Wacom drivers installed)"
+ReportId 0x02
+ReportLength 11
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 1023
+Width 152.0
+Height 95.0
+Type WacomDrivers
+
+
#
# Wacom CTL-480
#
@@ -120,7 +189,7 @@ Tablet 0x056a 0x030e 0xFF0D 0x0001
Name "Wacom CTL-480"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 1023
@@ -129,6 +198,22 @@ Height 95.0
InitFeature 0x02 0x02
+#
+# Wacom CTH-480 (Wacom drivers installed)
+#
+Tablet 0x056a 0x0302 0xFF00 0x000A
+Name "Wacom CTH-480 (Wacom drivers installed)"
+ReportId 0x02
+ReportLength 11
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 1023
+Width 152.0
+Height 95.0
+Type WacomDrivers
+
+
#
# Wacom CTH-480
#
@@ -136,7 +221,7 @@ Tablet 0x056a 0x0302 0xFF0D 0x0001
Name "Wacom CTH-480"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 1023
@@ -152,7 +237,7 @@ Tablet 0x056a 0x0323 0xFF0D 0x0001
Name "Wacom CTL-680"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 1023
@@ -168,7 +253,7 @@ Tablet 0x056a 0x0303 0xFF0D 0x0001
Name "Wacom CTH-680"
ReportId 0x02
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 1023
@@ -184,13 +269,14 @@ Tablet 0x056a 0x033b 0xFF0D 0x0001
Name "Wacom CTL-490"
ReportId 0x10
ReportLength 10
-ButtonMask 0xA0
+DetectMask 0xA0
MaxX 15200
MaxY 9500
MaxPressure 2047
KeepTipDown 2 packets
Width 152.0
Height 95.0
+#Feature 0x83 0x02 0x00
InitFeature 0x02 0x02
Type WacomIntuos
@@ -202,7 +288,7 @@ Tablet 0x056a 0x033c 0xFF0D 0x0001
Name "Wacom CTH-490"
ReportId 0x10
ReportLength 10
-ButtonMask 0xA0
+DetectMask 0xA0
MaxX 15200
MaxY 9500
MaxPressure 2047
@@ -213,13 +299,61 @@ InitFeature 0x02 0x02
Type WacomIntuos
+#
+# Wacom CTL-4100
+#
+Tablet 0x056a 0x0376 0xFF0D 0x0001
+Tablet 0x056a 0x0374 0xFF0D 0x0001
+Name "Wacom CTL-4100"
+ReportId 0x10
+ReportLength 192
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 4095
+Width 152.0
+Height 95.0
+Type Wacom4100
+
+#
+# Wacom CTL-4100 (Wacom drivers installed)
+#
+Tablet 0x056a 0x0376 0xFF00 0x000A
+Tablet 0x056a 0x0374 0xFF00 0x000A
+Name "Wacom CTL-4100 (Wacom drivers installed)"
+ReportId 0x10
+ReportLength 193
+DetectMask 0x40
+MaxX 15200
+MaxY 9500
+MaxPressure 4095
+Width 152.0
+Height 95.0
+Type Wacom4100
+
+
+#
+# Wacom CTL-4100 Bluetooth
+Tablet 0x056a 0x0377 0xFF0D 0x0001
+Name "Wacom CTL-4100 Bluetooth (Experimental)"
+ReportId100 Bluetooth
+# 0x81
+ReportLength 361
+DetectMask 0xE0
+MaxX 15200
+MaxY 9500
+MaxPressure 4095
+Width 152.0
+Height 95.0
+
+
#
# Wacom PTH-451 ???
#
Tablet 0x056a 0x0314 0xFF0D 0x01
Name "Wacom PTH-451"
ReportLength 10
-ButtonMask 0xE0
+DetectMask 0xE0
MaxX 31496
MaxY 19685
MaxPressure 2047
@@ -229,6 +363,23 @@ InitFeature 0x02 0x02
Type WacomIntuos
+#
+# Wacom PTH-850
+#
+Tablet 0x056A 0x0028 0x00D 0x0001
+Name "Wacom PTH-850"
+ReportLength 10
+DetectMask 0xE0
+MaxX 65024
+MaxY 40640
+MaxPressure 2047
+Width 325.120
+Height 203.200
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
+
#
# XP-Pen G430
#
@@ -236,7 +387,7 @@ Tablet 0x28BD 0x0075 0xFF0A 0x0001
Name "XP-Pen G430"
ReportId 0x02
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 45720
MaxY 29210
MaxPressure 2047
@@ -245,6 +396,22 @@ Height 76.2
InitReport 0x02 0xB0 0x02 0x00 0x00 0x00 0x00 0x00
+#
+# XP-Pen G540 Pro
+#
+Tablet 0x28BD 0x0061 0xFF0A 0x0001
+Name "XP-Pen G540 Pro"
+ReportId 0x02
+ReportLength 8
+DetectMask 0x80
+MaxX 45720
+MaxY 29210
+MaxPressure 8191
+Width 147.0
+Height 101.6
+InitReport 0x02 0xB0 0x02 0x00 0x00 0x00 0x00 0x00
+
+
#
# XP-Pen G640
#
@@ -252,15 +419,31 @@ Tablet 0x28BD 0x0094 0xFF0A 0x0001
Name "XP-Pen G640"
ReportId 0x02
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 32000
MaxY 20000
MaxPressure 8191
-width 160.0
+Width 160.0
Height 100.0
InitReport 0x02 0xB0 0x02 0x00 0x00 0x00 0x00 0x00
+#
+# XP-Pen Deco 01
+#
+Tablet 0x28BD 0x0042 0xFF0A 0x0001
+Name "XP-Pen Deco 01"
+ReportId 0x02
+ReportLength 8
+DetectMask 0x80
+MaxX 25400
+MaxY 15875
+MaxPressure 8191
+Width 254.0
+Height 158.75
+InitReport 0x02 0xB0 0x02 0x00 0x00 0x00 0x00 0x00
+
+
#
# Huion 420
#
@@ -268,7 +451,7 @@ Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 6 420
Name "Huion 420"
ReportId 0x07
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 8340
MaxY 4680
MaxPressure 2047
@@ -282,7 +465,7 @@ Height 56.6
Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 6 H420
Name "Huion H420"
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 8340
MaxY 4680
MaxPressure 2047
@@ -290,6 +473,22 @@ Width 101.6
Height 56.6
+#
+# Huion H430P
+#
+Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 201 HUION_T176
+Name "Huion H430P"
+ReportId 0x08
+ReportLength 12
+DetectMask 0x80
+IgnoreMask 0x60
+MaxX 24384
+MaxY 15240
+MaxPressure 4095
+Width 121.92
+Height 76.20
+
+
#
# Huion H640P
#
@@ -297,7 +496,7 @@ Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 201 HUION_T173
Name "Huion H640P"
ReportId 0x08
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 31999
MaxY 20000
MaxPressure 8191
@@ -312,7 +511,7 @@ Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 201 HUION_T156
Name "Gaomon S56K"
ReportId 0x07
ReportLength 8
-ButtonMask 0x80
+DetectMask 0x80
MaxX 25196
MaxY 18896
MaxPressure 2047
@@ -320,4 +519,17 @@ Width 160.0
Height 120.0
+#
+# Huion osu!tablet
+#
+Tablet "{62F12D4C-3431-4EFD-8DD7-8E9AAB18D30C}" 200 HVAN
+Name "Huion osu!tablet (check the GitHub issue #99)"
+ReportId 0x07
+ReportLength 8
+DetectMask 0x80
+MaxX 8340
+MaxY 4680
+MaxPressure 2047
+Width 101.6
+Height 56.6
diff --git a/TabletDriverService/config/wacom.cfg b/TabletDriverService/config/wacom.cfg
index 0970bc4..d0dc2a8 100644
--- a/TabletDriverService/config/wacom.cfg
+++ b/TabletDriverService/config/wacom.cfg
@@ -10,12 +10,57 @@
#
+# Wacom PTK-450
+Tablet 0x056A 0x0029 0xFF0D 0x0001
+Tablet 0x056A 0x0029 0x000D 0x0001
+Name "Wacom PTK-450"
+ReportLength 10
+DetectMask 0xE0
+MaxX 31496
+MaxY 19685
+MaxPressure 2047
+Width 157.480
+Height 98.425
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
+# Wacom PTK-650
+Tablet 0x056A 0x002A 0xFF0D 0x0001
+Tablet 0x056A 0x002A 0x000D 0x0001
+Name "Wacom PTK-650"
+ReportLength 10
+DetectMask 0xE0
+MaxX 44704
+MaxY 27940
+MaxPressure 2047
+Width 223.520
+Height 139.700
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
+# Wacom PTH-450
+Tablet 0x056A 0x0026 0xFF0D 0x0001
+Tablet 0x056A 0x0026 0x000D 0x0001
+Name "Wacom PTH-450"
+ReportLength 10
+DetectMask 0xE0
+MaxX 31496
+MaxY 19685
+MaxPressure 2047
+Width 157.480
+Height 98.425
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
# Wacom PTH-451
Tablet 0x056A 0x0314 0xFF0D 0x0001
Tablet 0x056A 0x0314 0x000D 0x0001
Name "Wacom PTH-451"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 31496
MaxY 19685
MaxPressure 2047
@@ -25,12 +70,27 @@ InitFeature 0x02 0x02
Type WacomIntuos
+# Wacom PTH-650
+Tablet 0x056A 0x0027 0xFF0D 0x0001
+Tablet 0x056A 0x0027 0x000D 0x0001
+Name "Wacom PTH-650"
+ReportLength 10
+DetectMask 0xE0
+MaxX 44704
+MaxY 27940
+MaxPressure 2047
+Width 223.520
+Height 139.700
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
# Wacom PTH-651
Tablet 0x056A 0x0315 0xFF0D 0x0001
Tablet 0x056A 0x0315 0x000D 0x0001
Name "Wacom PTH-651"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 44704
MaxY 27940
MaxPressure 2047
@@ -40,12 +100,27 @@ InitFeature 0x02 0x02
Type WacomIntuos
+# Wacom PTH-850
+Tablet 0x056A 0x0028 0xFF0D 0x0001
+Tablet 0x056A 0x0028 0x000D 0x0001
+Name "Wacom PTH-850"
+ReportLength 10
+DetectMask 0xE0
+MaxX 65024
+MaxY 40640
+MaxPressure 2047
+Width 325.120
+Height 203.200
+InitFeature 0x02 0x02
+Type WacomIntuos
+
+
# Wacom PTH-851
Tablet 0x056A 0x0317 0xFF0D 0x0001
Tablet 0x056A 0x0317 0x000D 0x0001
Name "Wacom PTH-851"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 65024
MaxY 40640
MaxPressure 2047
@@ -60,7 +135,7 @@ Tablet 0x056A 0x0065 0xFF0D 0x0001
Tablet 0x056A 0x0065 0x000D 0x0001
Name "Wacom MTE-450"
ReportLength 9
-ButtonMask 0x00
+DetectMask 0x00
MaxX 14760
MaxY 9225
MaxPressure 511
@@ -74,7 +149,7 @@ Tablet 0x056A 0x00D0 0xFF0D 0x0001
Tablet 0x056A 0x00D0 0x000D 0x0001
Name "Wacom CTT-460"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -88,7 +163,7 @@ Tablet 0x056A 0x00D4 0xFF0D 0x0001
Tablet 0x056A 0x00D4 0x000D 0x0001
Name "Wacom CTL-460"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -102,7 +177,7 @@ Tablet 0x056A 0x00DD 0xFF0D 0x0001
Tablet 0x056A 0x00DD 0x000D 0x0001
Name "Wacom CTL-470"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -116,7 +191,7 @@ Tablet 0x056A 0x0300 0xFF0D 0x0001
Tablet 0x056A 0x0300 0x000D 0x0001
Name "Wacom CTL-471"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9225
MaxPressure 1023
@@ -130,7 +205,7 @@ Tablet 0x056A 0x037A 0xFF0D 0x0001
Tablet 0x056A 0x037A 0x000D 0x0001
Name "Wacom CTL-472"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 2047
@@ -144,7 +219,7 @@ Tablet 0x056A 0x030E 0xFF0D 0x0001
Tablet 0x056A 0x030E 0x000D 0x0001
Name "Wacom CTL-480"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 1023
@@ -158,7 +233,7 @@ Tablet 0x056A 0x033B 0xFF0D 0x0001
Tablet 0x056A 0x033B 0x000D 0x0001
Name "Wacom CTL-490"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 2047
@@ -173,7 +248,7 @@ Tablet 0x056A 0x00D5 0xFF0D 0x0001
Tablet 0x056A 0x00D5 0x000D 0x0001
Name "Wacom CTL-660"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -187,7 +262,7 @@ Tablet 0x056A 0x0301 0xFF0D 0x0001
Tablet 0x056A 0x0301 0x000D 0x0001
Name "Wacom CTL-671"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13530
MaxPressure 1023
@@ -201,7 +276,7 @@ Tablet 0x056A 0x037B 0xFF0D 0x0001
Tablet 0x056A 0x037B 0x000D 0x0001
Name "Wacom CTL-672"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 2047
@@ -215,7 +290,7 @@ Tablet 0x056A 0x0323 0xFF0D 0x0001
Tablet 0x056A 0x0323 0x000D 0x0001
Name "Wacom CTL-680"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 1023
@@ -229,7 +304,7 @@ Tablet 0x056A 0x033D 0xFF0D 0x0001
Tablet 0x056A 0x033D 0x000D 0x0001
Name "Wacom CTL-690"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 2047
@@ -244,7 +319,7 @@ Tablet 0x056A 0x00D6 0xFF0D 0x0001
Tablet 0x056A 0x00D6 0x000D 0x0001
Name "Wacom CTH-460(A)"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -258,7 +333,7 @@ Tablet 0x056A 0x00D7 0xFF0D 0x0001
Tablet 0x056A 0x00D7 0x000D 0x0001
Name "Wacom CTH-461(A)"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -272,7 +347,7 @@ Tablet 0x056A 0x00DA 0xFF0D 0x0001
Tablet 0x056A 0x00DA 0x000D 0x0001
Name "Wacom CTH-461SE"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -286,7 +361,7 @@ Tablet 0x056A 0x00D1 0xFF0D 0x0001
Tablet 0x056A 0x00D1 0x000D 0x0001
Name "Wacom CTH-460"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -300,7 +375,7 @@ Tablet 0x056A 0x00D2 0xFF0D 0x0001
Tablet 0x056A 0x00D2 0x000D 0x0001
Name "Wacom CTH-461"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -314,7 +389,7 @@ Tablet 0x056A 0x00DE 0xFF0D 0x0001
Tablet 0x056A 0x00DE 0x000D 0x0001
Name "Wacom CTH-470"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 14720
MaxY 9200
MaxPressure 1023
@@ -328,7 +403,7 @@ Tablet 0x056A 0x0302 0xFF0D 0x0001
Tablet 0x056A 0x0302 0x000D 0x0001
Name "Wacom CTH-480"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 1023
@@ -342,7 +417,7 @@ Tablet 0x056A 0x033C 0xFF0D 0x0001
Tablet 0x056A 0x033C 0x000D 0x0001
Name "Wacom CTH-490"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 15200
MaxY 9500
MaxPressure 2047
@@ -357,7 +432,7 @@ Tablet 0x056A 0x00D3 0xFF0D 0x0001
Tablet 0x056A 0x00D3 0x000D 0x0001
Name "Wacom CTH-661"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -371,7 +446,7 @@ Tablet 0x056A 0x00D8 0xFF0D 0x0001
Tablet 0x056A 0x00D8 0x000D 0x0001
Name "Wacom CTH-661(A)"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -385,7 +460,7 @@ Tablet 0x056A 0x00DB 0xFF0D 0x0001
Tablet 0x056A 0x00DB 0x000D 0x0001
Name "Wacom CTH-661SE"
ReportLength 9
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -399,7 +474,7 @@ Tablet 0x056A 0x00DF 0xFF0D 0x0001
Tablet 0x056A 0x00DF 0x000D 0x0001
Name "Wacom CTH-670"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21648
MaxY 13700
MaxPressure 1023
@@ -413,7 +488,7 @@ Tablet 0x056A 0x0303 0xFF0D 0x0001
Tablet 0x056A 0x0303 0x000D 0x0001
Name "Wacom CTH-680"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 1023
@@ -427,7 +502,7 @@ Tablet 0x056A 0x033E 0xFF0D 0x0001
Tablet 0x056A 0x033E 0x000D 0x0001
Name "Wacom CTH-690"
ReportLength 10
-ButtonMask 0x40
+DetectMask 0x40
MaxX 21600
MaxY 13500
MaxPressure 2047
@@ -442,7 +517,7 @@ Tablet 0x056A 0x0017 0xFF0D 0x0001
Tablet 0x056A 0x0017 0x000D 0x0001
Name "Wacom CTE-450"
ReportLength 9
-ButtonMask 0x00
+DetectMask 0x00
MaxX 14760
MaxY 9225
MaxPressure 511
@@ -456,7 +531,7 @@ Tablet 0x056A 0x0018 0xFF0D 0x0001
Tablet 0x056A 0x0018 0x000D 0x0001
Name "Wacom CTE-650"
ReportLength 9
-ButtonMask 0x00
+DetectMask 0x00
MaxX 21648
MaxY 13530
MaxPressure 511
@@ -466,9 +541,10 @@ InitFeature 0x02 0x02
#
-# Automatically generated Wacom tablet configurations (32):
-# PTH-451, PTH-651, PTH-851, MTE-450, CTT-460, CTL-460, CTL-470, CTL-471,
-# CTL-472, CTL-480, CTL-490, CTL-660, CTL-671, CTL-672, CTL-680, CTL-690,
-# CTH-460(A), CTH-461(A), CTH-461SE, CTH-460, CTH-461, CTH-470, CTH-480, CTH-490,
-# CTH-661, CTH-661(A), CTH-661SE, CTH-670, CTH-680, CTH-690, CTE-450, CTE-650
+# Automatically generated Wacom tablet configurations (37):
+# PTK-450, PTK-650, PTH-450, PTH-451, PTH-650, PTH-651, PTH-850, PTH-851,
+# MTE-450, CTT-460, CTL-460, CTL-470, CTL-471, CTL-472, CTL-480, CTL-490,
+# CTL-660, CTL-671, CTL-672, CTL-680, CTL-690, CTH-460(A), CTH-461(A), CTH-461SE,
+# CTH-460, CTH-461, CTH-470, CTH-480, CTH-490, CTH-661, CTH-661(A), CTH-661SE,
+# CTH-670, CTH-680, CTH-690, CTE-450, CTE-650
#
diff --git a/TabletDriverService/startuplog.txt b/TabletDriverService/startuplog.txt
new file mode 100644
index 0000000..7e61bda
--- /dev/null
+++ b/TabletDriverService/startuplog.txt
@@ -0,0 +1,82 @@
+2018-06-08 11:29:35 [INFO] \ Reading 'config\init.cfg'
+2018-06-08 11:29:35 [INFO] >> Log startuplog.txt
+2018-06-08 11:29:35 [INFO] Log file 'startuplog.txt' opened.
+2018-06-08 11:29:35 [INFO] >> LogDirect true
+2018-06-08 11:29:35 [INFO] Log direct print = True
+2018-06-08 11:29:35 [INFO] >> Include "config\tablet.cfg"
+2018-06-08 11:29:35 [INFO] \ Reading 'config\tablet.cfg'
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # Example tablet definition:
+2018-06-08 11:29:35 [INFO] >> # Tablet 0x056a 0x00dd 0x0D 0x01
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # VID: 0x056a
+2018-06-08 11:29:35 [INFO] >> # PID: 0x00dd
+2018-06-08 11:29:35 [INFO] >> # HID Usage Page: 0x0D
+2018-06-08 11:29:35 [INFO] >> # HID Usage: 0x01
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # Wacom CTL-470 (Wacom drivers installed)
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> Tablet 0x056a 0x00dd 0xFF00 0x000A
+2018-06-08 11:29:35 [WARNING] Can't open HID tablet 0x056A 0x00DD 0xFF00 0x000A
+2018-06-08 11:29:35 [INFO] >> Name "Wacom CTL-470 (Wacom drivers installed)"
+2018-06-08 11:29:35 [INFO] >> ReportId 0x02
+2018-06-08 11:29:35 [INFO] >> ReportLength 11
+2018-06-08 11:29:35 [INFO] >> DetectMask 0x40
+2018-06-08 11:29:35 [INFO] >> MaxX 14720
+2018-06-08 11:29:35 [INFO] >> MaxY 9200
+2018-06-08 11:29:35 [INFO] >> MaxPressure 1023
+2018-06-08 11:29:35 [INFO] >> Width 147.2
+2018-06-08 11:29:35 [INFO] >> Height 92.0
+2018-06-08 11:29:35 [INFO] >> Type WacomDrivers
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # Wacom CTL-470
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> Tablet 0x056a 0x00dd 0x0D 0x01
+2018-06-08 11:29:35 [INFO] Tablet found!
+2018-06-08 11:29:35 [INFO] >> Name "Wacom CTL-470"
+2018-06-08 11:29:35 [INFO] Tablet name = 'Wacom CTL-470'
+2018-06-08 11:29:35 [INFO] >> ReportId 0x02
+2018-06-08 11:29:35 [INFO] Tablet report id = 2
+2018-06-08 11:29:35 [INFO] >> ReportLength 10
+2018-06-08 11:29:35 [INFO] Tablet report length = 10
+2018-06-08 11:29:35 [INFO] >> DetectMask 0x40
+2018-06-08 11:29:35 [INFO] Tablet detect mask = 40
+2018-06-08 11:29:35 [INFO] >> MaxX 14720
+2018-06-08 11:29:35 [INFO] Tablet max X = 14720
+2018-06-08 11:29:35 [INFO] >> MaxY 9200
+2018-06-08 11:29:35 [INFO] Tablet max Y = 9200
+2018-06-08 11:29:35 [INFO] >> MaxPressure 1023
+2018-06-08 11:29:35 [INFO] Tablet max pressure = 1023
+2018-06-08 11:29:35 [INFO] >> Width 147.2
+2018-06-08 11:29:35 [INFO] Tablet width = 147.20 mm
+2018-06-08 11:29:35 [INFO] >> Height 92.0
+2018-06-08 11:29:35 [INFO] Tablet height = 92.00 mm
+2018-06-08 11:29:35 [INFO] >> InitFeature 0x02 0x02
+2018-06-08 11:29:35 [INFO] Tablet init feature report: { 0x02, 0x02 }
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # Wacom CTH-470 ???
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> Tablet 0x056a 0x00de 0x0D 0x01
+2018-06-08 11:29:35 [INFO] Tablet is already defined!
+2018-06-08 11:29:35 [INFO] / End of 'config\tablet.cfg'
+2018-06-08 11:29:35 [INFO] >> Include "config\wacom.cfg"
+2018-06-08 11:29:35 [INFO] \ Reading 'config\wacom.cfg'
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # This file is automatically generated from these sources:
+2018-06-08 11:29:35 [INFO] >> # https://github.com/linuxwacom/input-wacom/blob/master/3.7/wacom_wac.h
+2018-06-08 11:29:35 [INFO] >> # https://github.com/linuxwacom/input-wacom/blob/master/3.7/wacom_wac.c
+2018-06-08 11:29:35 [INFO] >> # https://github.com/linuxwacom/input-wacom/wiki/Device-IDs
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # DO NOT EDIT THIS FILE!
+2018-06-08 11:29:35 [INFO] >> # Make the configuration modifications to the tablet.cfg
+2018-06-08 11:29:35 [INFO] >> # Tablet.cfg will overwrite the settings from this file, if the tablet is found.
+2018-06-08 11:29:35 [INFO] >> #
+2018-06-08 11:29:35 [INFO] >> # Wacom PTK-450
+2018-06-08 11:29:35 [INFO] >> Tablet 0x056A 0x0029 0xFF0D 0x0001
+2018-06-08 11:29:35 [INFO] Tablet is already defined!
+2018-06-08 11:29:35 [INFO] / End of 'config\wacom.cfg'
+2018-06-08 11:29:35 [INFO] >> Include "config\user.cfg"
+2018-06-08 11:29:35 [INFO] \ Reading 'config\user.cfg'
+2018-06-08 11:29:35 [INFO] / End of 'config\user.cfg'
+2018-06-08 11:29:35 [INFO] / End of 'config\init.cfg'
diff --git a/images/1.png b/images/1.png
new file mode 100644
index 0000000..13efc7b
Binary files /dev/null and b/images/1.png differ
diff --git a/images/2.png b/images/2.png
new file mode 100644
index 0000000..889f71c
Binary files /dev/null and b/images/2.png differ
diff --git a/images/3.png b/images/3.png
new file mode 100644
index 0000000..e15762e
Binary files /dev/null and b/images/3.png differ
diff --git a/images/4.png b/images/4.png
new file mode 100644
index 0000000..d6419b4
Binary files /dev/null and b/images/4.png differ
diff --git a/images/5.png b/images/5.png
new file mode 100644
index 0000000..eafeb57
Binary files /dev/null and b/images/5.png differ
diff --git a/images/6.png b/images/6.png
new file mode 100644
index 0000000..6a87c6c
Binary files /dev/null and b/images/6.png differ
diff --git a/images/7.png b/images/7.png
new file mode 100644
index 0000000..0305c1b
Binary files /dev/null and b/images/7.png differ
diff --git a/images/Big latency and big prediction.png b/images/Big latency and big prediction.png
new file mode 100644
index 0000000..5a1f7c3
Binary files /dev/null and b/images/Big latency and big prediction.png differ
diff --git a/images/Fun.png b/images/Fun.png
new file mode 100644
index 0000000..20f93ec
Binary files /dev/null and b/images/Fun.png differ
diff --git a/images/Smooth 2 + prediction = accurate with small overshot.png b/images/Smooth 2 + prediction = accurate with small overshot.png
new file mode 100644
index 0000000..4e3877f
Binary files /dev/null and b/images/Smooth 2 + prediction = accurate with small overshot.png differ
diff --git a/images/Straight - Pretty good realtime accurate.png b/images/Straight - Pretty good realtime accurate.png
new file mode 100644
index 0000000..555c125
Binary files /dev/null and b/images/Straight - Pretty good realtime accurate.png differ
diff --git a/images/formula_example.png b/images/formula_example.png
new file mode 100644
index 0000000..6f69793
Binary files /dev/null and b/images/formula_example.png differ
diff --git a/images/prediction_formula_example.png b/images/prediction_formula_example.png
new file mode 100644
index 0000000..d80e516
Binary files /dev/null and b/images/prediction_formula_example.png differ
diff --git a/images/prediction_fun.png b/images/prediction_fun.png
new file mode 100644
index 0000000..d4c73cf
Binary files /dev/null and b/images/prediction_fun.png differ
diff --git a/images/prediction_simplesmooth.png b/images/prediction_simplesmooth.png
new file mode 100644
index 0000000..d809fe7
Binary files /dev/null and b/images/prediction_simplesmooth.png differ
diff --git a/images/prediction_straight.png b/images/prediction_straight.png
new file mode 100644
index 0000000..82d6d3a
Binary files /dev/null and b/images/prediction_straight.png differ
diff --git a/images/simple.png b/images/simple.png
new file mode 100644
index 0000000..1e79173
Binary files /dev/null and b/images/simple.png differ
diff --git a/images/smooth.png b/images/smooth.png
new file mode 100644
index 0000000..28379e1
Binary files /dev/null and b/images/smooth.png differ
diff --git a/images/straight.png b/images/straight.png
new file mode 100644
index 0000000..f6f36a6
Binary files /dev/null and b/images/straight.png differ