diff --git a/README.md b/README.md
index d7acb0f..1a84096 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,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.1.zip
#
@@ -31,7 +31,7 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
- Wacom CTL-480
- Wacom CTH-480
- Wacom CTL-490
- - XP Pen G430
+ - XP Pen G430 (New 2016-2017 "Model B")
- XP Pen G640
- Huion 420
- Huion H640P
@@ -39,6 +39,8 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
### Configured, but not properly tested:
- Huion H420
+ - Wacom CTL-4100 USB
+ - Wacom CTL-4100 Bluetooth
- Wacom CTH-470
- Wacom CTH-670
- Wacom CTL-671
@@ -63,10 +65,12 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by
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.
@@ -87,6 +91,15 @@ If you want to compile the code and don't want to install anything from the Tabl
## Changelog
+>**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.
> - Added `-hide` GUI command line parameter. GUI will start as minimized when you run `TabletDriverGUI.exe -hide`
diff --git a/TabletDriverGUI/Configuration.cs b/TabletDriverGUI/Configuration.cs
index 9f91ed9..bd98f71 100644
--- a/TabletDriverGUI/Configuration.cs
+++ b/TabletDriverGUI/Configuration.cs
@@ -26,9 +26,9 @@ 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 Area DesktopSize;
public bool AutomaticDesktopSize;
@@ -78,15 +78,15 @@ public Configuration()
ButtonMap = new int[] { 1, 2, 3 };
DisableButtons = false;
- FilterEnabled = false;
- FilterLatency = 0;
- FilterInterval = 4;
+ SmoothingEnabled = false;
+ SmoothingLatency = 0;
+ SmoothingInterval = 4;
CommandsAfter = new string[] { "" };
CommandsBefore = new string[] { "" };
WindowWidth = 800;
- WindowHeight = 690;
+ WindowHeight = 710;
RunAtStartup = false;
diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml
index 4bc8fae..7e47861 100644
--- a/TabletDriverGUI/MainWindow.xaml
+++ b/TabletDriverGUI/MainWindow.xaml
@@ -5,7 +5,7 @@
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="670" Width="750"
>
-
-
+
+
+
+
+
+
+
+
@@ -48,6 +61,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/Main.cpp b/TabletDriverService/Main.cpp
index b272c6e..d8de81e 100644
--- a/TabletDriverService/Main.cpp
+++ b/TabletDriverService/Main.cpp
@@ -47,12 +47,8 @@ void RunTabletThread() {
bool isResent = false;
double x, y;
- BYTE buttons;
-
-
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
@@ -95,9 +91,6 @@ void RunTabletThread() {
continue;
}
- // Raw position
- // LOG_INFO("Raw position: x=%-5d y=%-5d\n", tablet->reportData.x, tablet->reportData.y);
-
// Debug messages
if(tablet->debugEnabled) {
timeNow = chrono::high_resolution_clock::now();
@@ -105,36 +98,42 @@ void RunTabletThread() {
LOG_DEBUG("STATE: %0.3f, %d, %0.3f, %0.3f, %0.3f\n",
delta,
tablet->state.buttons,
- tablet->state.x,
- tablet->state.y,
+ tablet->state.position.x,
+ tablet->state.position.y,
tablet->state.pressure
);
- //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 filter
+ //
+ if(tablet->filterPacket != NULL && tablet->filterPacket->isEnabled) {
+ tablet->filterPacket->SetTarget(tablet->state.position);
+ tablet->filterPacket->Update();
+ tablet->filterPacket->GetPosition(&tablet->state.position);
+ }
- // Do not write report when filter is enabled
- if(!tablet->filter.isEnabled) {
+
+ // Do not write report when timed filter is enabled
+ if(tablet->filterTimed == NULL || !tablet->filterTimed->isEnabled) {
// Relative mode
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();
@@ -144,14 +143,14 @@ void RunTabletThread() {
// 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,45 +163,43 @@ void RunTabletThread() {
//
// Tablet filter timer callback
//
-VOID CALLBACK FilterTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired) {
- double x, y;
+VOID CALLBACK FilterTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired) {
+ Vector2D position;
+
+ TabletFilter *filter = tablet->filterTimed;
// Filter enabled?
- if(!tablet->filter.isEnabled) return;
+ if(!filter->isEnabled) return;
// Set filter targets
- tablet->filter.targetX = tablet->state.x;
- tablet->filter.targetY = tablet->state.y;
-
- // First report?
- if(tablet->filter.targetX == 0 && tablet->filter.targetY == 0) {
- tablet->filter.x = tablet->filter.targetX;
- tablet->filter.y = tablet->filter.targetY;
- }
+ filter->SetTarget(tablet->state.position);
- // Process filter
- tablet->ProcessFilter();
+ // Update filter position
+ filter->Update();
- // Set filtered output
- x = tablet->filter.x;
- y = tablet->filter.y;
+ // Set output vector
+ filter->GetPosition(&position);
// Relative mode
if(vmulti->mode == VMulti::ModeRelativeMouse) {
// Map position to virtual screen (values between 0 and 1)
- mapper->GetRotatedTabletPosition(&x, &y);
+ mapper->GetRotatedTabletPosition(&position.x, &position.y);
- 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);
+ // Large distance -> Reset relative position
+ double distance = tablet->state.position.Distance(vmulti->relativeData.lastPosition);
if(distance > 10) {
- vmulti->ResetRelativeData(x, y);
+ vmulti->ResetRelativeData(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()
@@ -219,10 +216,15 @@ VOID CALLBACK FilterTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOr
// 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
@@ -335,12 +337,9 @@ int main(int argc, char**argv) {
// Set running state
running = true;
-
-
- // Filter timer
- tablet->filter.callback = FilterTimerCallback;
- tablet->StartFilterTimer();
-
+ // Timed filter timer
+ tablet->filterTimed->callback = FilterTimerCallback;
+ tablet->filterTimed->StartTimer();
// Start the tablet thread
tabletThread = new thread(RunTabletThread);
@@ -358,7 +357,7 @@ int main(int argc, char**argv) {
} else {
LOG_INFO("\n");
}
-
+
//
// Process all other commands
@@ -390,7 +389,9 @@ void CleanupAndExit(int code) {
// Delete filter timer
if(tablet != NULL) {
- tablet->StopFilterTimer();
+ if(tablet->filterTimed != NULL) {
+ tablet->filterTimed->StopTimer();
+ }
}
if(vmulti != NULL) {
diff --git a/TabletDriverService/ProcessCommand.cpp b/TabletDriverService/ProcessCommand.cpp
index 591b2e5..ac59dc2 100644
--- a/TabletDriverService/ProcessCommand.cpp
+++ b/TabletDriverService/ProcessCommand.cpp
@@ -207,8 +207,14 @@ bool ProcessCommand(CommandLine *cmd) {
// Skew
else if(cmd->is("Type")) {
if(tablet == NULL) return false;
+
+ // Wacom Intuos (490)
if(cmd->GetStringLower(0, "") == "wacomintuos") {
- tablet->settings.type = tablet->TypeWacomIntuos;
+ tablet->settings.type = TabletSettings::TypeWacomIntuos;
+
+ // Wacom CTL-4100
+ } else if(cmd->GetStringLower(0, "") == "wacom4100") {
+ tablet->settings.type = TabletSettings::TypeWacom4100;
}
LOG_INFO("Tablet type = %d\n", tablet->settings.type);
}
@@ -418,10 +424,10 @@ bool ProcessCommand(CommandLine *cmd) {
//
// Smoothing filter
//
- else if(cmd->is("Filter")) {
+ else if(cmd->is("Smoothing")) {
if(!CheckTablet()) return true;
- double latency = cmd->GetDouble(0, tablet->GetFilterLatency());
- double threshold = cmd->GetDouble(1, tablet->filter.threshold * 100);
+ double latency = cmd->GetDouble(0, tablet->smoothing.GetLatency());
+ double threshold = cmd->GetDouble(1, tablet->smoothing.threshold * 100);
threshold /= 100;
@@ -439,26 +445,26 @@ bool ProcessCommand(CommandLine *cmd) {
if(threshold > 0.99) threshold = 0.99;
// 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);
+ 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->filter.isEnabled = false;
- LOG_INFO("Filter = off\n");
+ tablet->smoothing.isEnabled = false;
+ LOG_INFO("Smoothing = off\n");
}
}
//
// 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;
@@ -467,15 +473,66 @@ bool ProcessCommand(CommandLine *cmd) {
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.bufferLength);
+ 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.SetBufferLength(length);
+ tablet->noise.distanceThreshold = distanceThreshold;
+ tablet->noise.iterations = iterations;
+
+ // Enable filter
+ if(tablet->noise.bufferLength > 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);
+
+
}
@@ -508,7 +565,7 @@ bool ProcessCommand(CommandLine *cmd) {
Sleep(waitTime);
}
- // Log
+ // Direct logging
else if(cmd->is("LogDirect")) {
logger.ProcessMessages();
logger.directPrint = cmd->GetBoolean(0, logger.directPrint);
@@ -530,55 +587,71 @@ bool ProcessCommand(CommandLine *cmd) {
LogInformation();
}
- // Info
+ // Status
else if(cmd->is("Status")) {
if(!CheckTablet()) return true;
LogStatus();
}
- // Info
+ // Benchmark
else if(cmd->is("Benchmark") || cmd->is("Bench")) {
if(!CheckTablet()) return true;
int timeLimit;
int packetCount = cmd->GetInt(0, 200);
+
+ // Limit packet count
if(packetCount < 10) packetCount = 10;
if(packetCount > 1000) packetCount = 1000;
+
+ // Time limit
timeLimit = packetCount * 10;
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");
- // Log the benchmark result
+ // Benchmark
+ tablet->benchmark.Start(packetCount);
+
+ // Wait for the benchmark to finish
for(int i = 0; i < timeLimit / 100; i++) {
Sleep(100);
+
+ // 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;
}
}
+
+ // Benchmark failed
if(tablet->benchmark.packetCounter > 0) {
LOG_ERROR("Benchmark failed!\n");
LOG_ERROR("Not enough packets captured in %0.2f seconds!\n",
@@ -590,7 +663,7 @@ bool ProcessCommand(CommandLine *cmd) {
}
- // Info
+ // Include
else if(cmd->is("Include")) {
string filename = cmd->GetString(0, "");
if(filename == "") {
diff --git a/TabletDriverService/Tablet.cpp b/TabletDriverService/Tablet.cpp
index 3c37f0c..e44e227 100644
--- a/TabletDriverService/Tablet.cpp
+++ b/TabletDriverService/Tablet.cpp
@@ -9,7 +9,6 @@
// USB Device Constructor
//
Tablet::Tablet(string usbGUID, int stringId, string stringMatch) : Tablet() {
- //_construct();
usbDevice = new USBDevice(usbGUID, stringId, stringMatch);
if(usbDevice->isOpen) {
this->isOpen = true;
@@ -24,7 +23,6 @@ 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) {
this->isOpen = true;
@@ -46,31 +44,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 +53,9 @@ Tablet::Tablet() {
// Reset state
memset(&state, 0, sizeof(state));
- // Reset benchmark
- memset(&benchmark, 0, sizeof(benchmark));
+ // Filters
+ filterTimed = &smoothing;
+ filterPacket = &noise;
// Button map
memset(&buttonMap, 0, sizeof(buttonMap));
@@ -170,113 +144,11 @@ bool Tablet::IsConfigured() {
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];
int buttonIndex;
@@ -299,7 +171,7 @@ int Tablet::ReadPosition() {
//
// Wacom Intuos data format
//
- if(settings.type == TypeWacomIntuos) {
+ if(settings.type == TabletSettings::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);
@@ -307,6 +179,19 @@ int Tablet::ReadPosition() {
reportData.buttons = buffer[1] & ~0x01;
//distance = buffer[9] >> 2;
+ //
+ // Wacom 4100 data format
+ //
+ } else if(settings.type == TabletSettings::TypeWacom4100) {
+
+ reportData.x = (buffer[2] | (buffer[3] << 8) | (buffer[4] << 16) );
+
+ reportData.y = (buffer[5] | (buffer[6] << 8) | (buffer[7] << 16) );
+
+ reportData.pressure = (buffer[8] | (buffer[9] << 8));
+ reportData.reportId = buffer[0];
+ reportData.buttons = buffer[1] & ~0x01;
+
//
// Copy buffer to struct
//
@@ -329,7 +214,7 @@ int Tablet::ReadPosition() {
reportData.buttons |= 1;
}
- // Force tip button if pressure is detected
+ // Force tip button down if pressure is detected
} else if(reportData.pressure > 10) {
reportData.buttons |= 1;
}
@@ -345,8 +230,6 @@ int Tablet::ReadPosition() {
}
-
-
// Set valid
state.isValid = true;
@@ -366,26 +249,15 @@ int Tablet::ReadPosition() {
}
// 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;
+ state.position.x = ((double)reportData.x / (double)settings.maxX) * settings.width;
+ state.position.y = ((double)reportData.y / (double)settings.maxY) * settings.height;
if(settings.skew != 0) {
- state.x += state.y * settings.skew;
+ 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;
diff --git a/TabletDriverService/Tablet.h b/TabletDriverService/Tablet.h
index 257d9cd..4d482f2 100644
--- a/TabletDriverService/Tablet.h
+++ b/TabletDriverService/Tablet.h
@@ -4,6 +4,11 @@
#include "USBDevice.h"
#include "HIDDevice.h"
+#include "TabletSettings.h"
+#include "TabletFilterSmoothing.h"
+#include "TabletFilterNoiseReduction.h"
+#include "TabletBenchmark.h"
+#include "Vector2D.h"
using namespace std;
@@ -18,39 +23,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
//
@@ -69,39 +53,27 @@ class Tablet {
struct {
bool isValid;
BYTE buttons;
- double x;
- double y;
+ Vector2D position;
double pressure;
} 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;
+
+ // Timed filter
+ TabletFilter *filterTimed;
+
+ // Packet filter
+ TabletFilter *filterPacket;
- //
// Benchmark
- //
- struct {
- double minX;
- double maxX;
- double minY;
- double maxY;
- int totalPackets;
- int packetCounter;
- } benchmark;
+ TabletBenchmark benchmark;
// Button map
BYTE buttonMap[16];
@@ -129,18 +101,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..1108c4a
--- /dev/null
+++ b/TabletDriverService/TabletBenchmark.cpp
@@ -0,0 +1,48 @@
+#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..900ca69 100644
--- a/TabletDriverService/TabletDriverService.vcxproj
+++ b/TabletDriverService/TabletDriverService.vcxproj
@@ -175,8 +175,14 @@ xcopy /C /Y "$(ProjectDir)config\init.cfg" "$(SolutionDir)TabletDriverGUI\bin\De
+
+
+
+
+
+
@@ -186,6 +192,11 @@ xcopy /C /Y "$(ProjectDir)config\init.cfg" "$(SolutionDir)TabletDriverGUI\bin\De
+
+
+
+
+
Create
@@ -194,6 +205,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..8e46824 100644
--- a/TabletDriverService/TabletDriverService.vcxproj.filters
+++ b/TabletDriverService/TabletDriverService.vcxproj.filters
@@ -45,6 +45,24 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -77,6 +95,24 @@
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..5ee10dd
--- /dev/null
+++ b/TabletDriverService/TabletFilter.cpp
@@ -0,0 +1,37 @@
+#include "stdafx.h"
+#include "TabletFilter.h"
+
+
+TabletFilter::TabletFilter() {
+ timer = NULL;
+ timerInterval = 2;
+ isValid = false;
+ isEnabled = false;
+}
+
+//
+// Start Timer
+//
+bool TabletFilter::StartTimer() {
+ return CreateTimerQueueTimer(
+ &timer,
+ NULL, callback,
+ NULL,
+ 0,
+ (int)timerInterval,
+ WT_EXECUTEDEFAULT
+ );
+}
+
+
+//
+// Stop Timer
+//
+bool TabletFilter::StopTimer() {
+ if(timer == NULL) return false;
+ bool result = DeleteTimerQueueTimer(NULL, timer, NULL);
+ if(result) {
+ timer = NULL;
+ }
+ return result;
+}
\ No newline at end of file
diff --git a/TabletDriverService/TabletFilter.h b/TabletDriverService/TabletFilter.h
new file mode 100644
index 0000000..9988e6b
--- /dev/null
+++ b/TabletDriverService/TabletFilter.h
@@ -0,0 +1,22 @@
+#pragma once
+class TabletFilter {
+public:
+ virtual void SetTarget(Vector2D vector) = 0;
+ virtual void SetPosition(Vector2D vector) = 0;
+ virtual bool GetPosition(Vector2D *vector) = 0;
+ virtual void Update() = 0;
+
+ HANDLE timer;
+ WAITORTIMERCALLBACK 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..30b201e
--- /dev/null
+++ b/TabletDriverService/TabletFilterNoiseReduction.cpp
@@ -0,0 +1,177 @@
+#include "stdafx.h"
+#include "TabletFilterNoiseReduction.h"
+
+
+//
+// Constructor
+//
+TabletFilterNoiseReduction::TabletFilterNoiseReduction() {
+ bufferMaxLength = sizeof(buffer) / sizeof(Vector2D);
+ bufferLength = 0;
+ bufferPositionCount = 0;
+ bufferCurrentIndex = 0;
+ distanceThreshold = 0;
+ iterations = 10;
+}
+
+//
+// Destructor
+//
+TabletFilterNoiseReduction::~TabletFilterNoiseReduction() {
+}
+
+
+//
+// Set buffer length
+//
+void TabletFilterNoiseReduction::SetBufferLength(int length) {
+ if(length > bufferMaxLength) {
+ bufferLength = bufferMaxLength;
+ } else {
+ bufferLength = length;
+ }
+}
+
+
+//
+// TabletFilter methods
+//
+
+// Set target position
+void TabletFilterNoiseReduction::SetTarget(Vector2D targetVector) {
+ if(isValid) {
+ double distance = lastTarget.Distance(targetVector);
+ if(distance > distanceThreshold) {
+ ResetBuffer();
+ }
+ }
+ lastTarget.Set(targetVector);
+ AddBuffer(targetVector);
+}
+
+// Get current position
+void TabletFilterNoiseReduction::SetPosition(Vector2D vector) {
+ position.x = vector.x;
+ position.y = vector.y;
+}
+
+// Get current position
+bool TabletFilterNoiseReduction::GetPosition(Vector2D *outputVector) {
+ outputVector->x = position.x;
+ outputVector->y = position.y;
+ return true;
+}
+
+// Update
+void TabletFilterNoiseReduction::Update() {
+ GetGeometricMedianVector(&position, iterations);
+}
+
+
+
+void TabletFilterNoiseReduction::ResetBuffer() {
+ bufferPositionCount = 0;
+ bufferCurrentIndex = 0;
+ isValid = false;
+}
+
+//
+// Add position to buffer
+//
+void TabletFilterNoiseReduction::AddBuffer(Vector2D vector) {
+ buffer[bufferCurrentIndex].x = vector.x;
+ buffer[bufferCurrentIndex].y = vector.y;
+ bufferCurrentIndex++;
+ bufferPositionCount++;
+ if(bufferPositionCount > bufferLength) {
+ bufferPositionCount = bufferLength;
+ }
+ if(bufferCurrentIndex >= bufferLength) {
+ bufferCurrentIndex = 0;
+ }
+ isValid = true;
+}
+
+//
+// Average Position Vector
+//
+bool TabletFilterNoiseReduction::GetAverageVector(Vector2D *output) {
+ double x, y;
+ if(!isValid) return false;
+
+ x = y = 0;
+ for(int i = 0; i < bufferPositionCount; i++) {
+ x += buffer[i].x;
+ y += buffer[i].y;
+ }
+ output->x = x / bufferPositionCount;
+ output->y = y / bufferPositionCount;
+ 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 < bufferPositionCount; 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 < bufferPositionCount; 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..7ca33a9
--- /dev/null
+++ b/TabletDriverService/TabletFilterNoiseReduction.h
@@ -0,0 +1,30 @@
+#pragma once
+class TabletFilterNoiseReduction : public TabletFilter {
+public:
+ Vector2D buffer[100];
+ int bufferMaxLength;
+ int bufferLength;
+ int bufferPositionCount;
+ int bufferCurrentIndex;
+ Vector2D position;
+ Vector2D lastTarget;
+ bool isValid;
+
+ int iterations;
+ double distanceThreshold;
+
+ void SetTarget(Vector2D targetVector);
+ void SetPosition(Vector2D vector);
+ bool GetPosition(Vector2D *outputVector);
+ void Update();
+
+ void SetBufferLength(int length);
+ void ResetBuffer();
+ void AddBuffer(Vector2D vector);
+ bool GetAverageVector(Vector2D *output);
+ bool GetGeometricMedianVector(Vector2D *output, int iterations);
+
+ TabletFilterNoiseReduction();
+ ~TabletFilterNoiseReduction();
+};
+
diff --git a/TabletDriverService/TabletFilterSmoothing.cpp b/TabletDriverService/TabletFilterSmoothing.cpp
new file mode 100644
index 0000000..af39330
--- /dev/null
+++ b/TabletDriverService/TabletFilterSmoothing.cpp
@@ -0,0 +1,111 @@
+#include "stdafx.h"
+#include "TabletFilterSmoothing.h"
+
+//
+// Constructor
+//
+TabletFilterSmoothing::TabletFilterSmoothing() {
+ latency = 2.0;
+ weight = 1.000;
+ threshold = 0.9;
+}
+
+
+//
+// Destructor
+//
+TabletFilterSmoothing::~TabletFilterSmoothing() {
+}
+
+
+//
+// TabletFilter methods
+//
+
+// Set target position
+void TabletFilterSmoothing::SetTarget(Vector2D vector) {
+ this->target.x = vector.x;
+ this->target.y = vector.y;
+}
+
+// Set current position
+void TabletFilterSmoothing::SetPosition(Vector2D vector) {
+ position.x = vector.x;
+ position.y = vector.y;
+}
+
+// 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;
+
+ deltaX = target.x - position.x;
+ deltaY = target.y - position.y;
+ distance = sqrt(deltaX*deltaX + deltaY * deltaY);
+
+ // Distance large enough?
+ if(distance > 0.01) {
+ position.x += deltaX * weight;
+ position.y += deltaY * weight;
+
+ // Too small distance -> set output values as target values
+ } else {
+ position.x = target.x;
+ position.y = target.y;
+ }
+
+}
+
+
+
+
+
+// Set position
+double TabletFilterSmoothing::SetPosition(double x, double y) {
+ this->position.x = x;
+ this->position.y = y;
+}
+
+
+//
+// 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..9c6c70b
--- /dev/null
+++ b/TabletDriverService/TabletFilterSmoothing.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Vector2D.h"
+#include "TabletFilter.h"
+
+class TabletFilterSmoothing : public TabletFilter {
+public:
+ double latency;
+ double weight;
+ double threshold;
+ Vector2D target;
+ Vector2D position;
+
+ TabletFilterSmoothing();
+ ~TabletFilterSmoothing();
+
+
+ //double SetTarget(double x, double y);
+ void SetTarget(Vector2D vector);
+ void SetPosition(Vector2D vector);
+ bool GetPosition(Vector2D *outputVector);
+ void Update();
+
+ double SetPosition(double x, double y);
+ 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..228cc6c
--- /dev/null
+++ b/TabletDriverService/TabletSettings.cpp
@@ -0,0 +1,27 @@
+#include "stdafx.h"
+#include "TabletSettings.h"
+
+
+//
+// Constructor
+//
+TabletSettings::TabletSettings() {
+ // Initial settings
+ reportId = 0;
+ reportLength = 8;
+ buttonMask = 0x00;
+ maxX = 1;
+ maxY = 1;
+ maxPressure = 1;
+ clickPressure = 0;
+ width = 1;
+ height = 1;
+ skew = 0;
+ type = TabletNormal;
+}
+
+//
+// Destructor
+//
+TabletSettings::~TabletSettings() {
+}
diff --git a/TabletDriverService/TabletSettings.h b/TabletDriverService/TabletSettings.h
new file mode 100644
index 0000000..f667af7
--- /dev/null
+++ b/TabletDriverService/TabletSettings.h
@@ -0,0 +1,27 @@
+#pragma once
+class TabletSettings {
+public:
+
+ enum TabletType {
+ TabletNormal,
+ TypeWacomIntuos,
+ TypeWacom4100
+ };
+
+ BYTE buttonMask;
+ int maxX;
+ int maxY;
+ int maxPressure;
+ int clickPressure;
+ int keepTipDown;
+ double width;
+ double height;
+ BYTE reportId;
+ int reportLength;
+ double skew;
+ TabletType type;
+
+ TabletSettings();
+ ~TabletSettings();
+};
+
diff --git a/TabletDriverService/VMulti.cpp b/TabletDriverService/VMulti.cpp
index 9272509..727b95c 100644
--- a/TabletDriverService/VMulti.cpp
+++ b/TabletDriverService/VMulti.cpp
@@ -72,10 +72,8 @@ bool VMulti::HasReportChanged() {
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;
}
@@ -125,8 +123,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,8 +134,7 @@ 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) {
diff --git a/TabletDriverService/VMulti.h b/TabletDriverService/VMulti.h
index 3df124e..d37bda7 100644
--- a/TabletDriverService/VMulti.h
+++ b/TabletDriverService/VMulti.h
@@ -1,6 +1,7 @@
#pragma once
#include "HIDDevice.h"
+#include "Vector2D.h"
class VMulti {
private:
@@ -46,12 +47,6 @@ class VMulti {
} reportDigitizer;
- // Position Double
- typedef struct {
- double x;
- double y;
- } PositionDouble;
-
// Position Integer
typedef struct {
int x;
@@ -61,8 +56,8 @@ class VMulti {
// Relative mouse data
struct {
PositionInt currentPosition;
- PositionDouble lastPosition;
- PositionDouble targetPosition;
+ Vector2D lastPosition;
+ Vector2D targetPosition;
double sensitivity;
} relativeData;
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);
+};
+