From 8a9dc7c4fe0c15f1dc5e8bfc4bd183c511bfa60d Mon Sep 17 00:00:00 2001 From: hawku Date: Sat, 24 Mar 2018 17:32:15 +0200 Subject: [PATCH 01/83] Driver restart button --- TabletDriverGUI/MainWindow.xaml | 8 +++++++- TabletDriverGUI/MainWindow.xaml.cs | 21 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml index 7e47861..616a34b 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="750" + Title="TabletDriverGUI" Height="703" Width="750" > + + + + + + + + + + None + Mouse 1 (Left / Tip) + Mouse 2 (Right / Barrel) + Mouse 3 (Middle / Eraser) + Mouse 4 (Back) + Mouse 5 (Forward) + Scroll Up/Down + Scroll Left/Right + Scroll Both + + + + CTRL+SHIFT+Z + + + CTRL+SHIFT+Z + + + + + + + + + diff --git a/TabletDriverGUI/ButtonMapping.xaml.cs b/TabletDriverGUI/ButtonMapping.xaml.cs new file mode 100644 index 0000000..3646def --- /dev/null +++ b/TabletDriverGUI/ButtonMapping.xaml.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +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 +{ + /// + /// Interaction logic for ButtonMapping.xaml + /// + public partial class ButtonMapping : Window + { + // WPF Button + Button button; + + public string Result; + + public ButtonMapping() + { + WindowStartupLocation = WindowStartupLocation.CenterOwner; + Owner = Application.Current.MainWindow; + + InitializeComponent(); + Result = ""; + } + + public ButtonMapping(Button button, bool isPenButton) : this() + { + this.button = button; + + // Tablet buttons don't need tip, barrel and eraser info + if (!isPenButton) + { + ((ComboBoxItem)comboBoxMouse.Items[1]).Content = "Mouse 1 (Left)"; + ((ComboBoxItem)comboBoxMouse.Items[2]).Content = "Mouse 2 (Right)"; + ((ComboBoxItem)comboBoxMouse.Items[3]).Content = "Mouse 3 (Middle)"; + + // Disable scroll + ((ComboBoxItem)comboBoxMouse.Items[6]).Visibility = Visibility.Collapsed; + ((ComboBoxItem)comboBoxMouse.Items[7]).Visibility = Visibility.Collapsed; + ((ComboBoxItem)comboBoxMouse.Items[8]).Visibility = Visibility.Collapsed; + } + + // Enable scroll + else + { + ((ComboBoxItem)comboBoxMouse.Items[6]).Visibility = Visibility.Visible; + ((ComboBoxItem)comboBoxMouse.Items[7]).Visibility = Visibility.Visible; + ((ComboBoxItem)comboBoxMouse.Items[8]).Visibility = Visibility.Visible; + } + CheckButtonValue(); + } + + public void CheckButtonValue() + { + string keys = button.Content.ToString().ToUpper().Trim(); + + if (keys.StartsWith("MOUSE")) + { + switch (keys) + { + case "MOUSE1": comboBoxMouse.SelectedIndex = 1; break; + case "MOUSE2": comboBoxMouse.SelectedIndex = 2; break; + case "MOUSE3": comboBoxMouse.SelectedIndex = 3; break; + case "MOUSE4": comboBoxMouse.SelectedIndex = 4; break; + case "MOUSE5": comboBoxMouse.SelectedIndex = 5; break; + case "MOUSESCROLLV": comboBoxMouse.SelectedIndex = 6; break; + case "MOUSESCROLLH": comboBoxMouse.SelectedIndex = 7; break; + case "MOUSESCROLLB": comboBoxMouse.SelectedIndex = 8; break; + default: break; + } + comboBoxMouse.Focus(); + } + else + { + textKeyboard.Focus(); + } + + textKeyboard.Text = keys; + textCustom.Text = keys; + } + + + // + // Mouse button changed + // + private void ComboBoxMouse_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (!IsLoaded) return; + string key = ""; + + + switch (comboBoxMouse.SelectedIndex) + { + case 1: key = "MOUSE1"; break; + case 2: key = "MOUSE2"; break; + case 3: key = "MOUSE3"; break; + case 4: key = "MOUSE4"; break; + case 5: key = "MOUSE5"; break; + case 6: key = "MOUSESCROLLV"; break; + case 7: key = "MOUSESCROLLH"; break; + case 8: key = "MOUSESCROLLB"; break; + default: break; + } + textKeyboard.Text = key; + textCustom.Text = key; + } + + + // + // Keyboard mapping key down + // + private void TextKeyboard_PreviewKeyDown(object sender, KeyEventArgs e) + { + System.Windows.Forms.KeysConverter keysConverter = new System.Windows.Forms.KeysConverter(); + CultureInfo cultureInfo = new CultureInfo("en-US"); + int keyCode; + List keys = new List(); + HashSet keyAdded = new HashSet(); + foreach (Key value in Enum.GetValues(typeof(Key))) + { + Key key = (Key)value; + if (key > Key.None && Keyboard.IsKeyDown(key)) + { + keyCode = KeyInterop.VirtualKeyFromKey(key); + + + string keyName = keysConverter.ConvertToString(null, cultureInfo, keyCode).ToUpper(); + switch (key) + { + case Key.LeftAlt: keyName = "LALT"; break; + case Key.RightAlt: keyName = "RALT"; break; + case Key.LeftCtrl: keyName = "LCTRL"; break; + case Key.RightCtrl: keyName = "RCTRL"; break; + case Key.LeftShift: keyName = "LSHIFT"; break; + case Key.RightShift: keyName = "RSHIFT"; break; + default: break; + } + if (!keyAdded.Contains(keyName)) + { + keys.Add(keyName); + keyAdded.Add(keyName); + } + } + } + + + keys.Sort( + (a, b) => + { + int value = 0; + if (a.Contains("CTRL")) value--; + if (a.Contains("ALT")) value--; + if (a.Contains("SHIFT")) value--; + if (b.Contains("CTRL")) value++; + if (b.Contains("ALT")) value++; + if (b.Contains("SHIFT")) value++; + return value; + } + ); + + // Set textboxes + string keyText = string.Join("+", keys.ToArray()); + textKeyboard.Text = keyText; + textCustom.Text = keyText; + + comboBoxMouse.SelectedIndex = 0; + + if (e != null) + e.Handled = true; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + + } + + // + // Set + // + private void ButtonSet_Click(object sender, RoutedEventArgs e) + { + if (button != null) + { + Result = textCustom.Text; + DialogResult = true; + Close(); + } + } + + + // + // Clear + // + private void ButtonClear_Click(object sender, RoutedEventArgs e) + { + if (button != null) + { + Result = ""; + DialogResult = true; + Close(); + } + } + + // + // Cancel + // + private void ButtonCancel_Click(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + + // + // Mouse combobox enter press -> set + // + private void ComboBoxMouse_KeyUp(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + ButtonSet_Click(sender, null); + } + } + } +} diff --git a/TabletDriverGUI/Configuration.cs b/TabletDriverGUI/Configuration.cs index bd47fc0..3e821e1 100644 --- a/TabletDriverGUI/Configuration.cs +++ b/TabletDriverGUI/Configuration.cs @@ -28,36 +28,44 @@ public enum OutputModes public Area ScreenArea; // Smoothing filter + public bool SmoothingEnabled; public double SmoothingLatency; public int SmoothingInterval; - public bool SmoothingEnabled; + public bool SmoothingOnlyWhenButtons; // Noise filter + public bool NoiseFilterEnabled; public int NoiseFilterBuffer; public double NoiseFilterThreshold; - public bool NoiseFilterEnabled; // Anti-smoothing filter + public bool AntiSmoothingEnabled; public double AntiSmoothingShape; public double AntiSmoothingCompensation; - public bool AntiSmoothingIgnoreWhenDragging; - public bool AntiSmoothingEnabled; + public bool AntiSmoothingOnlyWhenHover; public Area DesktopSize; public bool AutomaticDesktopSize; [XmlArray("ButtonMap")] [XmlArrayItem("Button")] - public int[] ButtonMap; + public string[] ButtonMap; public bool DisableButtons; - [XmlArray("CommandsAfter")] - [XmlArrayItem("Command")] - public string[] CommandsAfter; + [XmlArray("TabletButtonMap")] + [XmlArrayItem("Button")] + public string[] TabletButtonMap; + public bool DisableTabletButtons; + + public double PressureSensitivity; + public double PressureDeadzone; - [XmlArray("CommandsBefore")] + public double ScrollSensitivity; + public double ScrollAcceleration; + + [XmlArray("CustomCommands")] [XmlArrayItem("Command")] - public string[] CommandsBefore; + public string[] CustomCommands; public int WindowWidth; public int WindowHeight; @@ -90,12 +98,21 @@ public Configuration() DesktopSize = new Area(0, 0, 0, 0); AutomaticDesktopSize = true; - ButtonMap = new int[] { 1, 2, 3 }; + ButtonMap = new string[] { "MOUSE1", "MOUSE2", "MOUSE3" }; DisableButtons = false; + TabletButtonMap = new string[16]; + for (int i = 0; i < 16; i++) TabletButtonMap[i] = ""; + DisableTabletButtons = false; + + PressureSensitivity = 0; + ScrollSensitivity = 1.0; + ScrollAcceleration = 1.0; + SmoothingEnabled = false; SmoothingLatency = 0; SmoothingInterval = 4; + SmoothingOnlyWhenButtons = false; NoiseFilterEnabled = false; NoiseFilterBuffer = 10; @@ -103,10 +120,9 @@ public Configuration() AntiSmoothingEnabled = false; AntiSmoothingShape = 0.5; - AntiSmoothingCompensation = 4.0; + AntiSmoothingCompensation = 20.0; - CommandsAfter = new string[] { "" }; - CommandsBefore = new string[] { "" }; + CustomCommands = new string[] { "" }; WindowWidth = 700; WindowHeight = 710; diff --git a/TabletDriverGUI/MainWindow.Console.cs b/TabletDriverGUI/MainWindow.Console.cs index 6e6c751..060659e 100644 --- a/TabletDriverGUI/MainWindow.Console.cs +++ b/TabletDriverGUI/MainWindow.Console.cs @@ -1,19 +1,14 @@ -using Microsoft.Win32; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Shapes; -using System.Windows.Threading; namespace TabletDriverGUI { @@ -153,15 +148,27 @@ private void TextConsoleInput_PreviewKeyDown(object sender, KeyEventArgs e) // // Command tab complete // - if (e.Key == Key.Tab) + if (e.Key == Key.Tab + || + (e.Key == Key.Space && e.KeyboardDevice.Modifiers == ModifierKeys.Control) + ) { + string fill; string inputText = textConsoleInput.Text.Trim().ToLower(); - string fill = driver.CompleteCommandName(inputText, true); - if (fill != inputText) + if (inputText.StartsWith("help ")) { - textConsoleInput.Text = fill; - textConsoleInput.CaretIndex = textConsoleInput.Text.Length; + fill = driver.CompleteCommandName(inputText.Substring(5), true); + if (fill != null) + textConsoleInput.Text = "Help " + fill; + } + else + { + fill = driver.CompleteCommandName(inputText, true); + if (fill != null) + textConsoleInput.Text = fill; } + if (fill != null) + textConsoleInput.CaretIndex = textConsoleInput.Text.Length; ConsoleBufferToText(); e.Handled = true; } @@ -259,9 +266,53 @@ private void TextCommands_PreviewKeyDown(object sender, KeyEventArgs e) string newText = text; // Set selected text as completed command name - if (completedCommand != commandName) + if (completedCommand != null) { + + // + // Close old tool tip + // + if (textBoxSender.ToolTip != null) + { + ((ToolTip)textBoxSender.ToolTip).IsOpen = false; + ((ToolTip)textBoxSender.ToolTip).IsEnabled = false; + } textBoxSender.SelectedText = completedCommand; + + // Find commands + string foundCommands = "Commands:\n"; + int commandCount = 1; + foreach (var command in driver.Commands) + { + if (command.Key.ToLower().StartsWith(completedCommand.ToLower())) + { + foundCommands += command.Value + " "; + if (commandCount % 10 == 0) foundCommands += "\n"; + } + + commandCount++; + } + + // + // Create tool tip + // + ToolTip toolTip = new ToolTip + { + Placement = System.Windows.Controls.Primitives.PlacementMode.Relative, + PlacementTarget = textBoxSender, + HorizontalOffset = 100 + }; + toolTip.Opened += async delegate (object obj1, RoutedEventArgs eventArgs1) + { + toolTip.Content = foundCommands; + await Task.Delay(3000); + toolTip.IsOpen = false; + toolTip.IsEnabled = false; + textBoxSender.ToolTip = null; + }; + toolTip.IsOpen = true; + textBoxSender.ToolTip = toolTip; + } // Set cursor position diff --git a/TabletDriverGUI/MainWindow.Driver.cs b/TabletDriverGUI/MainWindow.Driver.cs index 34c4453..636a19a 100644 --- a/TabletDriverGUI/MainWindow.Driver.cs +++ b/TabletDriverGUI/MainWindow.Driver.cs @@ -1,19 +1,10 @@ -using Microsoft.Win32; -using System; +using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Shapes; -using System.Windows.Threading; namespace TabletDriverGUI { @@ -25,6 +16,7 @@ public partial class MainWindow : Window // void StartDriver() { + if (running) return; // Try to start the driver @@ -71,26 +63,13 @@ private void SendSettingsToDriver() { if (!driver.IsRunning) return; - // Commands before settings - if (config.CommandsBefore.Length > 0) - { - foreach (string command in config.CommandsBefore) - { - string tmp = command.Trim(); - if (tmp.Length > 0) - { - driver.SendCommand(tmp); - } - } - } - + settingCommands.Clear(); // Desktop size - driver.SendCommand("DesktopSize " + textDesktopWidth.Text + " " + textDesktopHeight.Text); - + settingCommands.Add("DesktopSize " + textDesktopWidth.Text + " " + textDesktopHeight.Text); // Screen area - driver.SendCommand("ScreenArea " + + settingCommands.Add("ScreenArea " + Utils.GetNumberString(config.ScreenArea.Width) + " " + Utils.GetNumberString(config.ScreenArea.Height) + " " + Utils.GetNumberString(config.ScreenArea.X) + " " + Utils.GetNumberString(config.ScreenArea.Y) ); @@ -102,24 +81,24 @@ private void SendSettingsToDriver() // Inverted if (config.Invert) { - driver.SendCommand("TabletArea " + + settingCommands.Add("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)); + settingCommands.Add("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation + 180)); } // Normal else { - driver.SendCommand("TabletArea " + + settingCommands.Add("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)); + settingCommands.Add("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation)); } @@ -127,86 +106,156 @@ private void SendSettingsToDriver() switch (config.OutputMode) { case Configuration.OutputModes.Absolute: - driver.SendCommand("Mode Absolute"); + settingCommands.Add("Mode Absolute"); break; case Configuration.OutputModes.Relative: - driver.SendCommand("Mode Relative"); - driver.SendCommand("RelativeSensitivity " + Utils.GetNumberString(config.ScreenArea.Width / config.TabletArea.Width)); + settingCommands.Add("Mode Relative"); + settingCommands.Add("RelativeSensitivity " + Utils.GetNumberString(config.ScreenArea.Width / config.TabletArea.Width)); break; case Configuration.OutputModes.Digitizer: - driver.SendCommand("Mode Digitizer"); + settingCommands.Add("Mode Digitizer"); break; } - - // Button map + // + // Pen button map + // if (config.DisableButtons) { - driver.SendCommand("ButtonMap 0 0 0"); + settingCommands.Add("ClearButtonMap"); + } + else + { + settingCommands.Add("ClearButtonMap"); + int button = 1; + foreach (string key in config.ButtonMap) + { + settingCommands.Add("ButtonMap " + button + " \"" + key + "\""); + button++; + } + } + + // + // Tablet button map + // + if (config.DisableTabletButtons) + { + settingCommands.Add("ClearAuxButtonMap"); } else { - driver.SendCommand("ButtonMap " + String.Join(" ", config.ButtonMap)); + settingCommands.Add("ClearAuxButtonMap"); + int button = 1; + foreach (string key in config.TabletButtonMap) + { + if (key != "") + { + settingCommands.Add("AuxButtonMap " + button + " \"" + key + "\""); + } + button++; + } } + + // + // Pressure + // + settingCommands.Add("PressureSensitivity " + Utils.GetNumberString(config.PressureSensitivity)); + settingCommands.Add("PressureDeadzone " + Utils.GetNumberString(config.PressureDeadzone)); + + + // + // Scroll + // + settingCommands.Add("ScrollSensitivity " + Utils.GetNumberString(config.ScrollSensitivity)); + settingCommands.Add("ScrollAcceleration " + Utils.GetNumberString(config.ScrollAcceleration)); + + // Smoothing filter if (config.SmoothingEnabled) { - driver.SendCommand("FilterTimerInterval " + Utils.GetNumberString(config.SmoothingInterval)); - driver.SendCommand("Smoothing " + Utils.GetNumberString(config.SmoothingLatency)); + settingCommands.Add("FilterTimerInterval " + Utils.GetNumberString(config.SmoothingInterval)); + settingCommands.Add( + "Smoothing " + Utils.GetNumberString(config.SmoothingLatency) + " 90 " + + (config.SmoothingOnlyWhenButtons ? "true" : "false") + ); } else { - driver.SendCommand("FilterTimerInterval 10"); - driver.SendCommand("Smoothing 0"); + settingCommands.Add("FilterTimerInterval 10"); + settingCommands.Add("Smoothing 0"); } // Noise filter if (config.NoiseFilterEnabled) { - driver.SendCommand("Noise " + Utils.GetNumberString(config.NoiseFilterBuffer) + " " + Utils.GetNumberString(config.NoiseFilterThreshold)); + settingCommands.Add("Noise " + Utils.GetNumberString(config.NoiseFilterBuffer) + " " + Utils.GetNumberString(config.NoiseFilterThreshold)); } else { - driver.SendCommand("Noise 0"); + settingCommands.Add("Noise 0"); } // Anti-smoothing filter if (config.AntiSmoothingEnabled) { - driver.SendCommand("AntiSmoothing " + Utils.GetNumberString(config.AntiSmoothingShape) + " " + + settingCommands.Add("AntiSmoothing " + Utils.GetNumberString(config.AntiSmoothingShape) + " " + Utils.GetNumberString(config.AntiSmoothingCompensation) + " " + - (config.AntiSmoothingIgnoreWhenDragging ? "true" : "false")); + (config.AntiSmoothingOnlyWhenHover ? "true" : "false")); } else { - driver.SendCommand("AntiSmoothing 0"); + settingCommands.Add("AntiSmoothing 0"); } // Debugging - if(config.DebuggingEnabled) + if (config.DebuggingEnabled) { - driver.SendCommand("Debug true"); - } else + settingCommands.Add("Debug true"); + } + else { - driver.SendCommand("Debug false"); + settingCommands.Add("Debug false"); } // Commands after settings - if (config.CommandsAfter.Length > 0) + if (config.CustomCommands.Length > 0) { - foreach (string command in config.CommandsAfter) + foreach (string command in config.CustomCommands) { string tmp = command.Trim(); if (tmp.Length > 0) { - driver.SendCommand(tmp); + settingCommands.Add(tmp); } } } + + // + // Send commands to the driver + // + foreach (string command in settingCommands) + { + // Skip comments + if (command.StartsWith("#")) continue; + + driver.SendCommand(command); + } + + // + // Write settings to usersettings.cfg + // + try + { + File.WriteAllLines("config\\usersettings.cfg", settingCommands.ToArray()); + } + catch (Exception) + { + } + } @@ -348,7 +397,32 @@ private void ProcessStatusMessage(string variableName, string parameters) } + if (variableName == "aux_buttons") + { + if (Utils.ParseNumber(parameters, out double test)) + { + tabletButtonCount = (int)test; + if(tabletButtonCount > 0) + { + for (int i = 0; i < 16; i++) + { + GroupBox box = (GroupBox)wrapPanelTabletButtons.Children[i]; + if (i >= tabletButtonCount) { + box.Visibility = Visibility.Collapsed; + } + else + { + box.Visibility = Visibility.Visible; + } + } + groupBoxTabletButtons.Visibility = Visibility.Visible; + + } + if (isFirstStart) + SendSettingsToDriver(); + } + } } @@ -358,7 +432,7 @@ private void ProcessStatusMessage(string variableName, string parameters) private void OnDriverStarted(object sender, EventArgs e) { // Debugging commands - if(config.DebuggingEnabled) + if (config.DebuggingEnabled) { driver.SendCommand("HIDList"); } @@ -423,6 +497,7 @@ private void OnDriverStopped(object sender, EventArgs e) { Title = "TabletDriverGUI"; notifyIcon.Text = "No tablet found"; + groupBoxTabletButtons.Visibility = Visibility.Collapsed; }); } diff --git a/TabletDriverGUI/MainWindow.Ink.cs b/TabletDriverGUI/MainWindow.Ink.cs new file mode 100644 index 0000000..dacaf8a --- /dev/null +++ b/TabletDriverGUI/MainWindow.Ink.cs @@ -0,0 +1,200 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace TabletDriverGUI +{ + public partial class MainWindow : Window + { + + // + // Ink canvas stylys move + // + private void InkCanvas_StylusMove(object sender, StylusEventArgs e) + { + double pressure = 0; + int count = 0; + StylusPointCollection points = e.GetStylusPoints(inkCanvas); + foreach (var point in points) + { + pressure += point.PressureFactor; + count++; + } + progressPressure.Value = pressure / count; + } + + + // + // Ink canvas stylus up + // + private void InkCanvas_StylusUp(object sender, StylusEventArgs e) + { + progressPressure.Value = 0; + + Random random = new Random(); + byte shade = (byte)random.Next(0x33, 0x77); + inkCanvasDrawingAttributes.Color = Color.FromRgb(shade, shade, shade); + if (inkCanvasUndoHistory != null && inkCanvasUndoHistory.Count > 0) + { + inkCanvasUndoHistory.Clear(); + } + } + + + // + // Ink canvas key down + // + private void InkCanvas_KeyDown(object sender, KeyEventArgs e) + { + // Ctrl + Z undo + if (e.Key == Key.Z && e.KeyboardDevice.Modifiers == ModifierKeys.Control) + { + + ButtonInkUndo_Click(sender, null); + } + + // Ctrl + Y redo + if (e.Key == Key.Y && e.KeyboardDevice.Modifiers == ModifierKeys.Control) + { + ButtonInkRedo_Click(sender, null); + } + + } + + + // + // Windows Ink pressure sensitivity changed + // + private void SliderPressure_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + sliderPressureSensitivity.ToolTip = Utils.GetNumberString(-sliderPressureSensitivity.Value); + sliderPressureDeadzone.ToolTip = Utils.GetNumberString(sliderPressureDeadzone.Value * 100) + "%"; + + if (isLoadingSettings) return; + + config.PressureSensitivity = sliderPressureSensitivity.Value; + config.PressureDeadzone = sliderPressureDeadzone.Value; + + + driver.SendCommand("PressureSensitivity " + Utils.GetNumberString(config.PressureSensitivity)); + driver.SendCommand("PressureDeadzone " + Utils.GetNumberString(config.PressureDeadzone)); + } + + + // + // Clear ink canvas + // + private void ButtonInkClear_Click(object sender, RoutedEventArgs e) + { + inkCanvas.Strokes.Clear(); + inkCanvasUndoHistory.Clear(); + } + + + // + // Undo ink canvas + // + private void ButtonInkUndo_Click(object sender, RoutedEventArgs e) + { + if (inkCanvas.Strokes.Count > 0) + { + inkCanvasUndoHistory.Add(inkCanvas.Strokes[inkCanvas.Strokes.Count - 1]); + inkCanvas.Strokes.RemoveAt(inkCanvas.Strokes.Count - 1); + } + } + + + // + // Redo ink canvas + // + private void ButtonInkRedo_Click(object sender, RoutedEventArgs e) + { + if (inkCanvasUndoHistory.Count > 0) + { + inkCanvas.Strokes.Add(inkCanvasUndoHistory.Last()); + inkCanvasUndoHistory.RemoveAt(inkCanvasUndoHistory.Count - 1); + } + } + + + // + // Save ink canvas + // + private void ButtonInkSave_Click(object sender, RoutedEventArgs e) + { + SaveFileDialog fileDialog = new SaveFileDialog + { + DefaultExt = ".png", + Filter = "PNG Files (*.png)|*.png", + FileName = "inktest_" + DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".png" + }; + + // File selection OK? + if (fileDialog.ShowDialog() == true) + { + try + { + RenderInkCanvasToPNG(inkCanvas, 2.0, fileDialog.FileName); + } + catch (Exception ex) + { + MessageBox.Show("Saving failed!\n" + ex.Message, "ERROR!", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + + // + // Render Ink canvas to PNG + // + private void RenderInkCanvasToPNG(InkCanvas canvas, double scale, string filepath) + { + double inkWidth = canvas.ActualWidth * scale; + double inkHeight = canvas.ActualHeight * scale; + + // Draw ink canvas to drawing visual + DrawingVisual drawingVisual = new DrawingVisual(); + using (DrawingContext context = drawingVisual.RenderOpen()) + { + VisualBrush visualBrush = new VisualBrush(canvas); + Brush borderBrush = new SolidColorBrush(inkCanvas.DefaultDrawingAttributes.Color); + Pen borderPen = new Pen(borderBrush, 5); + context.DrawRectangle(visualBrush, borderPen, new Rect(0, 0, inkWidth, inkHeight)); + } + + // Render drawing visual to a bitmap + RenderTargetBitmap targetBitmap = new RenderTargetBitmap( + (int)inkWidth, + (int)inkHeight, + 96, 96, + PixelFormats.Default + ); + targetBitmap.Render(drawingVisual); + + // Encode bitmap to PNG + PngBitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(targetBitmap)); + + // Save PNG to a file + FileStream fileStream = File.Open(filepath, FileMode.OpenOrCreate); + encoder.Save(fileStream); + + // Close the file + fileStream.Close(); + + encoder = null; + targetBitmap = null; + drawingVisual = null; + } + + } +} diff --git a/TabletDriverGUI/MainWindow.Settings.cs b/TabletDriverGUI/MainWindow.Settings.cs index 10f8643..d530a17 100644 --- a/TabletDriverGUI/MainWindow.Settings.cs +++ b/TabletDriverGUI/MainWindow.Settings.cs @@ -2,26 +2,20 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Interop; using System.Windows.Media; using System.Windows.Shapes; -using System.Windows.Threading; namespace TabletDriverGUI { public partial class MainWindow : Window { - - #region Driver configuration stuff + #region Configuration Load / Update // // Load settings from configuration @@ -51,6 +45,20 @@ private void LoadSettingsFromConfiguration() radioModeDigitizer.IsChecked = true; break; } + + + // + // Windows Ink pressure + // + if (config.OutputMode == Configuration.OutputModes.Digitizer) + groupBoxWindowsInkSettings.IsEnabled = true; + else + groupBoxWindowsInkSettings.IsEnabled = false; + + + // + // Rotation + // textTabletAreaRotation.Text = Utils.GetNumberString(config.TabletArea.Rotation); checkBoxInvert.IsChecked = config.Invert; @@ -103,7 +111,9 @@ private void LoadSettingsFromConfiguration() checkBoxAutomaticDesktopSize.IsChecked = config.AutomaticDesktopSize; + // // Force aspect ratio + // if (config.ForceAspectRatio) { config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height); @@ -121,36 +131,76 @@ private void LoadSettingsFromConfiguration() // - // Buttons + // Pen buttons // if (config.ButtonMap.Count() == 3) { - comboBoxButton1.SelectedIndex = config.ButtonMap[0]; - comboBoxButton2.SelectedIndex = config.ButtonMap[1]; - comboBoxButton3.SelectedIndex = config.ButtonMap[2]; + buttonPenButton1.Content = config.ButtonMap[0]; + buttonPenButton2.Content = config.ButtonMap[1]; + buttonPenButton3.Content = config.ButtonMap[2]; } else { - config.ButtonMap = new int[] { 1, 2, 3 }; + config.ButtonMap = new string[] { "MOUSE1", "MOUSE2", "MOUSE3" }; } checkBoxDisableButtons.IsChecked = config.DisableButtons; + // + // Tablet buttons + // + if (config.TabletButtonMap.Count() == 16) + { + for (int i = 0; i < 16; i++) + { + GroupBox box = (GroupBox)wrapPanelTabletButtons.Children[i]; + Button button = (Button)(box.Content); + button.Content = config.TabletButtonMap[i]; + } + } + else + { + config.TabletButtonMap = new string[16]; + for (int i = 0; i < 16; i++) config.TabletButtonMap[i] = ""; + } + if (wrapPanelTabletButtons.Children.Count == 17) + { + ((CheckBox)wrapPanelTabletButtons.Children[16]).IsChecked = config.DisableTabletButtons; + } + + + // + // Pressure + // + sliderPressureSensitivity.Value = config.PressureSensitivity; + sliderPressureDeadzone.Value = config.PressureDeadzone; + + + // + // Scroll + // + textScrollSensitivity.Text = Utils.GetNumberString(config.ScrollSensitivity); + textScrollAcceleration.Text = Utils.GetNumberString(config.ScrollAcceleration); + + // // Smoothing filter // checkBoxSmoothing.IsChecked = config.SmoothingEnabled; textSmoothingLatency.Text = Utils.GetNumberString(config.SmoothingLatency); comboBoxSmoothingRate.SelectedIndex = config.SmoothingInterval - 1; + checkBoxSmoothingOnlyWhenButtons.IsChecked = config.SmoothingOnlyWhenButtons; if (config.SmoothingEnabled) { textSmoothingLatency.IsEnabled = true; comboBoxSmoothingRate.IsEnabled = true; + checkBoxSmoothingOnlyWhenButtons.IsEnabled = true; } else { textSmoothingLatency.IsEnabled = false; comboBoxSmoothingRate.IsEnabled = false; + checkBoxSmoothingOnlyWhenButtons.IsEnabled = false; } @@ -176,20 +226,20 @@ private void LoadSettingsFromConfiguration() // Anti-smoothing filter // checkBoxAntiSmoothing.IsChecked = config.AntiSmoothingEnabled; - textAntiSmoothingShape.Text = Utils.GetNumberString(config.AntiSmoothingShape, "0.00"); - textAntiSmoothingCompensation.Text = Utils.GetNumberString(config.AntiSmoothingCompensation, "0.00"); - checkBoxAntiSmoothingIgnoreWhenDragging.IsChecked = config.AntiSmoothingIgnoreWhenDragging; + textAntiSmoothingShape.Text = Utils.GetNumberString(config.AntiSmoothingShape); + textAntiSmoothingCompensation.Text = Utils.GetNumberString(config.AntiSmoothingCompensation); + checkBoxAntiSmoothingOnlyWhenHover.IsChecked = config.AntiSmoothingOnlyWhenHover; if (config.AntiSmoothingEnabled) { textAntiSmoothingShape.IsEnabled = true; textAntiSmoothingCompensation.IsEnabled = true; - checkBoxAntiSmoothingIgnoreWhenDragging.IsEnabled = true; + checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = true; } else { textAntiSmoothingShape.IsEnabled = false; textAntiSmoothingCompensation.IsEnabled = false; - checkBoxAntiSmoothingIgnoreWhenDragging.IsEnabled = false; + checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = false; } // @@ -206,20 +256,12 @@ private void LoadSettingsFromConfiguration() // Custom commands // string tmp = ""; - foreach (string command in config.CommandsBefore) - { - if (command.Trim().Length > 0) - tmp += command.Trim() + "\n"; - } - textCommandsBefore.Text = tmp; - - tmp = ""; - foreach (string command in config.CommandsAfter) + foreach (string command in config.CustomCommands) { if (command.Trim().Length > 0) tmp += command.Trim() + "\n"; } - textCommandsAfter.Text = tmp; + textCustomCommands.Text = tmp; // // Debugging @@ -245,7 +287,10 @@ private void UpdateSettingsToConfiguration() bool oldValue; + + // // Tablet area + // if (Utils.ParseNumber(textTabletAreaWidth.Text, out double val)) config.TabletArea.Width = val; if (Utils.ParseNumber(textTabletAreaHeight.Text, out val)) @@ -261,13 +306,18 @@ private void UpdateSettingsToConfiguration() 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; + // // Force full area + // if (config.ForceFullArea) { // Set tablet area size to full area @@ -286,10 +336,16 @@ private void UpdateSettingsToConfiguration() } + + // // Force the tablet area to be inside of the full area + // config.TabletArea.MoveInside(config.TabletFullArea); + + // // Screen area + // if (Utils.ParseNumber(textScreenAreaWidth.Text, out val)) config.ScreenArea.Width = val; if (Utils.ParseNumber(textScreenAreaHeight.Text, out val)) @@ -300,7 +356,9 @@ private void UpdateSettingsToConfiguration() config.ScreenArea.Y = val; + // // Desktop size + // if (Utils.ParseNumber(textDesktopWidth.Text, out val)) config.DesktopSize.Width = val; if (Utils.ParseNumber(textDesktopHeight.Text, out val)) @@ -315,7 +373,9 @@ private void UpdateSettingsToConfiguration() } + // // Force aspect ratio + // if (config.ForceAspectRatio) { config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height); @@ -323,32 +383,72 @@ private void UpdateSettingsToConfiguration() } + // // Button map - config.ButtonMap[0] = comboBoxButton1.SelectedIndex; - config.ButtonMap[1] = comboBoxButton2.SelectedIndex; - config.ButtonMap[2] = comboBoxButton3.SelectedIndex; + // + config.ButtonMap[0] = buttonPenButton1.Content.ToString(); + config.ButtonMap[1] = buttonPenButton2.Content.ToString(); + config.ButtonMap[2] = buttonPenButton3.Content.ToString(); config.DisableButtons = (bool)checkBoxDisableButtons.IsChecked; + // + // Tablet button map + // + for (int i = 0; i < 16; i++) + { + GroupBox box = (GroupBox)wrapPanelTabletButtons.Children[i]; + Button button = (Button)(box.Content); + config.TabletButtonMap[i] = button.Content.ToString(); + } + if (wrapPanelTabletButtons.Children.Count == 17) + { + config.DisableTabletButtons = (bool)(((CheckBox)wrapPanelTabletButtons.Children[16]).IsChecked); + } + + + // + // Pressure sensitivity + // + config.PressureSensitivity = sliderPressureSensitivity.Value; + config.PressureDeadzone = sliderPressureDeadzone.Value; + + // + // Scroll + // + if (Utils.ParseNumber(textScrollSensitivity.Text, out val)) + config.ScrollSensitivity = val; + if (Utils.ParseNumber(textScrollAcceleration.Text, out val)) + config.ScrollAcceleration = val; + + + // // Smoothing filter + // config.SmoothingEnabled = (bool)checkBoxSmoothing.IsChecked; config.SmoothingInterval = comboBoxSmoothingRate.SelectedIndex + 1; if (Utils.ParseNumber(textSmoothingLatency.Text, out val)) config.SmoothingLatency = val; + config.SmoothingOnlyWhenButtons = (bool)checkBoxSmoothingOnlyWhenButtons.IsChecked; if (config.SmoothingEnabled) { textSmoothingLatency.IsEnabled = true; comboBoxSmoothingRate.IsEnabled = true; + checkBoxSmoothingOnlyWhenButtons.IsEnabled = true; } else { textSmoothingLatency.IsEnabled = false; comboBoxSmoothingRate.IsEnabled = false; + checkBoxSmoothingOnlyWhenButtons.IsEnabled = false; } + + // // Noise filter + // config.NoiseFilterEnabled = (bool)checkBoxNoiseFilter.IsChecked; if (Utils.ParseNumber(textNoiseBuffer.Text, out val)) config.NoiseFilterBuffer = (int)val; @@ -365,26 +465,30 @@ private void UpdateSettingsToConfiguration() textNoiseThreshold.IsEnabled = false; } + + // // Anti-smoothing filter + // config.AntiSmoothingEnabled = (bool)checkBoxAntiSmoothing.IsChecked; if (Utils.ParseNumber(textAntiSmoothingShape.Text, out val)) config.AntiSmoothingShape = val; if (Utils.ParseNumber(textAntiSmoothingCompensation.Text, out val)) config.AntiSmoothingCompensation = val; - config.AntiSmoothingIgnoreWhenDragging = (bool)checkBoxAntiSmoothingIgnoreWhenDragging.IsChecked; + config.AntiSmoothingOnlyWhenHover = (bool)checkBoxAntiSmoothingOnlyWhenHover.IsChecked; if (config.AntiSmoothingEnabled) { textAntiSmoothingShape.IsEnabled = true; textAntiSmoothingCompensation.IsEnabled = true; - checkBoxAntiSmoothingIgnoreWhenDragging.IsEnabled = true; + checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = true; } else { textAntiSmoothingShape.IsEnabled = false; textAntiSmoothingCompensation.IsEnabled = false; - checkBoxAntiSmoothingIgnoreWhenDragging.IsEnabled = false; + checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = false; } + // // Automatic restart // @@ -400,18 +504,14 @@ private void UpdateSettingsToConfiguration() SetRunAtStartup(config.RunAtStartup); + // // Custom commands + // List commandList = new List(); - foreach (string command in textCommandsBefore.Text.Split('\n')) - if (command.Trim().Length > 0) - commandList.Add(command.Trim()); - config.CommandsBefore = commandList.ToArray(); - - commandList.Clear(); - foreach (string command in textCommandsAfter.Text.Split('\n')) + foreach (string command in textCustomCommands.Text.Split('\n')) if (command.Trim().Length > 0) commandList.Add(command.Trim()); - config.CommandsAfter = commandList.ToArray(); + config.CustomCommands = commandList.ToArray(); // @@ -420,6 +520,7 @@ private void UpdateSettingsToConfiguration() config.DebuggingEnabled = (bool)checkBoxDebugging.IsChecked; + // Update canvases UpdateCanvasElements(); } @@ -437,6 +538,15 @@ private void SaveSettings(object sender, RoutedEventArgs e) { config.Write(configFilename); SendSettingsToDriver(); + + // + // Enable/Disable Windows Ink pressure settings + // + if (config.OutputMode == Configuration.OutputModes.Digitizer) + groupBoxWindowsInkSettings.IsEnabled = true; + else + groupBoxWindowsInkSettings.IsEnabled = false; + SetStatus("Settings saved!"); } catch (Exception) @@ -450,16 +560,6 @@ private void SaveSettings(object sender, RoutedEventArgs e) } - // - // Apply settings - // - private void ApplySettings(object sender, RoutedEventArgs e) - { - SendSettingsToDriver(); - SetStatus("Settings applied!"); - } - - // // Set run at startup // @@ -662,9 +762,6 @@ void CreateCanvasElements() }; canvasTabletArea.Children.Add(textTabletAspectRatio); - - - // // Canvas mouse drag // @@ -908,6 +1005,7 @@ private void Canvas_MouseMove(object sender, MouseEventArgs e) dx = 0; if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) dy = 0; + // Screen map canvas if (mouseDrag.Source == canvasScreenMap) @@ -918,6 +1016,12 @@ private void Canvas_MouseMove(object sender, MouseEventArgs e) if (scaleX > scaleY) scale = scaleX; + if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) + { + dx = Math.Round(dx * scale / 80.0) * 80.0 / scale; + dy = Math.Round(dy * scale / 80.0) * 80.0 / scale; + } + config.ScreenArea.X = mouseDrag.OriginDraggable.X + dx * scale; config.ScreenArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; UpdateScreenMapCanvas(); @@ -932,6 +1036,12 @@ private void Canvas_MouseMove(object sender, MouseEventArgs e) if (scaleX > scaleY) scale = scaleX; + if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) + { + dx = Math.Round(dx * scale / 5.0) * 5.0 / scale; + dy = Math.Round(dy * scale / 5.0) * 5.0 / scale; + } + config.TabletArea.X = mouseDrag.OriginDraggable.X + dx * scale; config.TabletArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; @@ -985,20 +1095,6 @@ private void CheckboxChanged(object sender, RoutedEventArgs e) } - // Disable button map selection when buttons are disabled - if (checkBoxDisableButtons.IsChecked == true) - { - comboBoxButton1.IsEnabled = false; - comboBoxButton2.IsEnabled = false; - comboBoxButton3.IsEnabled = false; - } - else - { - comboBoxButton1.IsEnabled = true; - comboBoxButton2.IsEnabled = true; - comboBoxButton3.IsEnabled = true; - } - // Disable desktop size settings when automatic is checked if (checkBoxAutomaticDesktopSize.IsChecked == true) { @@ -1130,7 +1226,7 @@ private void ComboBoxMonitor_SelectionChanged(object sender, SelectionChangedEve index--; // Monitors - if (index >= 0 && index < screens.Length) + if (index >= 0 && index <= screens.Length && screens.Length > 1) { textScreenAreaX.Text = Utils.GetNumberString(screens[index].Bounds.X - minX); textScreenAreaY.Text = Utils.GetNumberString(screens[index].Bounds.Y - minY); @@ -1176,6 +1272,49 @@ private void ComboBoxMonitor_SelectionChanged(object sender, SelectionChangedEve } + // + // Button mapping click + // + private void ButtonMap_Click(object sender, RoutedEventArgs e) + { + Button button = (Button)sender; + //MessageBox.Show(button.Content.ToString()); + + bool isPenButton = false; + + if (sender == buttonPenButton1) isPenButton = true; + else if (sender == buttonPenButton2) isPenButton = true; + else if (sender == buttonPenButton3) isPenButton = true; + + + ButtonMapping buttonMapping = new ButtonMapping(button, isPenButton); + buttonMapping.ShowDialog(); + if (buttonMapping.DialogResult == true) + { + button.Content = buttonMapping.Result.ToUpper(); + UpdateSettingsToConfiguration(); + } + + } + + + // + // Button map tooltip opening + // + private void ButtonMap_ToolTipOpening(object sender, ToolTipEventArgs e) + { + Button button = (Button)sender; + if (button.Content.ToString() == "") + { + button.ToolTip = "Empty"; + } + else + { + button.ToolTip = button.Content; + } + } + + // // Main Menu Click // @@ -1243,6 +1382,5 @@ private void MainMenuClick(object sender, RoutedEventArgs e) } - } } diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml index 9530402..811449e 100644 --- a/TabletDriverGUI/MainWindow.xaml +++ b/TabletDriverGUI/MainWindow.xaml @@ -13,7 +13,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -34,19 +34,29 @@ - + - Settings + + + - + - + @@ -56,9 +66,11 @@ - You can drag the screen area with a mouse. - - Drag + Control = Move area in X direction. + - Control + Drag = Move area in X direction. + + - Shift + Drag = Move area Y direction. - - Drag + Shift = Move area Y direction. + - Alt + Drag = Move area 80 pixels at a time. - Use the "Set Area" to set the screen area to single monitor or full desktop. @@ -135,7 +147,10 @@ - + @@ -144,10 +159,12 @@ - You can drag the tablet area with a mouse. - - Drag + Control = Move area in X direction. + - Control + Drag = Move area in X direction. - - Drag + Shift = Move area Y direction. + - Shift + Drag = Move area Y direction. + - Alt + Drag = Move area 5 mm at a time. + - 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. @@ -329,130 +346,336 @@ - - - - - - - - Button Mapping - - - - You can disable a single button by selecting "Disable" from the list. + + + + + + + + + + + + + + + + + + + + + + + + Pen Buttons + + + - You can disable a single button by clicking "Clear" in the mapping dialog. - - The "Disable buttons" checkbox will disable all tablet buttons. - - - + - The "Disable buttons" checkbox will disable all pen buttons. + + - Use negative number in scroll sensitivity to invert the direction. + + - Scroll acceleration 1.0 = No acceleration. + + + - + - - - + + + + + + + - - + + + + + - - - + + + + + + - - Disable buttons - - + + + + Disable + + buttons + + + + + + 1.0 + + + - - - - - - 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. - + + + + + 1.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - 1920 - - - + + + + + + + + - - - - 1080 - - - + + - + ToolTip="Saves settings to config.xml and send setting commands to driver">Save settings diff --git a/TabletDriverGUI/MainWindow.xaml.cs b/TabletDriverGUI/MainWindow.xaml.cs index 9c05c63..b669fc0 100644 --- a/TabletDriverGUI/MainWindow.xaml.cs +++ b/TabletDriverGUI/MainWindow.xaml.cs @@ -9,6 +9,7 @@ using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; +using System.Windows.Ink; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; @@ -24,7 +25,7 @@ public partial class MainWindow : Window { // Version - public string Version = "0.2.1"; + public string Version = "0.2.2"; // Console stuff private List commandHistory; @@ -37,8 +38,9 @@ public partial class MainWindow : Window // Driver private TabletDriver driver; private Dictionary driverCommands; + private List settingCommands; private bool running; - + private int tabletButtonCount; // Timers private DispatcherTimer timerStatusbar; @@ -83,6 +85,14 @@ public MouseDrag() // Measurement to area private bool isEnabledMeasurementToArea = false; + // Ink canvas undo history + StrokeCollection inkCanvasUndoHistory; + + // Ink canvas DrawingAttributes + DrawingAttributes inkCanvasDrawingAttributes; + + + // // Constructor // @@ -133,6 +143,9 @@ public MainWindow() commandHistory = new List { "" }; commandHistoryIndex = 0; + // Init setting commands list + settingCommands = new List(); + // Init tablet driver driver = new TabletDriver("TabletDriverService.exe"); driverCommands = new Dictionary(); @@ -171,23 +184,43 @@ public MainWindow() // - // Buttom Map ComboBoxes + // Hide tablet button mapping + // + groupBoxTabletButtons.Visibility = Visibility.Collapsed; + + // + // Create tablet button map WrapPanel items // - comboBoxButton1.Items.Clear(); - comboBoxButton2.Items.Clear(); - comboBoxButton3.Items.Clear(); - comboBoxButton1.Items.Add("Disable"); - comboBoxButton2.Items.Add("Disable"); - comboBoxButton3.Items.Add("Disable"); - for (int i = 1; i <= 5; i++) + for (int i = 0; i < 16; i++) { - comboBoxButton1.Items.Add("Mouse " + i); - comboBoxButton2.Items.Add("Mouse " + i); - comboBoxButton3.Items.Add("Mouse " + i); + GroupBox groupBox = new GroupBox + { + Width = 90, + Header = "Button " + (i + 1).ToString() + }; + Button button = new Button + { + Height = 22, + Content = "", + Padding = new Thickness(2,0,2,0), + ToolTip = "Empty", + Background = Brushes.White + }; + button.Click += ButtonMap_Click; + button.ToolTipOpening += ButtonMap_ToolTipOpening; + + groupBox.Content = button; + wrapPanelTabletButtons.Children.Add(groupBox); } - comboBoxButton1.SelectedIndex = 0; - comboBoxButton2.SelectedIndex = 0; - comboBoxButton3.SelectedIndex = 0; + CheckBox checkBox = new CheckBox + { + Content = "Disable buttons" + }; + checkBox.Checked += CheckboxChanged; + checkBox.Unchecked += CheckboxChanged; + checkBox.VerticalAlignment = VerticalAlignment.Bottom; + checkBox.Margin = new Thickness(5, 5, 5, 10); + wrapPanelTabletButtons.Children.Add(checkBox); // @@ -200,6 +233,18 @@ public MainWindow() } comboBoxSmoothingRate.SelectedIndex = 3; + + // Ink canvas undo history + inkCanvasUndoHistory = new StrokeCollection(); + + // Ink canvas drawing attributes + inkCanvasDrawingAttributes = new DrawingAttributes(); + inkCanvasDrawingAttributes.Width = 10; + inkCanvasDrawingAttributes.Height = 10; + inkCanvasDrawingAttributes.Color = Color.FromRgb(0x55, 0x55, 0x55); + inkCanvasDrawingAttributes.StylusTip = StylusTip.Ellipse; + inkCanvas.DefaultDrawingAttributes = inkCanvasDrawingAttributes; + // Process command line arguments ProcessCommandLineArguments(); @@ -559,6 +604,5 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b #endregion - } } \ No newline at end of file diff --git a/TabletDriverGUI/TabletDriver.cs b/TabletDriverGUI/TabletDriver.cs index 9a0b84d..2d90888 100644 --- a/TabletDriverGUI/TabletDriver.cs +++ b/TabletDriverGUI/TabletDriver.cs @@ -42,6 +42,7 @@ public DriverEventArgs(DriverEventType type, string message, string parameters) private readonly int ConsoleMaxLines; private System.Threading.Mutex mutexConsoleUpdate; private Dictionary commands; + public Dictionary Commands { get { return commands; } } // Other variables private readonly string servicePath; @@ -66,6 +67,7 @@ public TabletDriver(string servicePath) commands = new Dictionary(); + } // @@ -134,14 +136,13 @@ public void ConsoleUnlock() mutexConsoleUpdate.ReleaseMutex(); } - // // Command name complete // public string CompleteCommandName(string inputText, bool showCommands) { List commandsFound = new List(); - string result = inputText; + string result = null; // Find commands foreach (var item in commands) diff --git a/TabletDriverGUI/TabletDriverGUI.csproj b/TabletDriverGUI/TabletDriverGUI.csproj index 77e01f0..b85d398 100644 --- a/TabletDriverGUI/TabletDriverGUI.csproj +++ b/TabletDriverGUI/TabletDriverGUI.csproj @@ -77,8 +77,12 @@ MSBuild:Compile Designer + + ButtonMapping.xaml + + @@ -87,6 +91,10 @@ WacomArea.xaml + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/TabletDriverService/CommandHandler.Auxiliary.cpp b/TabletDriverService/CommandHandler.Auxiliary.cpp new file mode 100644 index 0000000..1bfd73f --- /dev/null +++ b/TabletDriverService/CommandHandler.Auxiliary.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "CommandHandler.h" + +#define LOG_MODULE "" +#include "Logger.h" + +// +// Create auxiliary device commands +// +void CommandHandler::CreateAuxCommands() { + + // + // Command: AuxHID + // + AddCommand(new Command("AuxHID", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + 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->hidDeviceAux == NULL) { + tablet->hidDeviceAux = new HIDDevice(vendorID, productID, usagePage, usage); + if(tablet->hidDeviceAux->isOpen) { + LOG_INFO("HID auxiliary device found!\n"); + return true; + } + else { + LOG_ERROR("Can't open auxiliary HID 0x%04X 0x%04X 0x%04X 0x%04X\n", vendorID, productID, usagePage, usage); + delete tablet->hidDeviceAux; + tablet->hidDeviceAux = NULL; + } + } + } + + return false; + })); + + + // + // Command: AuxReportId + // + AddCommand(new Command("AuxReportId", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.auxReportId = cmd->GetInt(0, tablet->settings.auxReportId); + LOG_INFO("Tablet aux report id = %d\n", tablet->settings.auxReportId); + return true; + })); + + + // + // Command: AuxReportLength + // + AddCommand(new Command("AuxReportLength", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.auxReportLength = cmd->GetInt(0, tablet->settings.auxReportLength); + LOG_INFO("Tablet aux report length = %d\n", tablet->settings.auxReportLength); + return true; + })); + + + // + // Command: AuxDetectMask + // + AddCommand(new Command("AuxDetectMask", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.auxDetectMask = cmd->GetInt(0, tablet->settings.auxDetectMask); + LOG_INFO("Tablet aux detect mask = 0x%02X\n", tablet->settings.auxDetectMask); + return true; + })); + + + // + // Command: AuxIgnoreMask + // + AddCommand(new Command("AuxIgnoreMask", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.auxIgnoreMask = cmd->GetInt(0, tablet->settings.auxIgnoreMask); + LOG_INFO("Tablet aux ignore mask = 0x%02X\n", tablet->settings.auxIgnoreMask); + return true; + })); + + + // + // Command: AuxButtonCount + // + AddCommand(new Command("AuxButtonCount", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.auxButtonCount = cmd->GetInt(0, tablet->settings.auxButtonCount); + LOG_INFO("Tablet aux button count = %d\n", tablet->settings.auxButtonCount); + return true; + })); + + + // + // Command: ClearAuxCustomData + // + // Clears custom auxiliary data formatter instructions + // + AddCommand(new Command("ClearAuxCustomData", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->auxDataFormatter.instructionCount = 0; + LOG_INFO("Custom aux data format instructions cleared!\n"); + return true; + })); + +} \ No newline at end of file diff --git a/TabletDriverService/CommandHandler.Device.cpp b/TabletDriverService/CommandHandler.Device.cpp index 5b24d7a..9a43b92 100644 --- a/TabletDriverService/CommandHandler.Device.cpp +++ b/TabletDriverService/CommandHandler.Device.cpp @@ -22,7 +22,11 @@ void CommandHandler::CreateDeviceCommands() { if(tablet == NULL) { tablet = new Tablet(guid); if(tablet->isOpen) { - LOG_INFO("WinUSB tablet found!\n"); + LOG_INFO("WinUSB tablet found! Manufacturer = '%s', Product = '%s', Serial = '%s'\n", + tablet->GetDeviceManufacturerName().c_str(), + tablet->GetDeviceProductName().c_str(), + tablet->GetDeviceSerialNumber().c_str() + ); } else { LOG_WARNING("Can't open WinUSB tablet '%s'\n", guid.c_str()); @@ -32,7 +36,7 @@ void CommandHandler::CreateDeviceCommands() { } } - // + // Show USB tablet info else { if(tablet != NULL) { if(tablet->usbDevice != NULL) { @@ -54,7 +58,7 @@ void CommandHandler::CreateDeviceCommands() { // // Command: HIDTablet // - AddHelp("HIDTablet", "Usage: HIDTablet "); + AddHelp("HIDTablet", "Usage: HIDTablet [Exclusive=True]"); AddHelp("HIDTablet", ""); AddCommand(new Command("HIDTablet", [&](CommandLine *cmd) { @@ -64,29 +68,43 @@ void CommandHandler::CreateDeviceCommands() { USHORT productID = cmd->GetInt(1, 0); USHORT usagePage = cmd->GetInt(2, 0); USHORT usage = cmd->GetInt(3, 0); + bool isExclusive = false; + for(int i = 4; i < cmd->valueCount; i += 2) { + string parameter = cmd->GetStringLower(i, ""); + if(parameter == "exclusive") { + isExclusive = cmd->GetBoolean(i + 1, false); + } + } if(tablet == NULL) { - tablet = new Tablet(vendorID, productID, usagePage, usage); + tablet = new Tablet(vendorID, productID, usagePage, usage, isExclusive); if(tablet->isOpen) { - LOG_INFO("Tablet found!\n"); + LOG_INFO("Tablet found! Manufacturer='%s', Product='%s', Serial='%s'\n", + tablet->GetDeviceManufacturerName().c_str(), + tablet->GetDeviceProductName().c_str(), + tablet->GetDeviceSerialNumber().c_str() + ); } else { - LOG_WARNING("Can't open HID tablet 0x%04X 0x%04X 0x%04X 0x%04X\n", vendorID, productID, usagePage, usage); + LOG_WARNING("Can't open HID tablet Vendor=0x%04X Product=0x%04X UsagePage=0x%04X Usage=0x%04X Exclusive=%s\n", + vendorID, productID, usagePage, usage, + isExclusive ? "True" : "False"); delete tablet; tablet = NULL; } } } - // + // Show HID tablet info else { if(tablet != NULL) { if(tablet->hidDevice != NULL) { HIDDevice *hid = tablet->hidDevice; - LOG_INFO("Tablet = HID(0x%04X 0x%04X 0x%04X 0x%04X)\n", + LOG_INFO("HIDTablet = [Vendor=0x%04X, Product=0x%04X, UsagePage=0x%04X, Usage=0x%04X, Exclusive=%s]\n", hid->vendorId, hid->productId, hid->usagePage, - hid->usage + hid->usage, + hid->isExclusive ? "True" : "False" ); } } @@ -117,57 +135,9 @@ void CommandHandler::CreateDeviceCommands() { })); - // - // Command: HIDAux - // - AddCommand(new Command("HIDAuxiliary", [&](CommandLine *cmd) { - - 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->hidDeviceAux == NULL) { - tablet->hidDeviceAux = new HIDDevice(vendorID, productID, usagePage, usage); - if(tablet->hidDeviceAux->isOpen) { - LOG_INFO("HID Device found!\n"); - return true; - } - else { - LOG_ERROR("Can't open HID device 0x%04X 0x%04X 0x%04X 0x%04X\n", vendorID, productID, usagePage, usage); - delete tablet->hidDeviceAux; - tablet->hidDeviceAux = NULL; - } - } - } - - return false; - })); - - - // - // Command: HIDAuxRead - // - AddCommand(new Command("HIDAuxRead", [&](CommandLine *cmd) { - - if(cmd->valueCount > 1 && tablet->hidDeviceAux != NULL) { - int length = cmd->GetInt(0, 1); - int count = cmd->GetInt(1, 1); - UCHAR buffer[1024]; - for(int i = 0; i < count; i++) { - tablet->hidDeviceAux->Read(buffer, length); - LOG_INFOBUFFER(buffer, length, "Aux read(%d): ", i + 1); - } - } - - return true; - })); - - // // Command: HIDList // - AddAlias("HIDAux", "HIDAuxiliary"); AddCommand(new Command("HIDList", [&](CommandLine *cmd) { HANDLE hidHandle = 0; HIDDevice *hid = new HIDDevice(); @@ -250,56 +220,6 @@ void CommandHandler::CreateDeviceCommands() { })); - // - // Command: HIDStringRequest, HIDString - // - // Request a string from HID device - // - AddAlias("HIDString", "HIDStringRequest"); - AddCommand(new Command("HIDStringRequest", [&](CommandLine *cmd) { - UCHAR buffer[256]; - int bytesRead; - string stringReply = ""; - - if(cmd->valueCount <= 0) return false; - if(tablet == NULL) return false; - if(tablet->hidDevice == NULL) return false; - if(tabletHandler->isRunning) { - LOG_ERROR("This command can only be run during the tablet configuration!\n"); - return false; - } - int stringIdMin = cmd->GetInt(0, 0); - int stringIdMax = cmd->GetInt(1, stringIdMin); - - // Limits - if(stringIdMin < 1) stringIdMin = 1; - else if(stringIdMin > 256) stringIdMin = 256; - if(stringIdMax < 1) stringIdMax = 1; - else if(stringIdMax > 256) stringIdMax = 256; - - // Loop through string ids - for(int stringId = stringIdMin; stringId <= stringIdMax; stringId++) { - - // String request USB control transter - bytesRead = tablet->hidDevice->StringRequest(stringId, buffer, 256); - - if(bytesRead > 0) { - // Bytes to string - stringReply = ""; - for(int i = 0; i < bytesRead; i += 2) { - stringReply.push_back(buffer[i]); - } - LOG_INFO("HIDString (id %d, %d bytes): '%s'\n", stringId, bytesRead, stringReply.c_str()); - } - else { - LOG_INFO("HIDString (id %d, 0 bytes)\n", stringId); - } - } - - return true; - })); - - // // Command: USBWrite // @@ -325,60 +245,36 @@ void CommandHandler::CreateDeviceCommands() { // - // Command: USBStringRequest, USBString + // Command: GetDeviceStrings, GetStrings, GetString // - // Request a string from a WinUSB device + // Request strings from HID or WinUSB device // - AddAlias("USBString", "USBStringRequest"); - AddCommand(new Command("USBStringRequest", [&](CommandLine *cmd) { - UCHAR buffer[256]; - int bytesRead; - string stringReply = ""; - + AddAlias("GetStrings", "GetDeviceStrings"); + AddAlias("GetString", "GetDeviceStrings"); + AddCommand(new Command("GetDeviceStrings", [&](CommandLine *cmd) { if(cmd->valueCount <= 0) return false; if(tablet == NULL) return false; - if(tablet->usbDevice == NULL) return false; - int stringIdMin = cmd->GetInt(0, 0); - int stringIdMax = cmd->GetInt(1, stringIdMin); - // Limits - if(stringIdMin < 1) stringIdMin = 1; - else if(stringIdMin > 256) stringIdMin = 256; - if(stringIdMax < 1) stringIdMax = 1; - else if(stringIdMax > 256) stringIdMax = 256; + string stringName = cmd->GetStringLower(0, ""); + string deviceString; - // Loop through string ids - for(int stringId = stringIdMin; stringId <= stringIdMax; stringId++) { - - // String request USB control transter - bytesRead = tablet->usbDevice->StringRequest(stringId, buffer, 256); - - if(bytesRead > 0) { - // Bytes to string - stringReply = ""; - for(int i = 0; i < bytesRead; i += 2) { - stringReply.push_back(buffer[i]); - } - LOG_INFO("USBString (id %d, %d bytes): '%s'\n", stringId, bytesRead, stringReply.c_str()); - } - else { - LOG_INFO("USBString (id %d, 0 bytes)\n", stringId); - } + // Manufacturer + if(stringName.compare(0, 3, "man") == 0) { + LOG_INFO(" Manufacturer string = '%s'\n", tablet->GetDeviceManufacturerName().c_str()); + return true; } - return true; - })); - + // Product name + else if(stringName.compare(0, 3, "pro") == 0) { + LOG_INFO(" Product string = '%s'\n", tablet->GetDeviceProductName().c_str()); + return true; + } - // - // Command: GetDeviceStrings, GetStrings - // - // Request strings from HID or WinUSB device - // - AddAlias("GetStrings", "GetDeviceStrings"); - AddCommand(new Command("GetDeviceStrings", [&](CommandLine *cmd) { - if(cmd->valueCount <= 0) return false; - if(tablet == NULL) return false; + // Serial number + else if(stringName.compare(0, 3, "ser") == 0) { + LOG_INFO(" Serial number string = '%s'\n", tablet->GetDeviceSerialNumber().c_str()); + return true; + } int stringIdMin = cmd->GetInt(0, 0); int stringIdMax = cmd->GetInt(1, stringIdMin); @@ -429,20 +325,60 @@ void CommandHandler::CreateDeviceCommands() { // Check if a device string matches // AddAlias("CheckString", "CheckDeviceString"); + AddHelp("CheckDeviceString", "Usage:"); + AddHelp("CheckDeviceString", " CheckDeviceString \"\""); + AddHelp("CheckDeviceString", " CheckDeviceString Manufacturer \"\""); + AddHelp("CheckDeviceString", " CheckDeviceString Product \"\""); + AddHelp("CheckDeviceString", " CheckDeviceString SerialNumber \"\""); AddCommand(new Command("CheckDeviceString", [&](CommandLine *cmd) { - if(cmd->valueCount <= 1) return false; + if(cmd->valueCount <= 1) { + ExecuteCommand("Help", "CheckDeviceString"); + return false; + } if(tablet == NULL) return false; int stringId = cmd->GetInt(0, 0); + string stringName = cmd->GetStringLower(0, ""); string matchString = cmd->GetString(1, ""); - // Request and read device string - string deviceString = tablet->GetDeviceString(stringId); + string deviceString = ""; + + // Manufacturer + if(stringName.compare(0, 3, "man") == 0) { + stringName = "Manufacturer"; + deviceString = tablet->GetDeviceManufacturerName(); + } + + // Product name + else if(stringName.compare(0, 3, "pro") == 0) { + stringName = "Product"; + deviceString = tablet->GetDeviceProductName(); + } + + // Serial number + else if(stringName.compare(0, 3, "ser") == 0) { + stringName = "SerialNumber"; + deviceString = tablet->GetDeviceSerialNumber(); + } + + // Request device string + else { + stringName = to_string(stringId); + if(stringId == 0) { + LOG_ERROR("Invalid string id!\n"); + return false; + } + try { + deviceString = tablet->GetDeviceString(stringId); + } catch(exception&e) { + LOG_ERROR("%s\n", e.what()); + } + } // Does the string match? if(deviceString.size() >= matchString.size() && deviceString.compare(0, matchString.size(), matchString) == 0) { - LOG_INFO("Device string (id %d) '%s' matches with '%s'\n", - stringId, + LOG_INFO("Device string (%s) '%s' matches with '%s'\n", + stringName.c_str(), deviceString.c_str(), matchString.c_str() ); @@ -451,8 +387,8 @@ void CommandHandler::CreateDeviceCommands() { // Does not match else { - LOG_INFO("Device string (id %d) '%s' does not match with '%s'. Tablet invalid!\n", - stringId, + LOG_INFO("Device string (%s) '%s' does not match with '%s'. Tablet invalid!\n", + stringName.c_str(), deviceString.c_str(), matchString.c_str() ); diff --git a/TabletDriverService/CommandHandler.Filters.cpp b/TabletDriverService/CommandHandler.Filters.cpp index b8f7f3c..6773675 100644 --- a/TabletDriverService/CommandHandler.Filters.cpp +++ b/TabletDriverService/CommandHandler.Filters.cpp @@ -20,6 +20,7 @@ void CommandHandler::CreateFilterCommands() { double latency = cmd->GetDouble(0, tablet->smoothing.GetLatency()); double threshold = cmd->GetDouble(1, tablet->smoothing.threshold * 100); + bool onlyWhenButtonsDown = cmd->GetBoolean(2, tablet->smoothing.onlyWhenButtonsDown); threshold /= 100; @@ -42,10 +43,18 @@ void CommandHandler::CreateFilterCommands() { // Set smoothing filter latency tablet->smoothing.SetLatency(latency); + // Only on button down + tablet->smoothing.onlyWhenButtonsDown = onlyWhenButtonsDown; + // Print output 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); + LOG_INFO("Smoothing = %0.2f ms to reach %0.0f%% (weight = %f), OnlyWhenButtonDown=%s\n", + latency, tablet->smoothing.threshold * 100, + tablet->smoothing.weight, + tablet->smoothing.onlyWhenButtonsDown ? "True" : "False" + + ); } else { tablet->smoothing.isEnabled = false; @@ -112,6 +121,7 @@ void CommandHandler::CreateFilterCommands() { AddAlias("Noise", "NoiseReduction"); AddCommand(new Command("NoiseReduction", [&](CommandLine *cmd) { + if(!ExecuteCommand("TabletValid")) return false; string stringValue = cmd->GetStringLower(0, ""); // Off / False @@ -196,23 +206,28 @@ void CommandHandler::CreateFilterCommands() { } else { - double shape = cmd->GetDouble(0, 1.0); - double compensation = cmd->GetDouble(1, 1.0); - bool ignoreWhenDragging = cmd->GetBoolean(2, false); + double shape = cmd->GetDouble(0, tablet->antiSmoothing.shape); + double compensation = cmd->GetDouble(1, tablet->antiSmoothing.compensation); + bool onlyWhenHover = cmd->GetBoolean(2, tablet->antiSmoothing.onlyWhenHover); - if(shape <= 0) { + if(cmd->valueCount == 0) { + LOG_INFO("Usage: %s \n", cmd->command.c_str()); + } + else if(shape <= 0) { tablet->antiSmoothing.isEnabled = false; LOG_INFO("Anti-smoothing = off\n"); } else { tablet->antiSmoothing.shape = shape; tablet->antiSmoothing.compensation = compensation; - tablet->antiSmoothing.ignoreWhenDragging = ignoreWhenDragging; + tablet->antiSmoothing.onlyWhenHover = onlyWhenHover; + tablet->antiSmoothing.isEnabled = true; - LOG_INFO("Anti-smoothing = Shape:%0.2f Compensation:%0.2f DragIgnore:%s\n", + LOG_INFO("Anti-smoothing = Shape:%0.2f Compensation:%0.2f OnlyOnHover:%s\n", tablet->antiSmoothing.shape, tablet->antiSmoothing.compensation, - tablet->antiSmoothing.ignoreWhenDragging ? "true" : "false" + tablet->antiSmoothing.onlyWhenHover ? "true" : "false" + ); } } @@ -221,10 +236,12 @@ void CommandHandler::CreateFilterCommands() { // - // Command: FilterTimerInterval + // Command: FilterTimerInterval, TimerInterval, Interval // // Sets filter timer interval // + AddAlias("Interval", "FilterTimerInterval"); + AddAlias("TimerInterval", "FilterTimerInterval"); AddCommand(new Command("FilterTimerInterval", [&](CommandLine *cmd) { if(tabletHandler == NULL) return true; int oldInterval = (int)round(tabletHandler->timerInterval); @@ -268,12 +285,12 @@ void CommandHandler::CreateFilterCommands() { filterAntiSmoothing = new TabletFilterAntiSmoothing(); filterAntiSmoothing->shape = tablet->antiSmoothing.shape; filterAntiSmoothing->compensation = tablet->antiSmoothing.compensation; - filterAntiSmoothing->ignoreWhenDragging = tablet->antiSmoothing.ignoreWhenDragging; + filterAntiSmoothing->onlyWhenHover = tablet->antiSmoothing.onlyWhenHover; settingsStringIndex += sprintf_s((settingsString + settingsStringIndex), settingsStringSize - settingsStringIndex, "settings AntiSmoothing shape=%0.3f compensation=%0.3f dragignore=%s\r\n", tablet->antiSmoothing.shape, tablet->antiSmoothing.compensation, - tablet->antiSmoothing.ignoreWhenDragging ? "true" : "false" + tablet->antiSmoothing.onlyWhenHover ? "true" : "false" ); } diff --git a/TabletDriverService/CommandHandler.Other.cpp b/TabletDriverService/CommandHandler.Other.cpp index 8ecbb61..fbfecd7 100644 --- a/TabletDriverService/CommandHandler.Other.cpp +++ b/TabletDriverService/CommandHandler.Other.cpp @@ -407,11 +407,22 @@ void CommandHandler::CreateOtherCommands() { LOG_INFO(" Report Length = %d bytes\n", tablet->settings.reportLength); LOG_INFO(" Detect Mask = 0x%02X\n", tablet->settings.detectMask); LOG_INFO(" Ignore Mask = 0x%02X\n", tablet->settings.ignoreMask); + LOG_INFO(" Aux button count = %d\n", tablet->settings.auxButtonCount); - for(int i = 0; i < 8; i++) { - stringIndex += sprintf_s(stringBuffer + stringIndex, maxLength - stringIndex, "%d ", tablet->buttonMap[i]); + for(int i = 0; i < tablet->settings.buttonCount; i++) { + stringIndex += sprintf_s(stringBuffer + stringIndex, maxLength - stringIndex, "'%s' ", tablet->settings.buttonMap[i].c_str()); } - LOG_INFO(" Button Map = %s\n", stringBuffer); + LOG_INFO(" Pen button map = %s\n", stringBuffer); + + + if(tablet->settings.auxButtonCount > 0) { + stringIndex = 0; + for(int i = 0; i < tablet->settings.auxButtonCount; i++) { + stringIndex += sprintf_s(stringBuffer + stringIndex, maxLength - stringIndex, "'%s' ", tablet->settings.auxButtonMap[i].c_str()); + } + LOG_INFO(" Aux button map = %s\n", stringBuffer); + } + if(tablet->initFeatureLength > 0) { @@ -472,6 +483,7 @@ void CommandHandler::CreateOtherCommands() { LOG_STATUS("MAX_X %d\n", tablet->settings.maxX); LOG_STATUS("MAX_Y %d\n", tablet->settings.maxY); LOG_STATUS("MAX_PRESSURE %d\n", tablet->settings.maxPressure); + LOG_STATUS("AUX_BUTTONS %d\n", tablet->settings.auxButtonCount); return true; })); @@ -526,6 +538,7 @@ void CommandHandler::CreateOtherCommands() { return true; })); + // // Command: GetCommands // @@ -561,6 +574,24 @@ void CommandHandler::CreateOtherCommands() { return true; })); + // + // Command: ListKeys + // + // List all keys + // + AddCommand(new Command("ListKeys", [&](CommandLine *cmd) { + + LOG_INFO("Keys:\n"); + for(string key : tabletHandler->inputEmulator.keys) { + if(tabletHandler->inputEmulator.keyMap.count(key) > 0) { + string name = tabletHandler->inputEmulator.keyMap[key]->name; + LOG_INFO(" % -20s %s\n", key.c_str(), name.c_str()); + } + } + + return true; + })); + // // Command: Help diff --git a/TabletDriverService/CommandHandler.Tablet.cpp b/TabletDriverService/CommandHandler.Tablet.cpp index 63457cf..cf6c475 100644 --- a/TabletDriverService/CommandHandler.Tablet.cpp +++ b/TabletDriverService/CommandHandler.Tablet.cpp @@ -75,7 +75,7 @@ void CommandHandler::CreateTabletCommands() { AddCommand(new Command("DetectMask", [&](CommandLine *cmd) { if(tablet == NULL) return false; tablet->settings.detectMask = cmd->GetInt(0, tablet->settings.detectMask); - LOG_INFO("Tablet detect mask = %02X\n", tablet->settings.detectMask); + LOG_INFO("Tablet detect mask = 0x%02X\n", tablet->settings.detectMask); return true; })); @@ -86,7 +86,7 @@ void CommandHandler::CreateTabletCommands() { AddCommand(new Command("IgnoreMask", [&](CommandLine *cmd) { if(tablet == NULL) return false; tablet->settings.ignoreMask = cmd->GetInt(0, tablet->settings.ignoreMask); - LOG_INFO("Tablet ignore mask = %02X\n", tablet->settings.ignoreMask); + LOG_INFO("Tablet ignore mask = 0x%02X\n", tablet->settings.ignoreMask); return true; })); @@ -97,7 +97,7 @@ void CommandHandler::CreateTabletCommands() { AddCommand(new Command("MaxX", [&](CommandLine *cmd) { if(tablet == NULL) return false; tablet->settings.maxX = cmd->GetInt(0, tablet->settings.maxX); - LOG_INFO("Tablet max X = %d\n", tablet->settings.maxX); + LOG_INFO("Tablet maximum X value = %d\n", tablet->settings.maxX); return true; })); @@ -108,7 +108,7 @@ void CommandHandler::CreateTabletCommands() { AddCommand(new Command("MaxY", [&](CommandLine *cmd) { if(tablet == NULL) return false; tablet->settings.maxY = cmd->GetInt(0, tablet->settings.maxY); - LOG_INFO("Tablet max Y = %d\n", tablet->settings.maxY); + LOG_INFO("Tablet maximum Y value = %d\n", tablet->settings.maxY); return true; })); @@ -119,10 +119,11 @@ void CommandHandler::CreateTabletCommands() { AddCommand(new Command("MaxPressure", [&](CommandLine *cmd) { if(tablet == NULL) return false; tablet->settings.maxPressure = cmd->GetInt(0, tablet->settings.maxPressure); - LOG_INFO("Tablet max pressure = %d\n", tablet->settings.maxPressure); + LOG_INFO("Tablet maximum pressure value = %d\n", tablet->settings.maxPressure); return true; })); + // // Command: ClickPressure // @@ -167,6 +168,52 @@ void CommandHandler::CreateTabletCommands() { })); + // + // Command: PressureSensitivity + // + AddCommand(new Command("PressureSensitivity", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.pressureSensitivity = cmd->GetDouble(0, tablet->settings.pressureSensitivity); + LOG_INFO("Tablet pressure sensitivity = %0.5f\n", tablet->settings.pressureSensitivity); + return true; + })); + + + // + // Command: PressureDeadzone + // + AddCommand(new Command("PressureDeadzone", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.pressureDeadzone = cmd->GetDouble(0, tablet->settings.pressureDeadzone); + LOG_INFO("Tablet pressure deadzone = %0.2f\n", tablet->settings.pressureDeadzone); + return true; + })); + + + // + // Command: ScrollSensitivity + // + AddCommand(new Command("ScrollSensitivity", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.scrollSensitivity = cmd->GetDouble(0, tablet->settings.scrollSensitivity); + LOG_INFO("Tablet scroll sensitivity = %0.2f scrolls per millimeter\n", tablet->settings.scrollSensitivity); + return true; + })); + + + // + // Command: ScrollAcceleration, ScrollAcc + // + AddAlias("ScrollAcc", "ScrollAcceleration"); + AddCommand(new Command("ScrollAcceleration", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->settings.scrollAcceleration = cmd->GetDouble(0, tablet->settings.scrollAcceleration); + if(tablet->settings.scrollAcceleration < 0.1) tablet->settings.scrollAcceleration = 0.1; + LOG_INFO("Tablet scroll acceleration = %0.2f\n", tablet->settings.scrollAcceleration); + return true; + })); + + // // Command: Skew // @@ -209,6 +256,11 @@ void CommandHandler::CreateTabletCommands() { tablet->settings.dataFormat = TabletSettings::TabletFormatSkipFirstDataByte; } + // Custom data format + else if(format == "custom") { + tablet->settings.dataFormat = TabletSettings::TabletFormatCustom; + } + // Unknown type else { LOG_ERROR("Unknown tablet data format: %s\n", format.c_str()); @@ -220,6 +272,115 @@ void CommandHandler::CreateTabletCommands() { })); + // + // Command: CustomDataInstruction, CustomData + // + // Sets tablet data format + // + // CustomData [TargetMask=] [Source=] [SourceMask=] [SourceShift=] + // + AddAlias("CustomData", "CustomDataInstruction"); + AddAlias("AuxCustomData", "CustomDataInstruction"); + AddCommand(new Command("CustomDataInstruction", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + + string commandName = cmd->GetCommandLowerCase(); + + if(cmd->valueCount >= 3) { + + DataFormatter::DataInstruction instruction; + + int targetByte = cmd->GetInt(0, 0); + string targetByteName = cmd->GetStringLower(0, ""); + /* + int targetBitMask = cmd->GetInt(1, 0xFF); + int sourceByte = cmd->GetInt(2, 0); + int sourceBitMask = cmd->GetInt(3, 0xFF); + int sourceBitShift = cmd->GetInt(4, 0); + */ + + + // Auxiliary byte names + if(commandName.compare(0, 3, "aux") == 0) { + if(targetByteName == "reportid") targetByte = 0; + if(targetByteName == "buttonslow") targetByte = 1; + if(targetByteName == "buttonshigh") targetByte = 2; + if(targetByteName == "detect") targetByte = 3; + + } + + // Tablet byte names + else { + if(targetByteName == "reportid") targetByte = 0; + if(targetByteName == "buttons") targetByte = 1; + if(targetByteName == "xlow") targetByte = 2; + if(targetByteName == "xhigh") targetByte = 3; + if(targetByteName == "ylow") targetByte = 4; + if(targetByteName == "yhigh") targetByte = 5; + if(targetByteName == "pressurelow") targetByte = 6; + if(targetByteName == "pressurehigh") targetByte = 7; + } + + + instruction.targetByte = targetByte; + + // Loop through parameters + for(int i = 1; i < cmd->valueCount; i += 2) { + string parameter = cmd->GetStringLower(i, ""); + int value = cmd->GetInt(i + 1, -1); + + // Set instruction parameters + if(parameter == "targetmask" || parameter == "mask") instruction.targetBitMask = value; + if(parameter == "source") instruction.sourceByte = value; + if(parameter == "sourcemask") instruction.sourceBitMask = value; + if(parameter == "sourceshift" || parameter == "shift") instruction.sourceBitShift = value; + + } + + + // Add aux data format instruction + if(commandName.compare(0, 3, "aux") == 0) { + tablet->auxDataFormatter.AddInstruction(&instruction); + } + // Add tablet data format instruction + else { + tablet->dataFormatter.AddInstruction(&instruction); + } + + + // + LOG_INFO("Custom data format: Target=%d, TargetMask=0x%02X, Source=%d, SourceMask=0x%02X, SourceShift=%d\n", + instruction.targetByte, + instruction.targetBitMask, + instruction.sourceByte, + instruction.sourceBitMask, + instruction.sourceBitShift + ); + + } + else { + LOG_INFO("Usage: %s [TargetMask=] [Source=] [SourceMask=] [SourceShift=]\n", + cmd->command.c_str() + ); + } + + return true; + })); + + + // + // Command: ClearCustomData + // + // Clears custom data formatter instructions + // + AddCommand(new Command("ClearCustomData", [&](CommandLine *cmd) { + if(tablet == NULL) return false; + tablet->dataFormatter.instructionCount = 0; + LOG_INFO("Custom data format instructions cleared!\n"); + return true; + })); + + // // Command: InitFeature // @@ -299,21 +460,79 @@ void CommandHandler::CreateTabletCommands() { // - // Command: ButtonMap, Buttons + // Command: ClearButtonMap + // + AddCommand(new Command("ClearButtonMap", [&](CommandLine *cmd) { + if(!ExecuteCommand("TabletValid")) return false; + for(int i = 0; i < 16; i++) { + tablet->settings.buttonMap[i] = ""; + } + LOG_INFO("Pen button map cleared!\n"); + return true; + })); + + + // + // Command: ClearAuxButtonMap + // + AddCommand(new Command("ClearAuxButtonMap", [&](CommandLine *cmd) { + if(!ExecuteCommand("TabletValid")) return false; + for(int i = 0; i < 16; i++) { + tablet->settings.auxButtonMap[i] = ""; + } + LOG_INFO("Aux button map cleared!\n"); + return true; + })); + + + // + // Command: ButtonMap, Buttons, AuxButtonMap, AuxButtons // // Maps input buttons to output buttons // AddAlias("Buttons", "ButtonMap"); + AddAlias("AuxButtonMap", "ButtonMap"); + AddAlias("AuxButtons", "ButtonMap"); AddCommand(new Command("ButtonMap", [&](CommandLine *cmd) { - if(!ExecuteCommand("TabletValid")) return false; - char buttonMapBuffer[32]; - int index = 0; - 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]); + + string commandName = cmd->GetCommandLowerCase(); + int button = cmd->GetInt(0, 0); + string keys = ""; + if(cmd->valueCount == 2) { + keys = cmd->GetString(1, ""); + transform(keys.begin(), keys.end(), keys.begin(), ::toupper); + } + else { + for(int i = 1; i < cmd->valueCount; i++) { + string key = cmd->GetString(i, ""); + key.erase(remove(key.begin(), key.end(), '+'), key.end()); + if(key.size() > 0) { + std::transform(key.begin(), key.end(), key.begin(), ::toupper); + if(i != 1) keys += "+"; + keys += key; + } + } } - LOG_INFO("Button Map = %s\n", buttonMapBuffer); + + if(button >= 1) { + if(commandName.compare(0, 3, "aux") == 0) { + if(button <= tablet->settings.auxButtonCount) { + tablet->settings.auxButtonMap[button - 1] = keys; + LOG_INFO("Aux button %d mapped to '%s'\n", button, keys.c_str()); + } + } + else { + if(button <= tablet->settings.buttonCount) { + tablet->settings.buttonMap[button - 1] = keys; + LOG_INFO("Pen button %d mapped to '%s'\n", button, keys.c_str()); + } + } + } + else { + LOG_INFO("Usage: %s - + - - - + + - - + - - - + + - - + + + + Disable buttons + + + + - - - - Disable - - buttons - - + + + - + s/mm - - + - + + + + Stop cursor + + + - + @@ -549,7 +563,7 @@ > - @@ -614,7 +628,7 @@ - @@ -241,35 +258,30 @@ - - + Cursor="Hand" + Height="120" Margin="5,0,5,0" + Background="Transparent" + ClipToBounds="True" + MouseMove="Canvas_MouseMove" + MouseDown="Canvas_MouseDown" + MouseUp="Canvas_MouseUp" /> + + + - - Full Area - - + Mode - Absolute: Use this with osu! Relative: Mouse mode. Sensitivity is determined by your tablet area size. Windows Ink: Pen pressure is enabled in this mode. Check if your drawing application supports Windows Ink. + + SendInput: This is for debugging and testing purposes. Use this if you have problems with the absolute mode. @@ -280,109 +292,114 @@ Checked="CheckboxChanged" /> - + + + - 80 - + 80 + - 45 - + 45 + - 0 - + 0 + - 0 - + 0 + - - - - Force - Aspect Ratio + + - - + + + Full area + + + + + Force aspect ratio + + + - - - + - - - - - - - + - - - - + - - - - - + + + + + + + + + + + + + + + + Pen Buttons - - + + - You can disable a single button by clicking "Clear" in the mapping dialog. - The "Disable buttons" checkbox will disable all pen buttons. @@ -390,57 +407,48 @@ - Use negative number in scroll sensitivity to invert the direction. - Scroll acceleration 1.0 = No acceleration. - - - + + + - + - + - + ToolTip="" + ToolTipOpening="ButtonMap_ToolTipOpening" + Click="ButtonMap_Click"> + MOUSE1 + - - + + ToolTip="" + ToolTipOpening="ButtonMap_ToolTipOpening" + Click="ButtonMap_Click"> + MOUSE1 + - - + + ToolTip="" + ToolTipOpening="ButtonMap_ToolTipOpening" + Click="ButtonMap_Click"> + MOUSE1 + @@ -452,75 +460,71 @@ Disable buttons - - + + - - - - - + + + + + Mouse Scroll - - + + - Use negative number in sensitivity to invert the direction. - Acceleration 1.0 = No acceleration. Recommended range is from 1 to 5. - - - - - - - - - 0.5 - - - - - - - - 1.0 - - + + + + - - + + + 0.5 + + + + + + + + 1.0 + + + + + - Stop cursor - + Stop cursor + - - + + - - - - - - - - - - - - + + + - + + + + + + + + + + - - + - - - - + + + + - - - - - - - - + + + + + + + + - + - - + - - - - + + + + - - - - + + + - - + - - + + - - + - - + + - - - - - - - - + + + + + + + + - - + - + - + diff --git a/TabletDriverGUI/MainWindow.xaml.cs b/TabletDriverGUI/MainWindow.xaml.cs index b669fc0..4dcc0b8 100644 --- a/TabletDriverGUI/MainWindow.xaml.cs +++ b/TabletDriverGUI/MainWindow.xaml.cs @@ -25,7 +25,7 @@ public partial class MainWindow : Window { // Version - public string Version = "0.2.2"; + public string Version = "0.2.3"; // Console stuff private List commandHistory; @@ -134,8 +134,7 @@ public MainWindow() notifyIcon.ContextMenu.MenuItems[0].Enabled = false; notifyIcon.Text = ""; - notifyIcon.DoubleClick += NotifyIcon_DoubleClick; - notifyIcon.Click += NotifyIcon_DoubleClick; + notifyIcon.MouseClick += NotifyIcon_Click; notifyIcon.Visible = true; IsRealExit = false; @@ -202,7 +201,7 @@ public MainWindow() { Height = 22, Content = "", - Padding = new Thickness(2,0,2,0), + Padding = new Thickness(2, 0, 2, 0), ToolTip = "Empty", Background = Brushes.White }; @@ -238,11 +237,13 @@ public MainWindow() inkCanvasUndoHistory = new StrokeCollection(); // Ink canvas drawing attributes - inkCanvasDrawingAttributes = new DrawingAttributes(); - inkCanvasDrawingAttributes.Width = 10; - inkCanvasDrawingAttributes.Height = 10; - inkCanvasDrawingAttributes.Color = Color.FromRgb(0x55, 0x55, 0x55); - inkCanvasDrawingAttributes.StylusTip = StylusTip.Ellipse; + inkCanvasDrawingAttributes = new DrawingAttributes + { + Width = 10, + Height = 10, + Color = Color.FromRgb(0x55, 0x55, 0x55), + StylusTip = StylusTip.Ellipse + }; inkCanvas.DefaultDrawingAttributes = inkCanvasDrawingAttributes; // Process command line arguments @@ -294,40 +295,10 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) isFirstStart = true; config = new Configuration(); } - isLoadingSettings = true; - Width = config.WindowWidth; - Height = config.WindowHeight; - isLoadingSettings = false; - - if (!config.DeveloperMode) - { - } - - - // Invalid config -> Set defaults - if (config.ScreenArea.Width == 0 || config.ScreenArea.Height == 0) - { - config.DesktopSize.Width = GetVirtualDesktopSize().Width; - config.DesktopSize.Height = GetVirtualDesktopSize().Height; - config.ScreenArea.Width = config.DesktopSize.Width; - config.ScreenArea.Height = config.DesktopSize.Height; - config.ScreenArea.X = 0; - config.ScreenArea.Y = 0; - } + // Initialize configuration + InitializeConfiguration(); - // Create canvas elements - CreateCanvasElements(); - - // Load settings from configuration - LoadSettingsFromConfiguration(); - - // Update the settings back to the configuration - UpdateSettingsToConfiguration(); - - - // Set run at startup - SetRunAtStartup(config.RunAtStartup); // Hide the window if the GUI is started as minimized if (WindowState == WindowState.Minimized) @@ -386,13 +357,22 @@ void ProcessCommandLineArguments() #region Notify icon stuff - // Notify icon double click -> show window - private void NotifyIcon_DoubleClick(object sender, EventArgs e) + // + // Notify icon click -> show window + // + private void NotifyIcon_Click(object sender, System.Windows.Forms.MouseEventArgs e) { + // Is not mouse left button? + if (e.Button != System.Windows.Forms.MouseButtons.Left) + return; + + // Minimized -> Show window if (WindowState == WindowState.Minimized) { NotifyShowWindow(sender, e); } + + // Hide window else { NotifyHideWindow(sender, e); @@ -602,6 +582,8 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b + + #endregion } diff --git a/TabletDriverService/CommandHandler.Other.cpp b/TabletDriverService/CommandHandler.Other.cpp index fbfecd7..97e64f7 100644 --- a/TabletDriverService/CommandHandler.Other.cpp +++ b/TabletDriverService/CommandHandler.Other.cpp @@ -395,6 +395,19 @@ void CommandHandler::CreateOtherCommands() { int stringIndex = 0; LOG_INFO("\n"); + + // VMulti type + switch(vmulti->type) { + case VMulti::TypeXPPen: LOG_INFO("VMulti: XP-Pen\n"); break; + case VMulti::TypeVEIKK: LOG_INFO("VMulti: VEIKK\n"); break; + default: break; + } + + LOG_INFO("\n"); + + // + // Tablet + // LOG_INFO("Tablet: %s\n", tablet->name.c_str()); LOG_INFO(" Width = %0.2f mm\n", tablet->settings.width); LOG_INFO(" Height = %0.2f mm\n", tablet->settings.height); @@ -407,7 +420,6 @@ void CommandHandler::CreateOtherCommands() { LOG_INFO(" Report Length = %d bytes\n", tablet->settings.reportLength); LOG_INFO(" Detect Mask = 0x%02X\n", tablet->settings.detectMask); LOG_INFO(" Ignore Mask = 0x%02X\n", tablet->settings.ignoreMask); - LOG_INFO(" Aux button count = %d\n", tablet->settings.auxButtonCount); for(int i = 0; i < tablet->settings.buttonCount; i++) { stringIndex += sprintf_s(stringBuffer + stringIndex, maxLength - stringIndex, "'%s' ", tablet->settings.buttonMap[i].c_str()); @@ -423,15 +435,27 @@ void CommandHandler::CreateOtherCommands() { LOG_INFO(" Aux button map = %s\n", stringBuffer); } - + LOG_INFO(" Aux button count = %d\n", tablet->settings.auxButtonCount); if(tablet->initFeatureLength > 0) { - LOG_INFOBUFFER(tablet->initFeature, tablet->initFeatureLength, " Tablet init feature report: "); + LOG_INFOBUFFER(tablet->initFeature, tablet->initFeatureLength, " Init feature report: "); } if(tablet->initReportLength > 0) { - LOG_INFOBUFFER(tablet->initReport, tablet->initReportLength, " Tablet init report: "); + LOG_INFOBUFFER(tablet->initReport, tablet->initReportLength, " Init report: "); + } + if(tablet->initStrings.size() > 0) { + string stringIds = ""; + for(int stringId : tablet->initStrings) { + stringIds += to_string(stringId) + " "; + } + LOG_INFO(" Init string ids: %s\n", stringIds.c_str()); } + LOG_INFO("\n"); + + // + // Areas + // LOG_INFO("Area:\n"); LOG_INFO(" Desktop = %0.0fpx x %0.0fpx\n", mapper->areaVirtualScreen.width, mapper->areaVirtualScreen.height @@ -561,7 +585,7 @@ void CommandHandler::CreateOtherCommands() { commandsString += aliasNames[aliasPair.first] + " "; } } - + if(commandsString.size() > 80) { LOG_STATUS("COMMANDS %s\n", commandsString.c_str()); commandsString = ""; diff --git a/TabletDriverService/CommandHandler.Tablet.cpp b/TabletDriverService/CommandHandler.Tablet.cpp index fa45cba..385c0e8 100644 --- a/TabletDriverService/CommandHandler.Tablet.cpp +++ b/TabletDriverService/CommandHandler.Tablet.cpp @@ -620,8 +620,17 @@ void CommandHandler::CreateTabletCommands() { AddAlias("Sensitivity", "RelativeSensitivity"); AddCommand(new Command("RelativeSensitivity", [&](CommandLine *cmd) { if(!ExecuteCommand("TabletValid")) return false; - outputManager->settings->relativeSensitivity = cmd->GetDouble(0, outputManager->settings->relativeSensitivity); - LOG_INFO("Relative mode sensitivity = %0.2f px/mm\n", outputManager->settings->relativeSensitivity); + outputManager->settings->relativeSensitivity.x = cmd->GetDouble(0, outputManager->settings->relativeSensitivity.x); + outputManager->settings->relativeSensitivity.y = cmd->GetDouble(1, outputManager->settings->relativeSensitivity.y); + + if(cmd->valueCount == 1) { + outputManager->settings->relativeSensitivity.y = outputManager->settings->relativeSensitivity.x; + } + + LOG_INFO("Relative mode sensitivity = X=%0.2f px/mm, Y=%0.2f px/mm\n", + outputManager->settings->relativeSensitivity.x, + outputManager->settings->relativeSensitivity.y + ); return true; })); diff --git a/TabletDriverService/Main.cpp b/TabletDriverService/Main.cpp index d08bef9..c582f8c 100644 --- a/TabletDriverService/Main.cpp +++ b/TabletDriverService/Main.cpp @@ -56,9 +56,6 @@ int main(int argc, char**argv) { mapper = new ScreenMapper(tablet); mapper->SetRotation(0); - // Output manager - outputManager = new OutputManager(); - // // Command: Start // @@ -113,8 +110,16 @@ int main(int argc, char**argv) { // Start logger LOGGER_START(); - // VMulti Device - vmulti = new VMulti(); + // VMulti XP-Pen + vmulti = new VMulti(VMulti::TypeXPPen); + + // VMulti VEIKK + if(!vmulti->isOpen) { + LOG_ERROR("Can't open XP-Pen's VMulti! Trying VEIKK's VMulti!\n"); + delete vmulti; + vmulti = new VMulti(VMulti::TypeVEIKK); + } + if(!vmulti->isOpen) { LOG_ERROR("Can't open VMulti device!\n\n"); LOG_ERROR("Possible fixes:\n"); @@ -124,6 +129,10 @@ int main(int argc, char**argv) { CleanupAndExit(1); } + // Output manager + outputManager = new OutputManager(); + + // Read init file filename = "init.cfg"; @@ -203,7 +212,7 @@ void CleanupAndExit(int code) { // Tablet if(tablet != NULL) delete tablet; - + // Logger LOGGER_STOP(); Sleep(500); diff --git a/TabletDriverService/OutputManager.cpp b/TabletDriverService/OutputManager.cpp index 57da3fc..3ead118 100644 --- a/TabletDriverService/OutputManager.cpp +++ b/TabletDriverService/OutputManager.cpp @@ -11,7 +11,6 @@ OutputManager::OutputManager() { // Set outputs array outputs[ModeVMultiAbsolute] = &vmultiAbsolute; - outputs[ModeVMultiAbsoluteV2] = &vmultiAbsoluteV2; outputs[ModeVMultiRelative] = &vmultiRelative; outputs[ModeVMultiDigitizer] = &vmultiDigitizer; outputs[ModeSendInputAbsolute] = &sendInputAbsolute; diff --git a/TabletDriverService/OutputManager.h b/TabletDriverService/OutputManager.h index 34a5b9c..3aa5fc6 100644 --- a/TabletDriverService/OutputManager.h +++ b/TabletDriverService/OutputManager.h @@ -2,7 +2,6 @@ #include "Output.h" #include "OutputVMultiAbsolute.h" -#include "OutputVMultiAbsoluteV2.h" #include "OutputVMultiRelative.h" #include "OutputVMultiDigitizer.h" #include "OutputSendInputAbsolute.h" @@ -13,7 +12,6 @@ class OutputManager : public Output { enum OutputMode { ModeVMultiAbsolute, - ModeVMultiAbsoluteV2, ModeVMultiRelative, ModeVMultiDigitizer, ModeSendInputAbsolute, @@ -22,10 +20,9 @@ class OutputManager : public Output { }; Output *output; - Output *outputs[7]; + Output *outputs[6]; OutputVMultiAbsolute vmultiAbsolute; - OutputVMultiAbsoluteV2 vmultiAbsoluteV2; OutputVMultiRelative vmultiRelative; OutputVMultiDigitizer vmultiDigitizer; OutputSendInputAbsolute sendInputAbsolute; diff --git a/TabletDriverService/OutputSendInputAbsolute.cpp b/TabletDriverService/OutputSendInputAbsolute.cpp index 1014710..28846ed 100644 --- a/TabletDriverService/OutputSendInputAbsolute.cpp +++ b/TabletDriverService/OutputSendInputAbsolute.cpp @@ -52,16 +52,6 @@ bool OutputSendInputAbsolute::Set(TabletState *tabletState) { // Map position to virtual screen (values between 0 and 1) mapper->GetScreenPosition(&x, &y); - - - if(logger.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.type = INPUT_MOUSE; input.mi.mouseData = 0; input.mi.dx = (LONG)floor( @@ -102,6 +92,18 @@ bool OutputSendInputAbsolute::Write() { // SendInput SendInput(1, &input, sizeof(INPUT)); + // Debug message + if(logger.debugEnabled) { + LOG_DEBUG("%0.0f,%0.0f | %0.0f,%0.0f | %0.0f,%0.0f | %ld, %ld -> %0.0f,%0.0f\n", + monitorInfo.virtualWidth, monitorInfo.virtualHeight, + monitorInfo.virtualX, monitorInfo.virtualY, + monitorInfo.primaryWidth, monitorInfo.primaryHeight, + input.mi.dx, input.mi.dy, + input.mi.dx / 65535.0 * (monitorInfo.primaryWidth) - monitorInfo.virtualX, + input.mi.dy / 65535.0 * (monitorInfo.primaryHeight) - monitorInfo.virtualY + ); + } + // Copy last input memcpy(&lastInput, &input, sizeof(INPUT)); diff --git a/TabletDriverService/OutputSettings.cpp b/TabletDriverService/OutputSettings.cpp index 90ec696..e9227d4 100644 --- a/TabletDriverService/OutputSettings.cpp +++ b/TabletDriverService/OutputSettings.cpp @@ -2,7 +2,7 @@ #include "OutputSettings.h" OutputSettings::OutputSettings() { - relativeSensitivity = 1; + relativeSensitivity.Set(1,1); relativeResetDistance = 25; relativeResetTime = 100; ResetRelativeState(0, 0, chrono::high_resolution_clock::now()); diff --git a/TabletDriverService/OutputSettings.h b/TabletDriverService/OutputSettings.h index a4aae60..355eec5 100644 --- a/TabletDriverService/OutputSettings.h +++ b/TabletDriverService/OutputSettings.h @@ -18,7 +18,7 @@ class OutputSettings { chrono::high_resolution_clock::time_point lastTime; } relativeState; - double relativeSensitivity; + Vector2D relativeSensitivity; double relativeResetDistance; double relativeResetTime; diff --git a/TabletDriverService/OutputVMultiAbsolute.cpp b/TabletDriverService/OutputVMultiAbsolute.cpp index ad4c714..eccc1db 100644 --- a/TabletDriverService/OutputVMultiAbsolute.cpp +++ b/TabletDriverService/OutputVMultiAbsolute.cpp @@ -10,13 +10,25 @@ // OutputVMultiAbsolute::OutputVMultiAbsolute() { - // Absolute mouse vmulti report - report.vmultiId = 0x40; + // Absolute mouse VMulti report report.reportLength = 7; - report.reportId = 3; report.buttons = 0; + report.x = 0; + report.y = 0; report.wheel = 0; + // XP-Pen + if(vmulti->type == VMulti::TypeXPPen) { + report.vmultiId = 0x40; + report.reportId = 3; + } + + // VEIKK + else if(vmulti->type == VMulti::TypeVEIKK) { + report.vmultiId = 0x09; + report.reportId = 1; + } + } diff --git a/TabletDriverService/OutputVMultiAbsoluteV2.cpp b/TabletDriverService/OutputVMultiAbsoluteV2.cpp deleted file mode 100644 index 084649d..0000000 --- a/TabletDriverService/OutputVMultiAbsoluteV2.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "stdafx.h" -#include "OutputVMultiAbsoluteV2.h" - -#define LOG_MODULE "VMultiAbsoluteV2" -#include "Logger.h" - - -// -// Constructor -// -OutputVMultiAbsoluteV2::OutputVMultiAbsoluteV2() { - - // Absolute mouse vmulti report - report.vmultiId = 0x40; - report.reportLength = 8; - report.reportId = 9; - report.buttons = 0; - report.pressure = 0; - -} - - -// -// Destructor -// -OutputVMultiAbsoluteV2::~OutputVMultiAbsoluteV2() { -} - - -// -// Set output -// -bool OutputVMultiAbsoluteV2::Set(TabletState *tabletState) { - - double x = tabletState->position.x; - double y = tabletState->position.y; - - // Map position to virtual screen (values between 0 and 1) - mapper->GetScreenPosition(&x, &y); - - report.buttons = tabletState->buttons; - report.x = (USHORT)round(x * 32767.0); - report.y = (USHORT)round(y * 32767.0); - //report.pressure = (USHORT)round(tabletState->pressure * 8191.0); - vmulti->SetReport(&report, sizeof(report)); - - if(logger.debugEnabled) { - LOG_DEBUGBUFFER(&report, 10, "Report: "); - } - - return true; -} - -// -// Write output -// -bool OutputVMultiAbsoluteV2::Write() { - - // Write report to VMulti device if report has changed - if(vmulti->HasReportChanged()) { - vmulti->WriteReport(); - return true; - } - return false; -} - - -// -// Reset output -// -bool OutputVMultiAbsoluteV2::Reset() { - - - // Do not reset when buttons are not pressed - if(report.buttons == 0) { - return true; - } - - report.buttons = 0; - report.pressure = 0; - vmulti->SetReport(&report, sizeof(report)); - vmulti->WriteReport(); - return true; -} - diff --git a/TabletDriverService/OutputVMultiAbsoluteV2.h b/TabletDriverService/OutputVMultiAbsoluteV2.h deleted file mode 100644 index 5b9bc12..0000000 --- a/TabletDriverService/OutputVMultiAbsoluteV2.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "Output.h" - -class OutputVMultiAbsoluteV2 : public Output { -public: - - // Absolute mouse vmulti report - struct { - BYTE vmultiId; - BYTE reportLength; - BYTE reportId; - unsigned char buttons; - USHORT x; - USHORT y; - USHORT pressure; - } report; - - bool Set(TabletState *tabletState); - bool Write(); - bool Reset(); - - OutputVMultiAbsoluteV2(); - ~OutputVMultiAbsoluteV2(); -}; - diff --git a/TabletDriverService/OutputVMultiDigitizer.cpp b/TabletDriverService/OutputVMultiDigitizer.cpp index 35324e5..a881783 100644 --- a/TabletDriverService/OutputVMultiDigitizer.cpp +++ b/TabletDriverService/OutputVMultiDigitizer.cpp @@ -10,13 +10,27 @@ // OutputVMultiDigitizer::OutputVMultiDigitizer() { - // VMulti digitizer report - report.vmultiId = 0x40; + // Digitizer VMulti report report.reportLength = 8; - report.reportId = 5; report.buttons = 0; + report.x = 0; + report.y = 0; report.pressure = 0; + // XP-Pen + if(vmulti->type == VMulti::TypeXPPen) { + report.vmultiId = 0x40; + report.reportId = 5; + maxPressure = 2047.0; + } + + // VEIKK + else if(vmulti->type == VMulti::TypeVEIKK) { + report.vmultiId = 0x09; + report.reportId = 2; + maxPressure = 8191.0; + } + } @@ -45,7 +59,7 @@ bool OutputVMultiDigitizer::Set(TabletState *tabletState) { report.buttons = tabletState->buttons | 0x20; report.x = (USHORT)round(x * 32767.0 + offsetX); report.y = (USHORT)round(y * 32767.0 + offsetY); - report.pressure = (USHORT)round(tabletState->pressure * 2047.0); + report.pressure = (USHORT)round(tabletState->pressure * maxPressure); vmulti->SetReport(&report, sizeof(report)); return true; diff --git a/TabletDriverService/OutputVMultiDigitizer.h b/TabletDriverService/OutputVMultiDigitizer.h index 1ff9b73..0d1cdbc 100644 --- a/TabletDriverService/OutputVMultiDigitizer.h +++ b/TabletDriverService/OutputVMultiDigitizer.h @@ -13,6 +13,8 @@ class OutputVMultiDigitizer : public Output { USHORT pressure; } report; + double maxPressure; + bool Set(TabletState *tabletState); bool Write(); bool Reset(); diff --git a/TabletDriverService/OutputVMultiRelative.cpp b/TabletDriverService/OutputVMultiRelative.cpp index f566d47..2ce7da5 100644 --- a/TabletDriverService/OutputVMultiRelative.cpp +++ b/TabletDriverService/OutputVMultiRelative.cpp @@ -4,20 +4,31 @@ #define LOG_MODULE "VMultiRelative" #include "Logger.h" + // // Constructor // OutputVMultiRelative::OutputVMultiRelative() { // Relative mouse vmulti report - report.vmultiId = 0x40; report.reportLength = 5; - report.reportId = 4; report.buttons = 0; report.x = 0; report.y = 0; report.wheel = 0; + // XP-Pen + if(vmulti->type == VMulti::TypeXPPen) { + report.vmultiId = 0x40; + report.reportId = 4; + } + + // VEIKK + else if(vmulti->type == VMulti::TypeVEIKK) { + report.vmultiId = 0x09; + report.reportId = 3; + } + firstReport = true; } @@ -80,10 +91,9 @@ bool OutputVMultiRelative::Set(TabletState *tabletState) { dy = 0; } - // Sensitivity - dx *= settings->relativeSensitivity; - dy *= settings->relativeSensitivity; + dx *= settings->relativeSensitivity.x; + dy *= settings->relativeSensitivity.y; // Move target position settings->relativeState.targetPosition.Add(dx, dy); diff --git a/TabletDriverService/TabletDriverService.vcxproj b/TabletDriverService/TabletDriverService.vcxproj index 2f0b2a4..ea63d12 100644 --- a/TabletDriverService/TabletDriverService.vcxproj +++ b/TabletDriverService/TabletDriverService.vcxproj @@ -187,7 +187,6 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG - @@ -230,7 +229,6 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG - @@ -270,6 +268,7 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG + diff --git a/TabletDriverService/TabletDriverService.vcxproj.filters b/TabletDriverService/TabletDriverService.vcxproj.filters index 57c2def..4821fc6 100644 --- a/TabletDriverService/TabletDriverService.vcxproj.filters +++ b/TabletDriverService/TabletDriverService.vcxproj.filters @@ -90,9 +90,6 @@ Header Files - - Header Files - Header Files @@ -200,9 +197,6 @@ Source Files - - Source Files - Source Files @@ -279,5 +273,8 @@ Resource Files + + Resource Files + \ No newline at end of file diff --git a/TabletDriverService/TabletHandler.cpp b/TabletDriverService/TabletHandler.cpp index 964d2f9..2c8d69d 100644 --- a/TabletDriverService/TabletHandler.cpp +++ b/TabletDriverService/TabletHandler.cpp @@ -23,20 +23,16 @@ TabletHandler::~TabletHandler() { StopTimer(); isRunning = false; - if(tablet->hidDevice != NULL) - tablet->hidDevice->CloseDevice(); - - if(tablet->hidDeviceAux != NULL) - tablet->hidDeviceAux->CloseDevice(); - - if(tablet->usbDevice != NULL) - tablet->usbDevice->CloseDevice(); + if(tablet != NULL) { + if(tablet->hidDevice != NULL) + tablet->hidDevice->CloseDevice(); - if(tabletInputThread != NULL) - tabletInputThread->join(); + if(tablet->hidDeviceAux != NULL) + tablet->hidDeviceAux->CloseDevice(); - if(auxInputThread != NULL) - auxInputThread->join(); + if(tablet->usbDevice != NULL) + tablet->usbDevice->CloseDevice(); + } } @@ -257,6 +253,17 @@ void TabletHandler::RunTabletInputThread() { if(isPressed) { lastScrollPosition.Set(scrollPosition); scrollStartPosition.Set(tablet->state.position); + + // + // Move normal mouse to digitizer position + // + if(outputManager->mode == OutputManager::ModeVMultiDigitizer) { + TabletState tmpState; + tmpState.position.Set(tablet->state.position); + outputManager->sendInputAbsolute.Set(&tmpState); + outputManager->sendInputAbsolute.Write(); + } + } // Delta from the last scroll position diff --git a/TabletDriverService/VMulti.cpp b/TabletDriverService/VMulti.cpp index ae81aaa..0205df8 100644 --- a/TabletDriverService/VMulti.cpp +++ b/TabletDriverService/VMulti.cpp @@ -5,16 +5,29 @@ #include "Logger.h" // Constructor -VMulti::VMulti() { +VMulti::VMulti(int type) { + isOpen = false; debugEnabled = false; + this->type = type; lastButtons = 0; + // Report buffers memset(reportBuffer, 0, 65); memset(lastReportBuffer, 0, 65); - hidDevice = new HIDDevice(0x00FF, 0xBACC, 0xFF00, 0x0001); + // XP-Pen VMulti + if(type == VMultiType::TypeXPPen) { + hidDevice = new HIDDevice(0x00FF, 0xBACC, 0xFF00, 0x0001); + } + + // VEIKK VMulti + else if(type == VMultiType::TypeVEIKK) { + hidDevice = new HIDDevice(0x2FEB, 0xFFFF, 0xFF00, 0x0001); + } + + if(hidDevice->isOpen) { isOpen = true; outputEnabled = true; diff --git a/TabletDriverService/VMulti.h b/TabletDriverService/VMulti.h index f89114e..e0b2edf 100644 --- a/TabletDriverService/VMulti.h +++ b/TabletDriverService/VMulti.h @@ -10,12 +10,17 @@ class VMulti { BYTE lastReportBuffer[65]; public: + enum VMultiType { + TypeXPPen, + TypeVEIKK + }; bool isOpen; bool debugEnabled; bool outputEnabled; + int type; int lastButtons; - VMulti(); + VMulti(int type); ~VMulti(); bool HasReportChanged(); void SetReport(void *buffer, int length); diff --git a/TabletDriverService/docs/VEIKK_VMulti_Descriptor.txt b/TabletDriverService/docs/VEIKK_VMulti_Descriptor.txt new file mode 100644 index 0000000..1fe8f87 --- /dev/null +++ b/TabletDriverService/docs/VEIKK_VMulti_Descriptor.txt @@ -0,0 +1,144 @@ + +// +// Absolute mouse +// +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x02, // Usage (Mouse) +0xA1, 0x01, // Collection (Application) +0x85, 0x01, // Report ID (1) +0x09, 0x01, // Usage (Pointer) +0xA1, 0x00, // Collection (Physical) +0x05, 0x09, // Usage Page (Button) +0x19, 0x01, // Usage Minimum (0x01) +0x29, 0x05, // Usage Maximum (0x05) +0x15, 0x00, // Logical Minimum (0) +0x25, 0x01, // Logical Maximum (1) +0x75, 0x01, // Report Size (1) +0x95, 0x05, // Report Count (5) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x95, 0x03, // Report Count (3) +0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x26, 0xFF, 0x7F, // Logical Maximum (32767) +0x75, 0x10, // Report Size (16) +0x95, 0x01, // Report Count (1) +0x55, 0x0F, // Unit Exponent (-1) +0x65, 0x11, // Unit (System: SI Linear, Length: Centimeter) +0x35, 0x00, // Physical Minimum (0) +0x45, 0x00, // Physical Maximum (0) +0x09, 0x30, // Usage (X) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x09, 0x31, // Usage (Y) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x38, // Usage (Wheel) +0x15, 0x81, // Logical Minimum (-127) +0x25, 0x7F, // Logical Maximum (127) +0x75, 0x08, // Report Size (8) +0x95, 0x01, // Report Count (1) +0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) +0xC0, // End Collection +0xC0, // End Collection + + +// +// Relative mouse +// +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x02, // Usage (Mouse) +0xA1, 0x01, // Collection (Application) +0x85, 0x03, // Report ID (3) +0x09, 0x01, // Usage (Pointer) +0xA1, 0x00, // Collection (Physical) +0x05, 0x09, // Usage Page (Button) +0x19, 0x01, // Usage Minimum (0x01) +0x29, 0x05, // Usage Maximum (0x05) +0x15, 0x00, // Logical Minimum (0) +0x25, 0x01, // Logical Maximum (1) +0x75, 0x01, // Report Size (1) +0x95, 0x05, // Report Count (5) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x95, 0x03, // Report Count (3) +0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x30, // Usage (X) +0x09, 0x31, // Usage (Y) +0x15, 0x81, // Logical Minimum (-127) +0x25, 0x7F, // Logical Maximum (127) +0x75, 0x08, // Report Size (8) +0x95, 0x02, // Report Count (2) +0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x38, // Usage (Wheel) +0x15, 0x81, // Logical Minimum (-127) +0x25, 0x7F, // Logical Maximum (127) +0x75, 0x08, // Report Size (8) +0x95, 0x01, // Report Count (1) +0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) +0xC0, // End Collection +0xC0, // End Collection + +// +// Digitizer +// +0x05, 0x0D, // Usage Page (Digitizer) +0x09, 0x01, // Usage (Digitizer) +0xA1, 0x01, // Collection (Application) +0x85, 0x02, // Report ID (2) +0x09, 0x20, // Usage (Stylus) +0xA1, 0x00, // Collection (Physical) +0x09, 0x42, // Usage (Tip Switch) +0x09, 0x44, // Usage (Barrel Switch) +0x09, 0x45, // Usage (Eraser) +0x15, 0x00, // Logical Minimum (0) +0x25, 0x01, // Logical Maximum (1) +0x75, 0x01, // Report Size (1) +0x95, 0x03, // Report Count (3) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x95, 0x02, // Report Count (2) +0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x09, 0x32, // Usage (In Range) +0x95, 0x01, // Report Count (1) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x95, 0x02, // Report Count (2) +0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x75, 0x10, // Report Size (16) +0x95, 0x01, // Report Count (1) +0x35, 0x00, // Physical Minimum (0) +0xA4, // Push +0x05, 0x01, // Usage Page (Generic Desktop Ctrls) +0x09, 0x30, // Usage (X) +0x46, 0xCB, 0x02, // Physical Maximum (715) +0x26, 0xFF, 0x7F, // Logical Maximum (32767) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x09, 0x31, // Usage (Y) +0x46, 0xCB, 0x02, // Physical Maximum (715) +0x26, 0xFF, 0x7F, // Logical Maximum (32767) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0xB4, // Pop +0x09, 0x30, // Usage (Tip Pressure) +0x45, 0x00, // Physical Maximum (0) +0x26, 0xFF, 0x1F, // Logical Maximum (8191) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0xC0, // End Collection +0xC0, // End Collection + +// +// VMulti +// +0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) +0x09, 0x01, // Usage (0x01) +0xA1, 0x01, // Collection (Application) +0x85, 0x09, // Report ID (9) +0x15, 0x00, // Logical Minimum (0) +0x26, 0xFF, 0x00, // Logical Maximum (255) +0x75, 0x08, // Report Size (8) +0x95, 0x40, // Report Count (64) +0x09, 0x02, // Usage (0x02) +0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x95, 0x40, // Report Count (64) +0x09, 0x02, // Usage (0x02) +0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) +0xC0, // End Collection + +// 249 bytes From e041d9f4f221603f8891ef84b5f19fc139b529eb Mon Sep 17 00:00:00 2001 From: hawku Date: Sun, 16 Dec 2018 13:49:54 +0200 Subject: [PATCH 72/83] v0.2.3 --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4dbc91..da9e4a3 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.2.2.zip +### http://hwk.fi/TabletDriver/TabletDriverV0.2.3.zip # @@ -20,7 +20,7 @@ The GUI minimizes to system tray / notification area. You can reopen the GUI by - Windows 10 64-bit - Windows 8.1 64-bit - Windows 8 64-bit - - Windows 7 64-bit (No multi-monitor support) + - Windows 7 64-bit (Multiple monitors do not work in absolute mode) # @@ -105,6 +105,12 @@ If you want to compile the code and don't want to install anything from the Tabl # ## Changelog + +>**v0.2.3:** +> - Fixed "TabletDriverService.exe has stopped working" error when a tablet is not connected. +> - Relative mouse mode can now have different sensitivity on X and Y axis. +> - Added SendInput output mode to GUI + >**v0.2.2:** > - XP-Pen G430S configuration by [frodriguez96](https://github.com/frodriguez96) and [riley-badour](https://github.com/riley-badour) > - Huion New 1060 Plus configuration by [riley-badour](https://github.com/riley-badour) From dbcbb5844a947cbee6fd3745039e7292f2aae6d8 Mon Sep 17 00:00:00 2001 From: hawku Date: Wed, 2 Jan 2019 20:21:45 +0200 Subject: [PATCH 73/83] VEIKK A50, Wacom CTL-6100, multiple areas, filter improvements, etc. - Added VEIKK A50 configuration. Tablet buttons from 9 to 13 are the touchpad gestures - Added Wacom CTL-6100 configuration - Added support for multiple tablet areas (suggested by Devocub). Right-click the area for more options - Added preset selector to the anti-smoothing and noise reduction filters - Added advanced smoothing filter (no GUI settings yet) - Added a tablet view tool that can be used to check how much filters are reducing or increasing the input latency (Menu->Tools->Tablet View) - Added force proportions button to Wacom area tool - Added 19 new tablets to wacom.cfg - Added precise volume control to button maps. Example custom mapping: VOLUMEUP0.5 - Improved anti-smoothing filter - Improved smoothing filter latency calculation. Latency is now higher if you use same latency value as with the older driver version - System timer resolution will now be modified to match with the smoothing filter rate - Relative positioning can now be used with every output mode - On Windows 7 the driver is now using SendInput as a standard output mode, so multiple monitors should work without changing the output mode - Windows Ink pressure deadzone can now be set separately for low and high pressure - The TabletDriverGUI now communicates with the driver through named pipes --- .gitignore | 3 +- TabletDriverGUI/App.xaml.cs | 12 +- TabletDriverGUI/Area.cs | 88 +- TabletDriverGUI/Configuration.cs | 214 ++- TabletDriverGUI/MainWindow.Areas.cs | 1542 ++++++++++++++++ TabletDriverGUI/MainWindow.Console.cs | 16 +- TabletDriverGUI/MainWindow.Driver.cs | 262 ++- TabletDriverGUI/MainWindow.Ink.cs | 30 +- TabletDriverGUI/MainWindow.Settings.cs | 1581 +++++++++-------- TabletDriverGUI/MainWindow.xaml | 540 ++++-- TabletDriverGUI/MainWindow.xaml.cs | 224 +-- TabletDriverGUI/MultimediaTimer.cs | 145 ++ TabletDriverGUI/NativeMethods.cs | 17 + TabletDriverGUI/TabletDriver.cs | 400 ++++- TabletDriverGUI/TabletDriverGUI.csproj | 42 +- TabletDriverGUI/Utils.cs | 22 + TabletDriverGUI/WindowAreaEditor.xaml | 120 ++ TabletDriverGUI/WindowAreaEditor.xaml.cs | 156 ++ ...nMapping.xaml => WindowButtonMapping.xaml} | 6 +- ...ng.xaml.cs => WindowButtonMapping.xaml.cs} | 143 +- TabletDriverGUI/WindowMessageBox.xaml | 39 + TabletDriverGUI/WindowMessageBox.xaml.cs | 46 + TabletDriverGUI/WindowTabletView.xaml | 62 + TabletDriverGUI/WindowTabletView.xaml.cs | 634 +++++++ TabletDriverGUI/WindowTabletViewSettings.xaml | 142 ++ .../WindowTabletViewSettings.xaml.cs | 206 +++ .../{WacomArea.xaml => WindowWacomArea.xaml} | 117 +- ...omArea.xaml.cs => WindowWacomArea.xaml.cs} | 374 ++-- .../CommandHandler.Auxiliary.cpp | 30 +- TabletDriverService/CommandHandler.Device.cpp | 28 +- .../CommandHandler.Filters.cpp | 151 +- TabletDriverService/CommandHandler.Other.cpp | 153 +- TabletDriverService/CommandHandler.Tablet.cpp | 153 +- TabletDriverService/DataFormatter.cpp | 15 +- TabletDriverService/DataFormatter.h | 10 +- TabletDriverService/HIDDevice.cpp | 26 +- TabletDriverService/HIDDevice.h | 2 + TabletDriverService/InputEmulator.cpp | 131 +- TabletDriverService/InputEmulator.h | 45 + TabletDriverService/Logger.cpp | 66 +- TabletDriverService/Logger.h | 1 + TabletDriverService/Main.cpp | 31 +- TabletDriverService/OutputDummy.cpp | 16 +- TabletDriverService/OutputManager.cpp | 95 +- TabletDriverService/OutputManager.h | 10 +- .../OutputSendInputAbsolute.cpp | 5 +- .../OutputSendInputRelative.cpp | 117 ++ TabletDriverService/OutputSendInputRelative.h | 17 + TabletDriverService/OutputSettings.cpp | 2 + TabletDriverService/OutputSettings.h | 2 + TabletDriverService/OutputVMultiAbsolute.cpp | 5 +- TabletDriverService/OutputVMultiDigitizer.cpp | 9 +- .../OutputVMultiDigitizerRelative.cpp | 140 ++ .../OutputVMultiDigitizerRelative.h | 40 + TabletDriverService/OutputVMultiRelative.cpp | 77 +- TabletDriverService/PipeHandler.cpp | 338 ++++ TabletDriverService/PipeHandler.h | 55 + TabletDriverService/ScreenMapper.cpp | 307 +++- TabletDriverService/ScreenMapper.h | 48 +- TabletDriverService/Tablet.cpp | 187 +- TabletDriverService/Tablet.h | 22 +- .../TabletDriverService.vcxproj | 8 + .../TabletDriverService.vcxproj.filters | 24 + .../TabletDriverService.vcxproj.user | 4 +- .../TabletFilterAdvancedSmoothing.cpp | 284 +++ .../TabletFilterAdvancedSmoothing.h | 52 + .../TabletFilterAntiSmoothing.cpp | 205 ++- .../TabletFilterAntiSmoothing.h | 19 +- .../TabletFilterNoiseReduction.cpp | 3 - TabletDriverService/TabletFilterSmoothing.cpp | 55 +- TabletDriverService/TabletFilterSmoothing.h | 3 +- TabletDriverService/TabletFilterTester.cpp | 48 +- TabletDriverService/TabletHandler.cpp | 181 +- TabletDriverService/TabletHandler.h | 3 + TabletDriverService/TabletSettings.cpp | 15 +- TabletDriverService/TabletSettings.h | 23 +- TabletDriverService/TabletState.cpp | 10 +- TabletDriverService/TabletState.h | 7 + TabletDriverService/Vector2D.cpp | 6 + TabletDriverService/Vector2D.h | 1 + TabletDriverService/config/tablet.cfg | 153 +- TabletDriverService/config/wacom.cfg | 1130 +++++++++++- TabletDriverService/stdafx.h | 5 +- 83 files changed, 9614 insertions(+), 2142 deletions(-) create mode 100644 TabletDriverGUI/MainWindow.Areas.cs create mode 100644 TabletDriverGUI/MultimediaTimer.cs create mode 100644 TabletDriverGUI/WindowAreaEditor.xaml create mode 100644 TabletDriverGUI/WindowAreaEditor.xaml.cs rename TabletDriverGUI/{ButtonMapping.xaml => WindowButtonMapping.xaml} (91%) rename TabletDriverGUI/{ButtonMapping.xaml.cs => WindowButtonMapping.xaml.cs} (57%) create mode 100644 TabletDriverGUI/WindowMessageBox.xaml create mode 100644 TabletDriverGUI/WindowMessageBox.xaml.cs create mode 100644 TabletDriverGUI/WindowTabletView.xaml create mode 100644 TabletDriverGUI/WindowTabletView.xaml.cs create mode 100644 TabletDriverGUI/WindowTabletViewSettings.xaml create mode 100644 TabletDriverGUI/WindowTabletViewSettings.xaml.cs rename TabletDriverGUI/{WacomArea.xaml => WindowWacomArea.xaml} (83%) rename TabletDriverGUI/{WacomArea.xaml.cs => WindowWacomArea.xaml.cs} (56%) create mode 100644 TabletDriverService/OutputSendInputRelative.cpp create mode 100644 TabletDriverService/OutputSendInputRelative.h create mode 100644 TabletDriverService/OutputVMultiDigitizerRelative.cpp create mode 100644 TabletDriverService/OutputVMultiDigitizerRelative.h create mode 100644 TabletDriverService/PipeHandler.cpp create mode 100644 TabletDriverService/PipeHandler.h create mode 100644 TabletDriverService/TabletFilterAdvancedSmoothing.cpp create mode 100644 TabletDriverService/TabletFilterAdvancedSmoothing.h diff --git a/.gitignore b/.gitignore index 7b755d8..ed15b76 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /TabletDriverGUI/obj/ /TabletDriverService/Release/ /TabletDriverService/Debug/ -/TabletDriverService/startuplog.txt \ No newline at end of file +/TabletDriverService/startuplog.txt +TabletDriverService/config/usersettings.cfg diff --git a/TabletDriverGUI/App.xaml.cs b/TabletDriverGUI/App.xaml.cs index 55afb50..14b7892 100644 --- a/TabletDriverGUI/App.xaml.cs +++ b/TabletDriverGUI/App.xaml.cs @@ -127,12 +127,16 @@ public App() } else { + MessageBox.Show("TabletDriverGUI is already open!"); + // Broadcast to the another instance to show itself NativeMethods.PostMessage( - (IntPtr)NativeMethods.HWND_BROADCAST, - NativeMethods.WM_SHOWTABLETDRIVERGUI, - IntPtr.Zero, - IntPtr.Zero); + (IntPtr)NativeMethods.HWND_BROADCAST, + NativeMethods.WM_SHOWTABLETDRIVERGUI, + IntPtr.Zero, + IntPtr.Zero + ); + Shutdown(); } } diff --git a/TabletDriverGUI/Area.cs b/TabletDriverGUI/Area.cs index cccb7d4..d2ed205 100644 --- a/TabletDriverGUI/Area.cs +++ b/TabletDriverGUI/Area.cs @@ -43,20 +43,29 @@ public double Rotation { double angle; _rotation = value; + angle = _rotation * Math.PI / 180; + _rotationMatrix[0] = Math.Cos(angle); _rotationMatrix[1] = Math.Sin(angle); _rotationMatrix[2] = -Math.Sin(angle); _rotationMatrix[3] = Math.Cos(angle); + UpdateCorners(); } } + public bool IsEnabled; + + private double _rotation; - private double[] _rotationMatrix; + private readonly double[] _rotationMatrix; + private readonly Point[] _corners; - private Point[] _corners; + // + // Constructors + // public Area() { _corners = new Point[4] { @@ -67,11 +76,11 @@ public Area() }; _rotation = 0; _rotationMatrix = new double[4] { 1, 0, 0, 1 }; - _width = 0; _height = 0; X = 0; Y = 0; + IsEnabled = false; } public Area(double width, double height, double x, double y) : this() @@ -80,10 +89,26 @@ public Area(double width, double height, double x, double y) : this() _height = height; X = x; Y = y; + IsEnabled = false; UpdateCorners(); } + // + // Copy values from an another area + // + public void Set(Area area) + { + X = area.X; + Y = area.Y; + Width = area.Width; + Height = area.Height; + Rotation = area.Rotation; + IsEnabled = area.IsEnabled; + } + // + // Update corner positions + // private void UpdateCorners() { GetRotatedPoint(ref _corners[0], -_width / 2.0, -_height / 2.0); @@ -92,6 +117,9 @@ private void UpdateCorners() GetRotatedPoint(ref _corners[3], -_width / 2.0, _height / 2.0); } + // + // Rotate point + // public void GetRotatedPoint(ref Point p, double x, double y) { p.X = x * _rotationMatrix[0] + y * _rotationMatrix[1]; @@ -99,13 +127,44 @@ public void GetRotatedPoint(ref Point p, double x, double y) } // + // Rotate point in reverse direction // + public void GetRotatedPointReverse(ref Point p, double x, double y) + { + p.X = x * _rotationMatrix[3] - y * _rotationMatrix[1]; + p.Y = x * -_rotationMatrix[2] + y * _rotationMatrix[0]; + } + + + // + // Check if a point is inside of the area // - public Point[] Corners + public bool IsInside(Point p) { - get { return _corners; } + double x1, y1, x2, y2; + + x1 = p.X - X; + y1 = p.Y - Y; + x2 = x1 * _rotationMatrix[0] + y1 * _rotationMatrix[1]; + y2 = x1 * _rotationMatrix[2] + y1 * _rotationMatrix[3]; + + if ( + x2 > -Width / 2.0 && + x2 < +Width / 2.0 && + y2 > -Height / 2.0 && + y2 < +Height / 2.0 + ) + { + return true; + } + + return false; } + // + // Get corners + // + public Point[] Corners => _corners; // // Bounding Box @@ -128,6 +187,7 @@ public double[] GetBoundingBox() return box; } + // // Scale to fit inside another area // @@ -146,6 +206,9 @@ public void ScaleInside(Area target) UpdateCorners(); } + // + // Move area position inside of another area + // public void MoveInside(Area target) { double[] box = GetBoundingBox(); @@ -161,5 +224,20 @@ public void MoveInside(Area target) Y = target.Y + targetBox[3] - box[3]; } + + // + // Convert to string + // + public override string ToString() + { + return + "Area[" + + Utils.GetNumberString(X) + "," + + Utils.GetNumberString(Y) + "," + + Utils.GetNumberString(Width) + "," + + Utils.GetNumberString(Height) + + "]"; + } + } } diff --git a/TabletDriverGUI/Configuration.cs b/TabletDriverGUI/Configuration.cs index f6068d7..7976e6e 100644 --- a/TabletDriverGUI/Configuration.cs +++ b/TabletDriverGUI/Configuration.cs @@ -1,7 +1,6 @@ using System; -using System.Linq; -using System.Collections; using System.IO; +using System.Windows; using System.Xml; using System.Xml.Serialization; @@ -11,22 +10,39 @@ namespace TabletDriverGUI public class Configuration { public int ConfigVersion; + + public string TabletName; + + public Area ScreenArea; + [XmlArray("ScreenAreas")] + [XmlArrayItem("ScreenArea")] + public Area[] ScreenAreas; + public Area SelectedScreenArea; + public Area TabletArea; + [XmlArray("TabletAreas")] + [XmlArrayItem("TabletArea")] + public Area[] TabletAreas; + public Area SelectedTabletArea; + public Area TabletFullArea; public bool ForceAspectRatio; public double Rotation; public bool Invert; - public bool ForceFullArea; - public OutputModes OutputMode; - public enum OutputModes + public OutputPositioning Positioning; + public OutputModes Mode; + + public enum OutputPositioning { Absolute = 0, - Relative = 1, - Digitizer = 2, - SendInput = 3 + Relative = 1 + } + public enum OutputModes + { + Standard = 0, + WindowsInk = 1, + Compatibility = 2 } - - public Area ScreenArea; // Smoothing filter public bool SmoothingEnabled; @@ -41,9 +57,25 @@ public enum OutputModes // Anti-smoothing filter public bool AntiSmoothingEnabled; - public double AntiSmoothingShape; - public double AntiSmoothingCompensation; + public class AntiSmoothingSetting + { + public bool Enabled; + public double Velocity; + public double Shape; + public double Compensation; + public AntiSmoothingSetting() + { + Enabled = false; + Velocity = 0; + Shape = 0.5; + Compensation = 0; + } + }; + [XmlArray("AntiSmoothingSettingsList")] + [XmlArrayItem("AntiSmoothingSettings")] + public AntiSmoothingSetting[] AntiSmoothingSettings; public bool AntiSmoothingOnlyWhenHover; + public double AntiSmoothingDragMultiplier; public Area DesktopSize; public bool AutomaticDesktopSize; @@ -59,11 +91,13 @@ public enum OutputModes public bool DisableTabletButtons; public double PressureSensitivity; - public double PressureDeadzone; + public double PressureDeadzoneLow; + public double PressureDeadzoneHigh; public double ScrollSensitivity; public double ScrollAcceleration; public bool ScrollStopCursor; + public bool ScrollDrag; [XmlArray("CustomCommands")] [XmlArrayItem("Command")] @@ -72,6 +106,42 @@ public enum OutputModes public int WindowWidth; public int WindowHeight; + public class TabletViewSettings + { + public string BackgroundColor; + public string InfoColor; + public string InputColor; + public string OutputColor; + public string LatencyColor; + public string DrawColor; + public int InputTrailLength; + public int OutputTrailLength; + public int DrawLength; + public string Font; + public double FontSize; + public Point OffsetText; + public Point OffsetPressure; + public bool FadeInOut; + public TabletViewSettings() + { + BackgroundColor = "#FFFFFF"; + InfoColor = "#000000"; + InputColor = "#33AA33"; + OutputColor = "#AA3333"; + LatencyColor = "#3333AA"; + DrawColor = "#000000"; + InputTrailLength = 30; + OutputTrailLength = 30; + DrawLength = 0; + Font = "Arial"; + FontSize = 25; + OffsetPressure = new Point(0, 0); + OffsetText = new Point(0, 0); + FadeInOut = false; + } + }; + public TabletViewSettings TabletView; + public bool AutomaticRestart; public bool RunAtStartup; @@ -81,19 +151,46 @@ public enum OutputModes public bool DebuggingEnabled; public bool DeveloperMode; + public class Preset + { + public string Name; + public Action Action; + public Preset(string name, Action action) + { + Name = name; + Action = action; + } + public override string ToString() + { + return Name; + } + } + public Configuration() { - ConfigVersion = 1; + ConfigVersion = 2; // Screen Map - ScreenArea = new Area(0, 0, 0, 0); + ScreenArea = null; + ScreenAreas = new Area[3]; + for (int i = 0; i < ScreenAreas.Length; i++) + { + ScreenAreas[i] = new Area(0, 0, 0, 0) + { + IsEnabled = false + }; + } + ScreenAreas[0].IsEnabled = true; + ScreenAreas[1] = new Area(1000, 500, 500, 250); // Tablet area - TabletArea = new Area(80, 45, 40, 22.5); + TabletArea = null; + TabletAreas = new Area[ScreenAreas.Length]; + for (int i = 0; i < GetAreaCount(); i++) + TabletAreas[i] = new Area(100, 56, 50, 28); TabletFullArea = new Area(100, 50, 50, 25); - ForceFullArea = true; - OutputMode = 0; + Mode = OutputModes.Standard; ForceAspectRatio = true; Rotation = 0; @@ -122,13 +219,18 @@ public Configuration() NoiseFilterThreshold = 0.5; AntiSmoothingEnabled = false; - AntiSmoothingShape = 0.5; - AntiSmoothingCompensation = 20.0; + AntiSmoothingSettings = new AntiSmoothingSetting[5]; + for (int i = 0; i < AntiSmoothingSettings.Length; i++) + AntiSmoothingSettings[i] = new AntiSmoothingSetting(); + AntiSmoothingDragMultiplier = 1.0; + AntiSmoothingOnlyWhenHover = false; CustomCommands = new string[] { "" }; - WindowWidth = 700; - WindowHeight = 710; + WindowWidth = 680; + WindowHeight = 650; + + TabletView = new TabletViewSettings(); AutomaticRestart = true; RunAtStartup = false; @@ -141,6 +243,70 @@ public Configuration() } + // + // Get number of areas + // + public int GetAreaCount() + { + if (ScreenAreas.Length <= TabletAreas.Length) + return ScreenAreas.Length; + return TabletAreas.Length; + } + + + // + // Get maximum number of areas + // + public int GetMaxAreaCount() + { + return 5; + } + + + // + // Get number of enabled areas + // + public int GetEnabledAreaCount() + { + int count = 0; + foreach (Area area in ScreenAreas) + { + if (area.IsEnabled) count++; + } + return count; + } + + + // + // Clear anti-smoothing filter settings + // + public void ClearAntiSmoothingSettings() + { + for (int i = 0; i < AntiSmoothingSettings.Length; i++) + { + AntiSmoothingSettings[i].Enabled = false; + AntiSmoothingSettings[i].Velocity = 0; + AntiSmoothingSettings[i].Shape = 0.5; + AntiSmoothingSettings[i].Compensation = 0; + } + } + + + // + // Set anti-smoothing filter settings + // + public void SetAntiSmoothingSetting(int index, bool enabled, double velocity, double shape, double compensation) + { + AntiSmoothingSettings[index].Enabled = enabled; + AntiSmoothingSettings[index].Velocity = velocity; + AntiSmoothingSettings[index].Shape = shape; + AntiSmoothingSettings[index].Compensation = compensation; + } + + + // + // Write configuration to a XML file + // public void Write(string filename) { var fileWriter = new StreamWriter(filename); @@ -160,6 +326,9 @@ public void Write(string filename) fileWriter.Close(); } + // + // Create configuration from a XML file + // public static Configuration CreateFromFile(string filename) { Configuration config = null; @@ -179,6 +348,7 @@ public static Configuration CreateFromFile(string filename) reader.Close(); return config; } + } diff --git a/TabletDriverGUI/MainWindow.Areas.cs b/TabletDriverGUI/MainWindow.Areas.cs new file mode 100644 index 0000000..87f8e53 --- /dev/null +++ b/TabletDriverGUI/MainWindow.Areas.cs @@ -0,0 +1,1542 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.Windows.Threading; + +namespace TabletDriverGUI +{ + public partial class MainWindow : Window + { + + // + // Screen map canvas elements + // + private Rectangle[] rectangleMonitors; + private Rectangle rectangleDesktop; + private Rectangle[] rectangleScreenAreas; + private Image imageDesktopScreenshot; + private Area lastDesktopSize; + private Matrix matrixScreenAreaToCanvas; + private Matrix matrixCanvasToScreenArea; + + // + // Tablet area canvas elements + // + private Polygon polygonTabletFullArea; + private Polygon[] polygonTabletAreas; + private Polygon polygonTabletAreaArrow; + private Ellipse ellipsePenPosition; + private Ellipse ellipsePenPosition2; + private int lastPenPositionIndex; + private Matrix matrixTabletAreaToCanvas; + private Matrix matrixCanvasToTabletArea; + + // Area colors + private Brush[] brushBackgrounds; + private Brush brushStrokeSelected; + private Brush brushStrokeNormal; + private static double selectedStrokeThickness = 4.0; + + + // Canvas clicked area + private class MouseArea + { + public Area Area; + public Point Point; + public int Index; + public bool IsValid; + public MouseArea() + { + Area = null; + Point = new Point(0, 0); + Index = 0; + IsValid = false; + } + } + private MouseArea mouseArea; + + + // Canvas Mouse drag + private class MouseDrag + { + public bool IsMouseDown; + public object Source; + public Point OriginMouse; + public Point OriginDraggable; + public Area DragArea; + public MouseDrag() + { + IsMouseDown = false; + Source = null; + OriginMouse = new Point(0, 0); + OriginDraggable = new Point(0, 0); + } + } + MouseDrag mouseDrag; + + + + // + // Get desktop size + // + System.Drawing.Rectangle GetVirtualDesktopSize() + { + System.Drawing.Rectangle rect = new System.Drawing.Rectangle(); + + // Windows 8 or greater needed for the multiscreen absolute mode + if (VersionHelper.IsWindows8OrGreater()) + { + rect.Width = System.Windows.Forms.SystemInformation.VirtualScreen.Width; + rect.Height = System.Windows.Forms.SystemInformation.VirtualScreen.Height; + } + else if(config.Mode != Configuration.OutputModes.Compatibility) + { + rect.Width = System.Windows.Forms.SystemInformation.VirtualScreen.Width; + rect.Height = System.Windows.Forms.SystemInformation.VirtualScreen.Height; + } + else + { + rect.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; + rect.Height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; + + } + return rect; + } + + + // + // Get available screens + // + System.Windows.Forms.Screen[] GetAvailableScreens() + { + System.Windows.Forms.Screen[] screens; + + // Windows 8 or greater needed for the multiscreen absolute mode + if (VersionHelper.IsWindows8OrGreater()) + screens = System.Windows.Forms.Screen.AllScreens; + else if (config.Mode != Configuration.OutputModes.Compatibility) + screens = System.Windows.Forms.Screen.AllScreens; + else + screens = new System.Windows.Forms.Screen[] { System.Windows.Forms.Screen.PrimaryScreen }; + return screens; + } + + // + // Get minimum screen position + // + Vector GetMinimumScreenPosition(System.Windows.Forms.Screen[] screens) + { + + // Monitor minimums + double minX = 0; + double minY = 0; + bool first = true; + foreach (System.Windows.Forms.Screen screen in screens) + { + if (first) + { + minX = screen.Bounds.X; + minY = screen.Bounds.Y; + first = false; + } + else + { + if (screen.Bounds.X < minX) minX = screen.Bounds.X; + if (screen.Bounds.Y < minY) minY = screen.Bounds.Y; + } + } + + return new Vector(minX, minY); + } + + // + // Fix tablet area dimension + // + void FixTabletAreaDimensions(Area tabletArea, Area screenArea) + { + // Limits + if (tabletArea.Width > config.TabletFullArea.Width) + tabletArea.Width = config.TabletFullArea.Width; + if (tabletArea.Height > config.TabletFullArea.Height) + tabletArea.Height = config.TabletFullArea.Height; + + // Aspect ratio + if (config.ForceAspectRatio) + { + double aspectRatio = screenArea.Width / screenArea.Height; + tabletArea.Height = tabletArea.Width / aspectRatio; + if (tabletArea.Height > config.TabletFullArea.Height) + { + tabletArea.Height = config.TabletFullArea.Height; + tabletArea.Width = tabletArea.Height * aspectRatio; + } + } + } + + + // + // Create canvas elements + // + void CreateCanvasElements() + { + // + // Screen map canvas + // + // Clear canvas + canvasScreenMap.Children.Clear(); + + // + // Area background brushes + // + brushBackgrounds = new SolidColorBrush[10]; + Color[] colors = new Color[10]; + for (int i = 0; i < colors.Length; i++) + { + colors[i] = Color.FromArgb(20, 20, 20, 20); + } + + colors[0] = Color.FromRgb(186, 255, 201); + colors[1] = Color.FromRgb(186, 225, 255); + colors[2] = Color.FromRgb(255, 223, 186); + colors[3] = Color.FromRgb(255, 179, 186); + colors[4] = Color.FromRgb(255, 255, 150); + + for (int i = 0; i < brushBackgrounds.Length; i++) + { + double multiplier = 0.9; + colors[i].R = (byte)(Math.Round(colors[i].R * multiplier)); + colors[i].G = (byte)(Math.Round(colors[i].G * multiplier)); + colors[i].B = (byte)(Math.Round(colors[i].B * multiplier)); + colors[i].A = 128; + brushBackgrounds[i] = new SolidColorBrush(colors[i]); + } + + // + // Selected brushes + // + brushStrokeSelected = new SolidColorBrush(Color.FromArgb(128, 255, 30, 30)); + brushStrokeNormal = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)); + + + // + // Screen map desktop screenshot + // + imageDesktopScreenshot = new Image + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Width = 100, + Height = 100, + Opacity = 0.7 + }; + canvasScreenMap.Children.Add(imageDesktopScreenshot); + lastDesktopSize = new Area(0, 0, 0, 0); + + + // Monitor rectangles + rectangleMonitors = new Rectangle[16]; + for (int i = 0; i < 16; i++) + { + rectangleMonitors[i] = new Rectangle + { + Width = 10, + Height = 10, + Stroke = Brushes.Black, + StrokeThickness = 1.0, + Fill = Brushes.Transparent, + Visibility = Visibility.Hidden + }; + canvasScreenMap.Children.Add(rectangleMonitors[i]); + } + + // + // Desktop area rectangle + // + rectangleDesktop = new Rectangle + { + Stroke = Brushes.Black, + StrokeThickness = 2.0, + Fill = Brushes.Transparent + }; + canvasScreenMap.Children.Add(rectangleDesktop); + + + // + // Screen map area rectangles + // + rectangleScreenAreas = new Rectangle[config.GetMaxAreaCount()]; + for (int i = config.GetMaxAreaCount() - 1; i >= 0; i--) + { + rectangleScreenAreas[i] = new Rectangle + { + Stroke = Brushes.Black, + StrokeThickness = 1.5, + Fill = brushBackgrounds[i], + Cursor = Cursors.Hand, + Visibility = Visibility.Collapsed + }; + canvasScreenMap.Children.Add(rectangleScreenAreas[i]); + } + + // Screen area matrix + matrixScreenAreaToCanvas = new Matrix(0, 0, 0, 0, 0, 0); + matrixCanvasToScreenArea = new Matrix(0, 0, 0, 0, 0, 0); + + // + // Tablet area canvas + // + // + // Clear + canvasTabletArea.Children.Clear(); + + // + // Tablet full area polygon + // + polygonTabletFullArea = new Polygon + { + Stroke = new SolidColorBrush(Color.FromRgb(100, 100, 100)), + StrokeThickness = 2.0, + Points = new PointCollection + { + new Point(0,0), + new Point(0,0), + new Point(0,0), + new Point(0,0) + } + }; + canvasTabletArea.Children.Add(polygonTabletFullArea); + + // + // Tablet area polygons + // + polygonTabletAreas = new Polygon[config.GetMaxAreaCount()]; + for (int i = config.GetMaxAreaCount() - 1; i >= 0; i--) + { + polygonTabletAreas[i] = new Polygon + { + Stroke = Brushes.Black, + StrokeLineJoin = PenLineJoin.Round, + Fill = brushBackgrounds[i], + StrokeThickness = 1.5, + Cursor = Cursors.Hand, + Points = new PointCollection + { + new Point(0,0), + new Point(0,0), + new Point(0,0), + new Point(0,0) + }, + }; + canvasTabletArea.Children.Add(polygonTabletAreas[i]); + } + + + // + // Tablet area arrow polygon + // + polygonTabletAreaArrow = new Polygon + { + Fill = new SolidColorBrush(Color.FromArgb(50, 20, 20, 20)), + Cursor = Cursors.Hand, + Points = new PointCollection + { + new Point(0,0), + new Point(0,0), + new Point(0,0) + }, + }; + canvasTabletArea.Children.Add(polygonTabletAreaArrow); + + // + // Tablet area pen position + // + ellipsePenPosition = new Ellipse + { + Stroke = Brushes.Green, + StrokeThickness = 1, + Width = 5, + Height = 5, + Visibility = Visibility.Collapsed + }; + canvasTabletArea.Children.Add(ellipsePenPosition); + + ellipsePenPosition2 = new Ellipse + { + Stroke = Brushes.Red, + StrokeThickness = 1, + Width = 5, + Height = 5, + Visibility = Visibility.Collapsed + }; + canvasTabletArea.Children.Add(ellipsePenPosition2); + + + timerUpdatePenPositions = new DispatcherTimer + { + Interval = new TimeSpan(0, 0, 0, 0, 20) + }; + lastPenPositionIndex = 0; + timerUpdatePenPositions.Tick += (sender, e) => + { + if (lastPenPositionIndex == 0) + { + ellipsePenPosition.Visibility = Visibility.Visible; + ellipsePenPosition2.Visibility = Visibility.Visible; + } + if (driver.tabletState.index != lastPenPositionIndex) + { + UpdateTabletAreaCanvasPenPositions(); + lastPenPositionIndex = driver.tabletState.index; + } + }; + timerUpdatePenPositions.Stop(); + + + // + // Tablet area matrix + // + matrixTabletAreaToCanvas = new Matrix(0, 0, 0, 0, 0, 0); + matrixCanvasToTabletArea = new Matrix(0, 0, 0, 0, 0, 0); + + // Canvas mouse area + mouseArea = new MouseArea(); + + // + // Canvas mouse drag + // + mouseDrag = new MouseDrag(); + } + + + // + // Update canvas elements + // + void UpdateCanvasElements() + { + if (config == null) return; + if (isLoadingSettings) return; + UpdateDesktopImage(); + UpdateScreenMapCanvas(); + UpdateTabletAreaCanvas(); + UpdateAreaInformation(); + } + + + // + // Update screen map desktop image + // + void UpdateDesktopImage() + { + if (imageDesktopScreenshot != null && imageDesktopScreenshot.Source != null) + { + if ( + config.DesktopSize.Width == lastDesktopSize.Width + && + config.DesktopSize.Height == lastDesktopSize.Height + ) + { + return; + } + } + + try + { + int screenLeft = System.Windows.Forms.SystemInformation.VirtualScreen.Left; + int screenTop = System.Windows.Forms.SystemInformation.VirtualScreen.Top; + int screenWidth = System.Windows.Forms.SystemInformation.VirtualScreen.Width; + int screenHeight = System.Windows.Forms.SystemInformation.VirtualScreen.Height; + + + // Create desktop screenshot bitmap + System.Drawing.Bitmap bitmapDesktop = new System.Drawing.Bitmap(screenWidth, screenHeight); + System.Drawing.Graphics graphicsDesktop = System.Drawing.Graphics.FromImage(bitmapDesktop); + graphicsDesktop.CopyFromScreen(screenLeft, screenTop, 0, 0, bitmapDesktop.Size, System.Drawing.CopyPixelOperation.SourceCopy); + + + // Create downscaled bitmap + double scaleX = canvasScreenMap.ActualWidth / screenWidth; + double scaleY = canvasScreenMap.ActualHeight / screenHeight; + double scale = scaleX; + if (scaleX > scaleY) + scale = scaleY; + System.Drawing.Bitmap bitmapDownscaled = new System.Drawing.Bitmap( + (int)Math.Round(screenWidth * scale), + (int)Math.Round(screenHeight * scale) + ); + System.Drawing.Graphics graphicsDownscaled = System.Drawing.Graphics.FromImage(bitmapDownscaled); + graphicsDownscaled.DrawImage(bitmapDesktop, 0, 0, bitmapDownscaled.Width, bitmapDownscaled.Height); + + + // Create source from the bitmap + IntPtr handleBitmap = bitmapDownscaled.GetHbitmap(); + BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( + handleBitmap, + IntPtr.Zero, + Int32Rect.Empty, + BitmapSizeOptions.FromEmptyOptions() + ); + imageDesktopScreenshot.Source = bitmapSource; + + // Release resources + NativeMethods.DeleteObject(handleBitmap); + graphicsDesktop.Dispose(); + bitmapDesktop.Dispose(); + graphicsDownscaled.Dispose(); + bitmapDownscaled.Dispose(); + + // Run GC + GC.Collect(10, GCCollectionMode.Forced); + + + // Update last desktop size + lastDesktopSize.Width = config.DesktopSize.Width; + lastDesktopSize.Height = config.DesktopSize.Height; + + } + catch (Exception) + { + } + } + + + // + // Update screen map canvas elements + // + void UpdateScreenMapCanvas() + { + + // Canvas element scaling + double scaleX = (canvasScreenMap.ActualWidth - 2) / config.DesktopSize.Width; + double scaleY = (canvasScreenMap.ActualHeight - 2) / config.DesktopSize.Height; + double scale = scaleX; + if (scaleX > scaleY) + scale = scaleY; + + // Centered offset + double offsetX = canvasScreenMap.ActualWidth / 2.0 - config.DesktopSize.Width * scale / 2.0; + double offsetY = canvasScreenMap.ActualHeight / 2.0 - config.DesktopSize.Height * scale / 2.0; + + // Matrices + matrixScreenAreaToCanvas.M11 = scale; + matrixScreenAreaToCanvas.M22 = scale; + matrixScreenAreaToCanvas.OffsetX = offsetX; + matrixScreenAreaToCanvas.OffsetY = offsetY; + + matrixCanvasToScreenArea.M11 = 1 / scale; + matrixCanvasToScreenArea.M22 = 1 / scale; + matrixCanvasToScreenArea.OffsetX = -offsetX / scale; + matrixCanvasToScreenArea.OffsetY = -offsetY / scale; + + + // Full desktop area + rectangleDesktop.Width = config.DesktopSize.Width * scale; + rectangleDesktop.Height = config.DesktopSize.Height * scale; + Canvas.SetLeft(rectangleDesktop, offsetX); + Canvas.SetTop(rectangleDesktop, offsetY); + + imageDesktopScreenshot.Width = config.DesktopSize.Width * scale; + imageDesktopScreenshot.Height = config.DesktopSize.Height * scale; + Canvas.SetLeft(imageDesktopScreenshot, offsetX); + Canvas.SetTop(imageDesktopScreenshot, offsetY); + + + // + // Screen map area rectangles + // + for (int i = 0; i < config.GetAreaCount(); i++) + { + rectangleScreenAreas[i].Width = config.ScreenAreas[i].Width * scale; + rectangleScreenAreas[i].Height = config.ScreenAreas[i].Height * scale; + Canvas.SetLeft(rectangleScreenAreas[i], offsetX + (config.ScreenAreas[i].X - config.ScreenAreas[i].Width / 2.0) * scale); + Canvas.SetTop(rectangleScreenAreas[i], offsetY + (config.ScreenAreas[i].Y - config.ScreenAreas[i].Height / 2.0) * scale); + + if (mouseArea.IsValid && mouseArea.Index == i) + { + rectangleScreenAreas[i].Stroke = brushStrokeSelected; + rectangleScreenAreas[i].StrokeThickness = selectedStrokeThickness; + } + else + { + rectangleScreenAreas[i].Stroke = brushStrokeNormal; + rectangleScreenAreas[i].StrokeThickness = 1.5; + } + + if (config.ScreenAreas[i].IsEnabled) + { + rectangleScreenAreas[i].Visibility = Visibility.Visible; + } + else + { + rectangleScreenAreas[i].Visibility = Visibility.Collapsed; + } + } + + // Only primary area enabled -> remove red border + if (config.GetEnabledAreaCount() == 1) + { + rectangleScreenAreas[0].Stroke = brushStrokeNormal; + rectangleScreenAreas[0].StrokeThickness = 1.5; + + } + + // + // Monitor rectangles + // + System.Windows.Forms.Screen[] screens = GetAvailableScreens(); + Vector minimumScreenPosition = GetMinimumScreenPosition(screens); + + // Hide all rectangles + for (int i = 0; i < rectangleMonitors.Length; i++) + rectangleMonitors[i].Visibility = Visibility.Collapsed; + + int rectangeIndex = 0; + foreach (System.Windows.Forms.Screen screen in screens) + { + double x = screen.Bounds.X - minimumScreenPosition.X; + double y = screen.Bounds.Y - minimumScreenPosition.Y; + + rectangleMonitors[rectangeIndex].Visibility = Visibility.Visible; + rectangleMonitors[rectangeIndex].Width = screen.Bounds.Width * scale; + rectangleMonitors[rectangeIndex].Height = screen.Bounds.Height * scale; + Canvas.SetLeft(rectangleMonitors[rectangeIndex], offsetX + x * scale); + Canvas.SetTop(rectangleMonitors[rectangeIndex], offsetY + y * scale); + + rectangeIndex++; + if (rectangeIndex >= 16) break; + } + + + canvasScreenMap.UpdateLayout(); + + } + + + // + // Update tablet area canvas elements + // + void UpdateTabletAreaCanvas() + { + double fullWidth = config.TabletFullArea.Width; + double fullHeight = config.TabletFullArea.Height; + + // Canvas element scaling + double scaleX = (canvasTabletArea.ActualWidth - 2) / fullWidth; + double scaleY = (canvasTabletArea.ActualHeight - 2) / fullHeight; + double scale = scaleX; + if (scaleX > scaleY) + scale = scaleY; + + double offsetX = canvasTabletArea.ActualWidth / 2.0 - fullWidth * scale / 2.0; + double offsetY = canvasTabletArea.ActualHeight / 2.0 - fullHeight * scale / 2.0; + + // Matrices + matrixTabletAreaToCanvas.M11 = scale; + matrixTabletAreaToCanvas.M22 = scale; + matrixTabletAreaToCanvas.OffsetX = offsetX; + matrixTabletAreaToCanvas.OffsetY = offsetY; + + matrixCanvasToTabletArea.M11 = 1 / scale; + matrixCanvasToTabletArea.M22 = 1 / scale; + matrixCanvasToTabletArea.OffsetX = -offsetX / scale; + matrixCanvasToTabletArea.OffsetY = -offsetY / scale; + + + + // + // Tablet full area + // + Point[] corners = config.TabletFullArea.Corners; + for (int i = 0; i < 4; i++) + { + Point p = corners[i]; + p.X *= scale; + p.Y *= scale; + p.X += config.TabletFullArea.X * scale + offsetX; + p.Y += config.TabletFullArea.Y * scale + offsetY; + polygonTabletFullArea.Points[i] = p; + } + + + // + // Tablet areas + // + for (int i = 0; i < config.GetAreaCount(); i++) + { + corners = config.TabletAreas[i].Corners; + for (int j = 0; j < 4; j++) + { + Point p = corners[j]; + p.X *= scale; + p.Y *= scale; + p.X += config.TabletAreas[i].X * scale + offsetX; + p.Y += config.TabletAreas[i].Y * scale + offsetY; + polygonTabletAreas[i].Points[j] = p; + } + + + if (mouseArea.IsValid && mouseArea.Index == i) + { + polygonTabletAreas[i].Stroke = brushStrokeSelected; + polygonTabletAreas[i].StrokeThickness = selectedStrokeThickness; + } + else + { + polygonTabletAreas[i].Stroke = brushStrokeNormal; + polygonTabletAreas[i].StrokeThickness = 1.5; + } + + if (config.ScreenAreas[i].IsEnabled) + { + polygonTabletAreas[i].Visibility = Visibility.Visible; + } + else + { + polygonTabletAreas[i].Visibility = Visibility.Collapsed; + } + } + + // Only primary area enabled -> remove red border + if (config.GetEnabledAreaCount() == 1) + { + polygonTabletAreas[0].Stroke = brushStrokeNormal; + polygonTabletAreas[0].StrokeThickness = 1.5; + } + + + + // + // Tablet area arrow + // + corners = config.TabletAreas[0].Corners; + polygonTabletAreaArrow.Points[0] = new Point( + offsetX + config.TabletAreas[0].X * scale, + offsetY + config.TabletAreas[0].Y * scale + ); + + polygonTabletAreaArrow.Points[1] = new Point( + offsetX + corners[2].X * scale + config.TabletAreas[0].X * scale, + offsetY + corners[2].Y * scale + config.TabletAreas[0].Y * scale + ); + + polygonTabletAreaArrow.Points[2] = new Point( + offsetX + corners[3].X * scale + config.TabletAreas[0].X * scale, + offsetY + corners[3].Y * scale + config.TabletAreas[0].Y * scale + ); + + + canvasTabletArea.UpdateLayout(); + + + + } + + + + // + // Update canvas pen positions + // + void UpdateTabletAreaCanvasPenPositions() + { + Point p = new Point(); + + // Inverted + if (config.Invert) + { + // Input position + p.X = config.TabletFullArea.Width - driver.tabletState.inputX; + p.Y = config.TabletFullArea.Height - driver.tabletState.inputY; + matrixTabletAreaToCanvas.Transform(p); + Canvas.SetLeft(ellipsePenPosition, p.X); + Canvas.SetTop(ellipsePenPosition, p.Y); + + // Output position + p.X = config.TabletFullArea.Width - driver.tabletState.outputX; + p.Y = config.TabletFullArea.Height - driver.tabletState.outputY; + matrixTabletAreaToCanvas.Transform(p); + Canvas.SetLeft(ellipsePenPosition2, p.X); + Canvas.SetTop(ellipsePenPosition2, p.Y); + } + else + { + // Input position + p.X = driver.tabletState.inputX; + p.Y = driver.tabletState.inputY; + matrixTabletAreaToCanvas.Transform(p); + Canvas.SetLeft(ellipsePenPosition, p.X); + Canvas.SetTop(ellipsePenPosition, p.Y); + + // Output position + p.X = driver.tabletState.outputX; + p.Y = driver.tabletState.outputY; + matrixTabletAreaToCanvas.Transform(p); + Canvas.SetLeft(ellipsePenPosition2, p.X); + Canvas.SetTop(ellipsePenPosition2, p.Y); + } + + } + + // + // Get area coordinates from canvas position + // + Point GetAreaCoordinates(UIElement canvas, Point p) + { + Point point = new Point(p.X, p.Y); + + if (canvas == canvasScreenMap) + { + point = matrixCanvasToScreenArea.Transform(point); + } + else if (canvas == canvasTabletArea) + { + point = matrixCanvasToTabletArea.Transform(point); + } + + return point; + } + + + // + // Update last clicked area + // + void UpdateMouseArea(UIElement sender, Point cursorPosition) + { + Area area = null; + Point areaPoint = new Point(0, 0); + int index = -1; + + Point clickPosition = GetAreaCoordinates(sender, cursorPosition); + + // + // Screen areas + // + if (sender == canvasScreenMap) + { + for (int i = 0; i < config.GetAreaCount(); i++) + { + if ( + config.ScreenAreas[i].IsEnabled + && + config.ScreenAreas[i].IsInside(clickPosition) + && + + // Area smaller than last found area? + ( + area == null + || + area.Width > config.ScreenAreas[i].Width + ) + ) + { + area = config.ScreenAreas[i]; + config.SelectedScreenArea = config.ScreenAreas[i]; + config.SelectedTabletArea = config.TabletAreas[i]; + index = i; + } + } + if (index == -1) + { + config.SelectedScreenArea = config.ScreenAreas[0]; + config.SelectedTabletArea = config.TabletAreas[0]; + } + } + + + // + // Tablet areas + // + else if (sender == canvasTabletArea) + { + for (int i = 0; i < config.GetAreaCount(); i++) + { + if ( + config.ScreenAreas[i].IsEnabled + && + config.TabletAreas[i].IsInside(clickPosition) + && + + // Area smaller than last found area? + ( + area == null + || + area.Width > config.TabletAreas[i].Width + ) + ) + { + area = config.TabletAreas[i]; + config.SelectedScreenArea = config.ScreenAreas[i]; + config.SelectedTabletArea = config.TabletAreas[i]; + index = i; + } + } + if (index == -1) + { + config.SelectedScreenArea = config.ScreenAreas[0]; + config.SelectedTabletArea = config.TabletAreas[0]; + } + } + + if (area != null) + { + mouseArea.Area = area; + mouseArea.Index = index; + mouseArea.IsValid = true; + } + else + { + mouseArea.Area = null; + mouseArea.Index = index; + mouseArea.IsValid = false; + } + mouseArea.Point.X = clickPosition.X; + mouseArea.Point.Y = clickPosition.Y; + + + } + + // + // Update area information + // + void UpdateAreaInformation() + { + // Update area information label + int areaIndex = mouseArea.Index; + if (areaIndex < 0) areaIndex = 0; + string areaText = "AREA #" + (areaIndex + 1) + " | "; + if (config.GetEnabledAreaCount() == 1) areaText = ""; + + // Screen area + labelScreenAreaInfo.Content = areaText + + "" + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedScreenArea.Height, "0.000") + ":1" + + " | " + Utils.GetNumberString(config.SelectedScreenArea.Width * config.SelectedScreenArea.Height, "0") + " pixels"; + + // Tablet area + labelTabletAreaInfo.Content = areaText + + "" + Utils.GetNumberString(config.SelectedTabletArea.Width / config.SelectedTabletArea.Height, "0.000") + ":1" + + " | " + Utils.GetNumberString(config.SelectedTabletArea.Width * config.SelectedTabletArea.Height, "0") + " mm²" + + " " + Utils.GetNumberString( + config.SelectedTabletArea.Width * config.SelectedTabletArea.Height / + (config.TabletFullArea.Width * config.TabletFullArea.Height) * 100.0 + , "0") + "%" + + " | " + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedTabletArea.Width, "0.0") + " x " + + Utils.GetNumberString(config.SelectedScreenArea.Height / config.SelectedTabletArea.Height, "0.0") + " px/mm"; + + } + + // + // Canvas mouse events + // + // + // Canvas mouse down + private void Canvas_MouseDown(object sender, MouseButtonEventArgs e) + { + bool mouseDown = false; + + // Is the button left mouse button? + if (e.LeftButton == MouseButtonState.Pressed) + mouseDown = true; + + // Update last clicked area + Point cursorPosition = e.GetPosition((UIElement)sender); + UpdateMouseArea((UIElement)sender, cursorPosition); + + if (sender != canvasScreenMap && sender != canvasTabletArea) return; + + mouseDrag.Source = (UIElement)sender; + mouseDrag.OriginMouse = cursorPosition; + + // + // Screen map drag + // + if (mouseDrag.Source == canvasScreenMap) + { + if (!mouseArea.IsValid) return; + mouseDrag.DragArea = mouseArea.Area; + + // Reset monitor selection + comboBoxMonitor.SelectedIndex = -1; + + mouseDrag.IsMouseDown = mouseDown; + mouseDrag.OriginDraggable = new Point(mouseDrag.DragArea.X, mouseDrag.DragArea.Y); + canvasScreenMap.CaptureMouse(); + } + + // + // Tablet area drag + // + else if (mouseDrag.Source == canvasTabletArea) + { + if (!mouseArea.IsValid) return; + mouseDrag.DragArea = mouseArea.Area; + + mouseDrag.IsMouseDown = mouseDown; + mouseDrag.OriginDraggable = new Point(mouseDrag.DragArea.X, mouseDrag.DragArea.Y); + canvasTabletArea.CaptureMouse(); + } + + } + + + // + // Canvas mouse up + // + private void Canvas_MouseUp(object sender, MouseButtonEventArgs e) + { + mouseDrag.IsMouseDown = false; + LoadSettingsFromConfiguration(); + //isLoadingSettings = true; + textScreenAreaX.Text = Utils.GetNumberString(config.SelectedScreenArea.X - config.SelectedScreenArea.Width / 2.0, "0"); + textScreenAreaY.Text = Utils.GetNumberString(config.SelectedScreenArea.Y - config.SelectedScreenArea.Height / 2.0, "0"); + textTabletAreaX.Text = Utils.GetNumberString(config.SelectedTabletArea.X); + textTabletAreaY.Text = Utils.GetNumberString(config.SelectedTabletArea.Y); + //isLoadingSettings = false; + canvasScreenMap.ReleaseMouseCapture(); + canvasTabletArea.ReleaseMouseCapture(); + } + + + // + // Canvas mouse move + // + private void Canvas_MouseMove(object sender, MouseEventArgs e) + { + Point position; + double dx, dy; + double scaleX = 0, scaleY = 0, scale = 0; + double gridSize = 1; + + // Canvas mouse drag + if (mouseDrag.IsMouseDown && mouseDrag.Source == sender) + { + position = e.GetPosition((UIElement)mouseDrag.Source); + + dx = position.X - mouseDrag.OriginMouse.X; + dy = position.Y - mouseDrag.OriginMouse.Y; + + // Shift + Drag -> Only move up/down + if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) + dx = 0; + + // Control + Drag -> Only move left/right + if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) + dy = 0; + + // Alt + Drag -> Use larger grid size + if (sender == canvasScreenMap && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) + gridSize = 80; + if (sender == canvasTabletArea && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) + gridSize = 5; + + + // Screen map canvas + if (mouseDrag.Source == canvasScreenMap && mouseDrag.DragArea != null) + { + scaleX = config.DesktopSize.Width / canvasScreenMap.ActualWidth; + scaleY = config.DesktopSize.Height / canvasScreenMap.ActualHeight; + scale = scaleY; + if (scaleX > scaleY) + scale = scaleX; + + // Grid + dx = Math.Round(dx * scale / gridSize) * gridSize / scale; + dy = Math.Round(dy * scale / gridSize) * gridSize / scale; + + // Move area + mouseDrag.DragArea.X = mouseDrag.OriginDraggable.X + dx * scale; + mouseDrag.DragArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; + + LoadSettingsFromConfiguration(); + } + + // Tablet area canvas + else if (mouseDrag.Source == canvasTabletArea && mouseDrag.DragArea != null) + { + scaleX = config.TabletFullArea.Width / canvasTabletArea.ActualWidth; + scaleY = config.TabletFullArea.Height / canvasTabletArea.ActualHeight; + scale = scaleY; + if (scaleX > scaleY) + scale = scaleX; + + // Grid + dx = Math.Round(dx * scale / gridSize) * gridSize / scale; + dy = Math.Round(dy * scale / gridSize) * gridSize / scale; + + // Move area + mouseDrag.DragArea.X = mouseDrag.OriginDraggable.X + dx * scale; + mouseDrag.DragArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; + + LoadSettingsFromConfiguration(); + } + } + + } + + + // + // Screen or tablet area mouse scroll + // + private void CanvasMouseWheel(object sender, MouseWheelEventArgs e) + { + + // + // Screen or tablet area scroll + // + if (sender == canvasScreenMap || sender == canvasTabletArea) + { + if (!mouseArea.IsValid) return; + double delta = e.Delta / 120.0; + if (sender == canvasScreenMap) + { + delta *= 10; + } + if (Keyboard.Modifiers == ModifierKeys.Control) + { + delta *= 10; + } + + double oldWidth = mouseArea.Area.Width; + double oldHeight = mouseArea.Area.Height; + mouseArea.Area.Width = Math.Round(oldWidth + delta); + mouseArea.Area.Height = oldHeight / oldWidth * mouseArea.Area.Width; + LoadSettingsFromConfiguration(); + } + + } + + // + // Canvas context menu click + // + private void Canvas_MenuClick(object sender, RoutedEventArgs e) + { + if (sender == null) return; + if (!(sender is MenuItem)) return; + MenuItem menuItem = (MenuItem)sender; + + + // + // Move screen area center of a monitor or set to full monitor + // + if (sender == menuCanvasMoveToMonitorCenter || sender == menuCanvasSetToFullMonitor) + { + if (!mouseArea.IsValid) return; + + // Get monitors + System.Windows.Forms.Screen[] screens = GetAvailableScreens(); + Vector minimumScreenPosition = GetMinimumScreenPosition(screens); + + // Loop through monitors + foreach (System.Windows.Forms.Screen screen in screens) + { + double x = screen.Bounds.X - minimumScreenPosition.X; + double y = screen.Bounds.Y - minimumScreenPosition.Y; + if ( + mouseArea.Point.X >= x && mouseArea.Point.X <= x + screen.Bounds.Width + && + mouseArea.Point.Y >= y && mouseArea.Point.Y <= y + screen.Bounds.Height + ) + { + mouseArea.Area.X = x + screen.Bounds.Width / 2.0; + mouseArea.Area.Y = y + screen.Bounds.Height / 2.0; + if (sender == menuCanvasSetToFullMonitor) + { + mouseArea.Area.Width = screen.Bounds.Width; + mouseArea.Area.Height = screen.Bounds.Height; + } + LoadSettingsFromConfiguration(); + break; + } + } + + } + + + // + // Set to full screen area + // + else if (sender == menuCanvasSetToFullDesktop) + { + if (!mouseArea.IsValid) return; + mouseArea.Area.X = config.DesktopSize.X; + mouseArea.Area.Y = config.DesktopSize.Y; + mouseArea.Area.Width = config.DesktopSize.Width; + mouseArea.Area.Height = config.DesktopSize.Height; + + LoadSettingsFromConfiguration(); + } + + + // + // Move tablet area to center + // + else if (sender == menuCanvasMoveToCenter) + { + if (!mouseArea.IsValid) return; + + mouseArea.Area.X = config.TabletFullArea.Width / 2.0; + mouseArea.Area.Y = config.TabletFullArea.Height / 2.0; + + LoadSettingsFromConfiguration(); + } + + + // + // Set to full tablet area + // + else if (sender == menuCanvasSetToFullTabletArea) + { + if (!mouseArea.IsValid) return; + mouseArea.Area.Width = config.TabletFullArea.Width; + mouseArea.Area.Height = config.TabletFullArea.Height; + mouseArea.Area.X = config.TabletFullArea.X; + mouseArea.Area.Y = config.TabletFullArea.Y; + FixTabletAreaDimensions(config.SelectedTabletArea, config.SelectedScreenArea); + LoadSettingsFromConfiguration(); + } + + // + // Force tablet area proportions + // + else if(sender == menuCanvasForceProportions) + { + double screenAspectRatio = config.SelectedScreenArea.Width / config.SelectedScreenArea.Height; + double tabletAspectRatio = config.SelectedTabletArea.Width / config.SelectedTabletArea.Height; + + // Tablet aspect ratio higher -> change width + if (tabletAspectRatio >= screenAspectRatio) + config.SelectedTabletArea.Width = config.SelectedTabletArea.Height * screenAspectRatio; + + // Tablet aspect ratio lower -> change height + else + config.SelectedTabletArea.Height = config.SelectedTabletArea.Width / screenAspectRatio; + + LoadSettingsFromConfiguration(); + } + + // + // Add secondary area + // + else if (sender == menuCanvasAddScreenArea || sender == menuCanvasAddTabletArea) + { + for (int i = 1; i < config.GetAreaCount(); i++) + { + // Area is not enabled? + if (!config.ScreenAreas[i].IsEnabled) + { + // Enable area + config.ScreenAreas[i].IsEnabled = true; + + // Screen area + if (sender == menuCanvasAddScreenArea) + { + config.ScreenAreas[i].X = mouseArea.Point.X; + config.ScreenAreas[i].Y = mouseArea.Point.Y; + } + else + { + config.ScreenAreas[i].X = config.DesktopSize.Width / 2.0; + config.ScreenAreas[i].Y = config.DesktopSize.Height / 2.0; + } + config.ScreenAreas[i].Width = 640; + config.ScreenAreas[i].Height = 640 * config.ScreenAreas[0].Height / config.ScreenAreas[0].Width; + + // Tablet area + if (sender == menuCanvasAddTabletArea) + { + config.TabletAreas[i].X = mouseArea.Point.X; + config.TabletAreas[i].Y = mouseArea.Point.Y; + } + else + { + config.TabletAreas[i].X = config.TabletFullArea.Width / 2.0; + config.TabletAreas[i].Y = config.TabletFullArea.Height / 2.0; + } + config.TabletAreas[i].Width = 50; + config.TabletAreas[i].Height = 50 * config.ScreenAreas[0].Height / config.ScreenAreas[0].Width; + + break; + } + } + LoadSettingsFromConfiguration(); + } + + // + // Remove area + // + else if (sender == menuCanvasRemoveScreenArea || sender == menuCanvasRemoveTabletArea) + { + if (!mouseArea.IsValid) return; + + WindowMessageBox messageBox = new WindowMessageBox( + "Are you sure?", "Remove area #" + (mouseArea.Index + 1) + "?", + "Yes", "No"); + messageBox.ShowDialog(); + if (messageBox.DialogResult == true) + { + bool removed = false; + for (int i = 1; i < config.GetAreaCount(); i++) + { + // Item to be removed found + if (!removed && config.ScreenAreas[i] == mouseArea.Area || config.TabletAreas[i] == mouseArea.Area) + { + removed = true; + } + + // Shift area arrays + else if (removed) + { + config.ScreenAreas[i - 1].Set(config.ScreenAreas[i]); + config.TabletAreas[i - 1].Set(config.TabletAreas[i]); + config.ScreenAreas[i].IsEnabled = false; + config.TabletAreas[i].IsEnabled = false; + } + } + + // Select primary area + if (removed) + { + config.SelectedScreenArea = config.ScreenAreas[0]; + config.SelectedTabletArea = config.TabletAreas[0]; + mouseArea.Area = null; + mouseArea.IsValid = false; + LoadSettingsFromConfiguration(); + } + } + } + + + // + // Edit area + // + else if (sender == menuCanvasEditScreenArea || sender == menuCanvasEditTabletArea) + { + if (!mouseArea.IsValid) return; + + int areaIndex = -1; + for (int i = 0; i < config.GetAreaCount(); i++) + { + if (config.ScreenAreas[i] == mouseArea.Area || config.TabletAreas[i] == mouseArea.Area) + { + areaIndex = i; + } + } + if (areaIndex >= 0) + { + WindowAreaEditor areaEditor = new WindowAreaEditor(config, config.ScreenAreas[areaIndex], config.TabletAreas[areaIndex]) + { + Title = "Area #" + (areaIndex + 1) + }; + areaEditor.ShowDialog(); + if (areaEditor.DialogResult == true) + { + LoadSettingsFromConfiguration(); + } + } + } + + + // + // Reset screen areas + // + else if (sender == menuCanvasResetScreenAreas) + { + WindowMessageBox messageBox = new WindowMessageBox( + "Are you sure?", "Reset all screen areas?", + "Yes", "No"); + messageBox.ShowDialog(); + if (messageBox.DialogResult == true) + { + double offsetY = -(config.GetEnabledAreaCount() * 100.0 / 2.0); + foreach (Area screenArea in config.ScreenAreas) + { + screenArea.X = config.DesktopSize.X; + screenArea.Y = config.DesktopSize.Y + offsetY; + screenArea.Width = config.DesktopSize.Width / 2.0; + screenArea.Height = config.DesktopSize.Height / 2.0; + offsetY += 100; + } + LoadSettingsFromConfiguration(); + } + } + + + // + // Reset tablet areas + // + else if (sender == menuCanvasResetTabletAreas) + { + WindowMessageBox messageBox = new WindowMessageBox( + "Are you sure?", "Reset all tablet areas?", + "Yes", "No"); + messageBox.ShowDialog(); + if (messageBox.DialogResult == true) + { + double offsetY = -(config.GetEnabledAreaCount() * 10.0 / 2.0); + foreach (Area tabletArea in config.TabletAreas) + { + tabletArea.X = config.TabletFullArea.X; + tabletArea.Y = config.TabletFullArea.Y + offsetY; + tabletArea.Width = config.TabletFullArea.Width / 2.0; + tabletArea.Height = config.TabletFullArea.Height / 2.0; + offsetY += 10.0; + + } + LoadSettingsFromConfiguration(); + } + } + + + } + + + // + // Canvas context menu opening + // + private void Canvas_ContextMenuOpening(object sender, ContextMenuEventArgs e) + { + + // + // Screen area canvas context menu + // + if (sender == canvasScreenMap) + { + if (!mouseArea.IsValid) + { + foreach (object item in canvasScreenMap.ContextMenu.Items) + ((UIElement)item).Visibility = Visibility.Collapsed; + menuCanvasResetScreenAreas.Visibility = Visibility.Visible; + } + else + { + // Find area index + int index = 0; + for (int i = 0; i < config.GetAreaCount(); i++) + { + if (config.ScreenAreas[i] == mouseArea.Area) index = i; + } + + // Create area information string + string areaInfo = "Area #" + (index + 1) + ": "; + areaInfo += Utils.GetNumberString(mouseArea.Area.Width, "0") + "x" + + Utils.GetNumberString(mouseArea.Area.Height, "0") + ", "; + areaInfo += Utils.GetNumberString(mouseArea.Area.Width / mouseArea.Area.Height, "0.000") + ":1"; + + menuCanvasScreenAreaInfo.Header = areaInfo; + foreach (object item in canvasScreenMap.ContextMenu.Items) + ((UIElement)item).Visibility = Visibility.Visible; + + if (index == 0) + { + menuCanvasRemoveScreenArea.Visibility = Visibility.Collapsed; + } + + + } + + // Hide add secondary area menu item + int areaCount = 0; + foreach (Area area in config.ScreenAreas) + { + if (area.IsEnabled) areaCount++; + } + if (areaCount < config.GetAreaCount()) + menuCanvasAddScreenArea.Visibility = Visibility.Visible; + else + menuCanvasAddScreenArea.Visibility = Visibility.Collapsed; + } + + // + // Tablet area canvas context menu + // + else if (sender == canvasTabletArea) + { + if (!mouseArea.IsValid) + { + foreach (object item in canvasTabletArea.ContextMenu.Items) + ((UIElement)item).Visibility = Visibility.Collapsed; + menuCanvasAddTabletArea.Visibility = Visibility.Visible; + menuCanvasResetTabletAreas.Visibility = Visibility.Visible; + + } + else + { + // Find area index + int index = 0; + for (int i = 0; i < config.GetAreaCount(); i++) + { + if (config.TabletAreas[i] == mouseArea.Area) index = i; + } + + // Create area information string + string areaInfo = "Area #" + (index + 1) + ": "; + areaInfo += Utils.GetNumberString(mouseArea.Area.Width) + "mm x " + + Utils.GetNumberString(mouseArea.Area.Height) + "mm, "; + areaInfo += Utils.GetNumberString(mouseArea.Area.Width / mouseArea.Area.Height, "0.000") + ":1"; + + menuCanvasTabletAreaInfo.Header = areaInfo; + + foreach (object item in canvasTabletArea.ContextMenu.Items) + ((UIElement)item).Visibility = Visibility.Visible; + + if (index == 0) + { + menuCanvasRemoveTabletArea.Visibility = Visibility.Collapsed; + } + } + + // Hide add secondary area menu item + int areaCount = 0; + foreach (Area area in config.ScreenAreas) + { + if (area.IsEnabled) areaCount++; + } + if (areaCount < config.GetAreaCount()) + menuCanvasAddTabletArea.Visibility = Visibility.Visible; + else + menuCanvasAddTabletArea.Visibility = Visibility.Collapsed; + + } + + } + + + + + #region Wacom / Draw area + + // + // Wacom Area + // + private void ButtonWacomArea_Click(object sender, RoutedEventArgs e) + { + WindowWacomArea wacom = new WindowWacomArea(config, config.SelectedTabletArea); + wacom.ShowDialog(); + + // Set button clicked + if (wacom.DialogResult == true) + { + LoadSettingsFromConfiguration(); + } + } + + + // + // Draw area + // + private void ButtonDrawArea_Click(object sender, RoutedEventArgs e) + { + if (!isEnabledMeasurementToArea) + { + isEnabledMeasurementToArea = true; + driver.SendCommand("Measure 2"); + SetStatus("Click the top left and the bottom right corners of the area with your tablet pen!"); + buttonDrawArea.IsEnabled = false; + } + } + + #endregion + + } +} diff --git a/TabletDriverGUI/MainWindow.Console.cs b/TabletDriverGUI/MainWindow.Console.cs index 060659e..a837946 100644 --- a/TabletDriverGUI/MainWindow.Console.cs +++ b/TabletDriverGUI/MainWindow.Console.cs @@ -22,6 +22,8 @@ private void ConsoleBufferToText() { StringBuilder stringBuilder = new StringBuilder(); + if (driver == null) return; + // Lock console driver.ConsoleLock(); @@ -108,11 +110,12 @@ private void ConsoleSendCommand(string line) textConsoleInput.ScrollToEnd(); try { - driver.SendCommand(line); + driver.SendPipeCommand(line); + //driver.SendCommand(line); } catch (Exception e) { - driver.ConsoleAddText("Error! " + e.Message); + driver.ConsoleAddLine("Error! " + e.Message); } } @@ -122,7 +125,12 @@ private void ConsoleSendCommand(string line) // private void TimerConsoleUpdate_Tick(object sender, EventArgs e) { - ConsoleBufferToText(); + + // Update console text if console tab is active + if (tabControl.SelectedItem == tabConsole && WindowState != WindowState.Minimized) + { + ConsoleBufferToText(); + } } @@ -134,7 +142,7 @@ private void TextConsoleInput_KeyDown(object sender, KeyEventArgs e) if (e.Key == Key.Enter) { string line = textConsoleInput.Text; - ConsoleSendCommand(line); + ConsoleSendCommand(line.Trim()); } } diff --git a/TabletDriverGUI/MainWindow.Driver.cs b/TabletDriverGUI/MainWindow.Driver.cs index a8ac04d..f962850 100644 --- a/TabletDriverGUI/MainWindow.Driver.cs +++ b/TabletDriverGUI/MainWindow.Driver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -27,11 +28,18 @@ void StartDriver() // Console timer timerConsoleUpdate.Start(); + // Pen position timer + //timerUpdatePenPositions.Start(); + driver.Start(config.DriverPath, config.DriverArguments); if (!driver.IsRunning) { SetStatus("Can't start the driver! Check the console!"); - driver.ConsoleAddText("ERROR! Can't start the driver!"); + driver.ConsoleAddLine("ERROR! Can't start the driver!"); + } + else + { + SetStatus("Driver starting..."); } } @@ -39,7 +47,7 @@ void StartDriver() catch (Exception e) { SetStatus("Can't start the driver! Check the console!"); - driver.ConsoleAddText("ERROR! Can't start the driver!\n " + e.Message); + driver.ConsoleAddLine("ERROR! Can't start the driver!\n " + e.Message); } } @@ -51,6 +59,9 @@ void StopDriver() { if (!running) return; running = false; + + //timerUpdatePenPositions.Stop(); + driver.Stop(); timerConsoleUpdate.Stop(); } @@ -63,69 +74,137 @@ private void SendSettingsToDriver() { if (!driver.IsRunning) return; + // Clear setting commands list settingCommands.Clear(); + // // Desktop size + // settingCommands.Add("DesktopSize " + textDesktopWidth.Text + " " + textDesktopHeight.Text); - // Screen area - settingCommands.Add("ScreenArea " + - Utils.GetNumberString(config.ScreenArea.Width) + " " + Utils.GetNumberString(config.ScreenArea.Height) + " " + - Utils.GetNumberString(config.ScreenArea.X) + " " + Utils.GetNumberString(config.ScreenArea.Y) - ); - // - // Tablet area + // Screen and tablet areas // - // Inverted - if (config.Invert) + int areaIndex = 0; + for (int i = 0; i < config.GetAreaCount(); i++) { - settingCommands.Add("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) - ); - settingCommands.Add("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation + 180)); - } - // Normal - else - { - settingCommands.Add("TabletArea " + - Utils.GetNumberString(config.TabletArea.Width) + " " + - Utils.GetNumberString(config.TabletArea.Height) + " " + - Utils.GetNumberString(config.TabletArea.X) + " " + - Utils.GetNumberString(config.TabletArea.Y) - ); - settingCommands.Add("Rotate " + Utils.GetNumberString(config.TabletArea.Rotation)); + if (config.ScreenAreas[i].IsEnabled) + { + // Screen area + settingCommands.Add("ScreenArea " + + Utils.GetNumberString(config.ScreenAreas[i].Width) + " " + Utils.GetNumberString(config.ScreenAreas[i].Height) + " " + + Utils.GetNumberString(config.ScreenAreas[i].X) + " " + Utils.GetNumberString(config.ScreenAreas[i].Y) + " " + + areaIndex + ); + + // Inverted tablet area + if (config.Invert) + { + settingCommands.Add("TabletArea " + + Utils.GetNumberString(config.TabletAreas[i].Width) + " " + + Utils.GetNumberString(config.TabletAreas[i].Height) + " " + + Utils.GetNumberString(config.TabletFullArea.Width - config.TabletAreas[i].X) + " " + + Utils.GetNumberString(config.TabletFullArea.Height - config.TabletAreas[i].Y) + " " + + areaIndex + ); + settingCommands.Add( + "Rotate " + Utils.GetNumberString(config.TabletAreas[0].Rotation + 180) + " " + + areaIndex + ); + + } + + // Normal tablet area + else + { + settingCommands.Add("TabletArea " + + Utils.GetNumberString(config.TabletAreas[i].Width) + " " + + Utils.GetNumberString(config.TabletAreas[i].Height) + " " + + Utils.GetNumberString(config.TabletAreas[i].X) + " " + + Utils.GetNumberString(config.TabletAreas[i].Y) + " " + + areaIndex + ); + settingCommands.Add( + "Rotate " + Utils.GetNumberString(config.TabletAreas[0].Rotation) + " " + + areaIndex + ); + } + areaIndex++; + } + + + } + settingCommands.Add("ScreenMapCount " + areaIndex); + // // Output Mode - switch (config.OutputMode) + // + switch (config.Mode) { - case Configuration.OutputModes.Absolute: - settingCommands.Add("OutputMode Absolute"); - break; - case Configuration.OutputModes.Relative: - settingCommands.Add("OutputMode Relative"); - settingCommands.Add("RelativeSensitivity " + - Utils.GetNumberString(config.ScreenArea.Width / config.TabletArea.Width) + - " " + - Utils.GetNumberString(config.ScreenArea.Height / config.TabletArea.Height) - ); - break; - case Configuration.OutputModes.Digitizer: - settingCommands.Add("OutputMode Digitizer"); + case Configuration.OutputModes.Standard: + + // Windows 8, 8.1, 10 + if (VersionHelper.IsWindows8OrGreater()) + { + if (config.Positioning == Configuration.OutputPositioning.Absolute) + settingCommands.Add("OutputMode Absolute"); + else + settingCommands.Add("OutputMode Relative"); + } + + // Windows 7 + else + { + if (config.Positioning == Configuration.OutputPositioning.Absolute) + settingCommands.Add("OutputMode SendInputAbsolute"); + else + settingCommands.Add("OutputMode SendInputRelative"); + } break; - case Configuration.OutputModes.SendInput: - settingCommands.Add("OutputMode SendInputAbsolute"); + case Configuration.OutputModes.WindowsInk: + if (config.Positioning == Configuration.OutputPositioning.Absolute) + settingCommands.Add("OutputMode DigitizerAbsolute"); + else + settingCommands.Add("OutputMode DigitizerRelative"); break; + case Configuration.OutputModes.Compatibility: + // Windows 8, 8.1, 10 + if (VersionHelper.IsWindows8OrGreater()) + { + if (config.Positioning == Configuration.OutputPositioning.Absolute) + settingCommands.Add("OutputMode SendInputAbsolute"); + else + settingCommands.Add("OutputMode SendInputRelative"); + } + + // Windows 7 + else + { + if (config.Positioning == Configuration.OutputPositioning.Absolute) + settingCommands.Add("OutputMode Absolute"); + else + settingCommands.Add("OutputMode Relative"); + } + break; + default: + break; } + // + // Relative positioning sensitivity + // + settingCommands.Add("RelativeSensitivity " + + Utils.GetNumberString(config.ScreenAreas[0].Width / config.TabletAreas[0].Width) + + " " + + Utils.GetNumberString(config.ScreenAreas[0].Height / config.TabletAreas[0].Height) + ); + + // // Pen button map // @@ -144,6 +223,7 @@ private void SendSettingsToDriver() } } + // // Tablet button map // @@ -170,7 +250,10 @@ private void SendSettingsToDriver() // Pressure // settingCommands.Add("PressureSensitivity " + Utils.GetNumberString(config.PressureSensitivity)); - settingCommands.Add("PressureDeadzone " + Utils.GetNumberString(config.PressureDeadzone)); + settingCommands.Add("PressureDeadzone " + + Utils.GetNumberString(config.PressureDeadzoneLow) + " " + + Utils.GetNumberString(config.PressureDeadzoneHigh) + ); // @@ -179,24 +262,30 @@ private void SendSettingsToDriver() settingCommands.Add("ScrollSensitivity " + Utils.GetNumberString(config.ScrollSensitivity)); settingCommands.Add("ScrollAcceleration " + Utils.GetNumberString(config.ScrollAcceleration)); settingCommands.Add("ScrollStopCursor " + (config.ScrollStopCursor ? "true" : "false")); + settingCommands.Add("ScrollDrag " + (config.ScrollDrag ? "true" : "false")); + // // Smoothing filter + // if (config.SmoothingEnabled) { - settingCommands.Add("FilterTimerInterval " + Utils.GetNumberString(config.SmoothingInterval)); settingCommands.Add( - "Smoothing " + Utils.GetNumberString(config.SmoothingLatency) + " 90 " + + "Smoothing " + Utils.GetNumberString(config.SmoothingLatency) + " " + (config.SmoothingOnlyWhenButtons ? "true" : "false") ); + settingCommands.Add("FilterTimerInterval " + Utils.GetNumberString(config.SmoothingInterval)); } else { - settingCommands.Add("FilterTimerInterval 10"); settingCommands.Add("Smoothing 0"); + settingCommands.Add("FilterTimerInterval 10"); } + + // // Noise filter + // if (config.NoiseFilterEnabled) { settingCommands.Add("Noise " + Utils.GetNumberString(config.NoiseFilterBuffer) + " " + Utils.GetNumberString(config.NoiseFilterThreshold)); @@ -207,20 +296,49 @@ private void SendSettingsToDriver() } + // // Anti-smoothing filter + // if (config.AntiSmoothingEnabled) { - settingCommands.Add("AntiSmoothing " + Utils.GetNumberString(config.AntiSmoothingShape, "0.000") + " " + - Utils.GetNumberString(config.AntiSmoothingCompensation) + " " + - (config.AntiSmoothingOnlyWhenHover ? "true" : "false")); + settingCommands.Add( + "AntiSmoothing " + + (config.AntiSmoothingOnlyWhenHover ? "true" : "false") + " " + + Utils.GetNumberString(config.AntiSmoothingDragMultiplier) + ); + + Configuration.AntiSmoothingSetting[] settings; + settings = (Configuration.AntiSmoothingSetting[])config.AntiSmoothingSettings.Clone(); + + // Sort + Array.Sort(settings, (a, b) => + { + if (a.Velocity > b.Velocity) return 1; + if (b.Velocity > a.Velocity) return -1; + return 0; + }); + foreach (var setting in settings) + { + if (setting.Enabled) + { + settingCommands.Add("AntiSmoothingAdd " + + Utils.GetNumberString(setting.Velocity) + " " + + Utils.GetNumberString(setting.Shape, "0.000") + " " + + Utils.GetNumberString(setting.Compensation) + ); + } + } + } else { - settingCommands.Add("AntiSmoothing 0"); + settingCommands.Add("AntiSmoothing off"); } + // // Debugging + // if (config.DebuggingEnabled) { settingCommands.Add("Debug true"); @@ -231,7 +349,9 @@ private void SendSettingsToDriver() } - // Commands after settings + // + // Custom commands + // if (config.CustomCommands.Length > 0) { foreach (string command in config.CustomCommands) @@ -244,6 +364,7 @@ private void SendSettingsToDriver() } } + // // Send commands to the driver // @@ -255,6 +376,7 @@ private void SendSettingsToDriver() driver.SendCommand(command); } + // // Write settings to usersettings.cfg // @@ -310,6 +432,8 @@ private void ProcessStatusMessage(string variableName, string parameters) { string tabletName = parameters; string title = "TabletDriverGUI - " + tabletName; + Regex regex = new Regex("\\([^\\)]+\\)"); + config.TabletName = regex.Replace(tabletName, ""); Title = title; // Limit notify icon text length @@ -333,10 +457,15 @@ private void ProcessStatusMessage(string variableName, string parameters) { config.TabletFullArea.Width = val; config.TabletFullArea.X = val / 2.0; - LoadSettingsFromConfiguration(); - UpdateSettingsToConfiguration(); if (isFirstStart) + { + config.TabletAreas[0].Width = config.TabletFullArea.Width; + config.TabletAreas[0].X = config.TabletFullArea.X; + FixTabletAreaDimensions(config.TabletAreas[0], config.ScreenAreas[0]); SendSettingsToDriver(); + } + LoadSettingsFromConfiguration(); + UpdateSettingsToConfiguration(); } } @@ -349,10 +478,15 @@ private void ProcessStatusMessage(string variableName, string parameters) { config.TabletFullArea.Height = val; config.TabletFullArea.Y = val / 2.0; - LoadSettingsFromConfiguration(); - UpdateSettingsToConfiguration(); if (isFirstStart) + { + config.TabletAreas[0].Height = config.TabletFullArea.Height; + config.TabletAreas[0].Y = config.TabletFullArea.Y; + FixTabletAreaDimensions(config.TabletAreas[0], config.ScreenAreas[0]); SendSettingsToDriver(); + } + LoadSettingsFromConfiguration(); + UpdateSettingsToConfiguration(); } } @@ -392,10 +526,10 @@ private void ProcessStatusMessage(string variableName, string parameters) double centerX = minimumX + areaWidth / 2.0; double centerY = minimumY + areaHeight / 2.0; - config.TabletArea.Width = areaWidth; - config.TabletArea.Height = areaHeight; - config.TabletArea.X = centerX; - config.TabletArea.Y = centerY; + config.SelectedTabletArea.Width = areaWidth; + config.SelectedTabletArea.Height = areaHeight; + config.SelectedTabletArea.X = centerX; + config.SelectedTabletArea.Y = centerY; LoadSettingsFromConfiguration(); UpdateSettingsToConfiguration(); @@ -487,7 +621,7 @@ private void OnDriverStopped(object sender, EventArgs e) if (config.AutomaticRestart) { SetStatus("Driver stopped. Restarting! Check console !!!"); - driver.ConsoleAddText("Driver stopped. Restarting!"); + driver.ConsoleAddLine("Driver stopped. Restarting!"); // Run in the main application thread Application.Current.Dispatcher.Invoke(() => @@ -500,7 +634,7 @@ private void OnDriverStopped(object sender, EventArgs e) else { SetStatus("Driver stopped!"); - driver.ConsoleAddText("Driver stopped!"); + driver.ConsoleAddLine("Driver stopped!"); } // Run in the main application thread diff --git a/TabletDriverGUI/MainWindow.Ink.cs b/TabletDriverGUI/MainWindow.Ink.cs index dacaf8a..cbc4fcb 100644 --- a/TabletDriverGUI/MainWindow.Ink.cs +++ b/TabletDriverGUI/MainWindow.Ink.cs @@ -1,21 +1,26 @@ using Microsoft.Win32; using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; +using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; -using System.Windows.Shapes; namespace TabletDriverGUI { public partial class MainWindow : Window { + // Ink canvas undo history + StrokeCollection inkCanvasUndoHistory; + + // Ink canvas DrawingAttributes + DrawingAttributes inkCanvasDrawingAttributes; + + // // Ink canvas stylys move // @@ -41,8 +46,12 @@ private void InkCanvas_StylusUp(object sender, StylusEventArgs e) progressPressure.Value = 0; Random random = new Random(); - byte shade = (byte)random.Next(0x33, 0x77); - inkCanvasDrawingAttributes.Color = Color.FromRgb(shade, shade, shade); + double shade = random.Next(0x33, 0x77); + inkCanvasDrawingAttributes.Color = Color.FromRgb( + (byte)(shade * (0.95 + random.NextDouble() * 0.1)), + (byte)(shade * (0.95 + random.NextDouble() * 0.1)), + (byte)(shade * (0.95 + random.NextDouble() * 0.1)) + ); if (inkCanvasUndoHistory != null && inkCanvasUndoHistory.Count > 0) { inkCanvasUndoHistory.Clear(); @@ -77,16 +86,21 @@ private void InkCanvas_KeyDown(object sender, KeyEventArgs e) private void SliderPressure_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { sliderPressureSensitivity.ToolTip = Utils.GetNumberString(-sliderPressureSensitivity.Value); - sliderPressureDeadzone.ToolTip = Utils.GetNumberString(sliderPressureDeadzone.Value * 100) + "%"; + sliderPressureDeadzoneLow.ToolTip = Utils.GetNumberString(sliderPressureDeadzoneLow.Value * 100) + "%"; + sliderPressureDeadzoneHigh.ToolTip = Utils.GetNumberString(sliderPressureDeadzoneHigh.Value * 100) + "%"; if (isLoadingSettings) return; config.PressureSensitivity = sliderPressureSensitivity.Value; - config.PressureDeadzone = sliderPressureDeadzone.Value; + config.PressureDeadzoneLow = sliderPressureDeadzoneLow.Value; + config.PressureDeadzoneHigh = sliderPressureDeadzoneHigh.Value; driver.SendCommand("PressureSensitivity " + Utils.GetNumberString(config.PressureSensitivity)); - driver.SendCommand("PressureDeadzone " + Utils.GetNumberString(config.PressureDeadzone)); + driver.SendCommand("PressureDeadzone " + + Utils.GetNumberString(config.PressureDeadzoneLow) + " " + + Utils.GetNumberString(config.PressureDeadzoneHigh) + ); } diff --git a/TabletDriverGUI/MainWindow.Settings.cs b/TabletDriverGUI/MainWindow.Settings.cs index e75e706..b4bea89 100644 --- a/TabletDriverGUI/MainWindow.Settings.cs +++ b/TabletDriverGUI/MainWindow.Settings.cs @@ -8,52 +8,384 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Shapes; +using System.Windows.Threading; namespace TabletDriverGUI { public partial class MainWindow : Window { + private CheckBox[] checkboxAntiSmoothingSettingEnabled; + private TextBox[] textAntiSmoothingVelocity; + private TextBox[] textAntiSmoothingShape; + private TextBox[] textAntiSmoothingCompensation; - #region Configuration Load / Update + List presetsNoiseReduction; + List presetsAntiSmoothing; + + WindowTabletView tabletView; + + + // + // Create setting UI components + // + private void CreateSettingElements() + { + + // + // Create tablet button map WrapPanel items + // + for (int i = 0; i < 16; i++) + { + GroupBox groupBox = new GroupBox + { + Width = 90, + Header = "Button " + (i + 1).ToString() + }; + Button button = new Button + { + Height = 22, + Content = "", + Padding = new Thickness(2, 0, 2, 0), + ToolTip = "Empty", + Background = Brushes.White + }; + button.Click += ButtonMap_Click; + button.ToolTipOpening += ButtonMap_ToolTipOpening; + + groupBox.Content = button; + wrapPanelTabletButtons.Children.Add(groupBox); + } + CheckBox checkBox = new CheckBox + { + Content = "Disable buttons" + }; + checkBox.Checked += CheckboxChanged; + checkBox.Unchecked += CheckboxChanged; + checkBox.VerticalAlignment = VerticalAlignment.Bottom; + checkBox.Margin = new Thickness(5, 5, 5, 10); + wrapPanelTabletButtons.Children.Add(checkBox); + + + + // + // Smoothing rate ComboBox + // + comboBoxSmoothingRate.Items.Clear(); + for (int i = 1; i <= 8; i++) + { + comboBoxSmoothingRate.Items.Add((1000.0 / i).ToString("0") + " Hz"); + } + comboBoxSmoothingRate.SelectedIndex = 3; + + + + // + // Noise reduction presets + // + + presetsNoiseReduction = new List + { + new Configuration.Preset("Wacom 470/471", (conf) => + { + conf.NoiseFilterBuffer = 10; + conf.NoiseFilterThreshold = 0.4; + }), + new Configuration.Preset("Wacom 472", (conf) => + { + conf.NoiseFilterBuffer = 10; + conf.NoiseFilterThreshold = 0.3; + }), + new Configuration.Preset("Wacom 480", (conf) => + { + conf.NoiseFilterBuffer = 10; + conf.NoiseFilterThreshold = 0.5; + }), + }; + comboBoxNoiseReductionPresets.Items.Clear(); + foreach (var preset in presetsNoiseReduction) + { + comboBoxNoiseReductionPresets.Items.Add(preset.Name); + } + comboBoxNoiseReductionPresets.SelectionChanged += (sender, e) => + { + int index = comboBoxNoiseReductionPresets.SelectedIndex; + if (index >= 0 && index < presetsNoiseReduction.Count) + { + Configuration.Preset preset = presetsNoiseReduction[comboBoxNoiseReductionPresets.SelectedIndex]; + preset.Action(config); + LoadSettingsFromConfiguration(); + SetStatus("Noise reduction filter preset '" + preset.Name + "' loaded!"); + } + }; + + + + + + + + // + // Anti-smoothing presets + // + presetsAntiSmoothing = new List + { + new Configuration.Preset("Clear", (conf) => { }), + new Configuration.Preset("Simple", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.5, 10); + }), + + new Configuration.Preset("Gaomon S56K", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 10); + }), + + new Configuration.Preset("Huion 420", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 15); + }), + + new Configuration.Preset("Huion H420", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 15); + }), + + new Configuration.Preset("Huion H430P", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 50); + conf.SetAntiSmoothingSetting(1, true, 70, 0.1, 30); + conf.SetAntiSmoothingSetting(2, true, 150, 0.1, 20); + }), + + new Configuration.Preset("Huion H640P", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 50); + conf.SetAntiSmoothingSetting(1, true, 70, 0.1, 30); + conf.SetAntiSmoothingSetting(2, true, 150, 0.1, 20); + }), + + new Configuration.Preset("VEIKK A50", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 10); + }), + + + new Configuration.Preset("VEIKK S640", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 0); + conf.SetAntiSmoothingSetting(1, true, 50, 1.5, 5); + conf.SetAntiSmoothingSetting(2, true, 100, 1.5, 10); + }), + + new Configuration.Preset("Wacom 490 Hover", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 25); + conf.SetAntiSmoothingSetting(1, true, 100, 0.1, 20); + conf.AntiSmoothingOnlyWhenHover = true; + }), + + new Configuration.Preset("XP-Pen G430", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 0); + conf.SetAntiSmoothingSetting(1, true, 50, 1.5, 5); + conf.SetAntiSmoothingSetting(2, true, 200, 1.5, 10); + }), + + new Configuration.Preset("XP-Pen G640", (conf) => + { + conf.SetAntiSmoothingSetting(0, true, 0, 0.1, 0); + conf.SetAntiSmoothingSetting(1, true, 50, 1.5, 5); + conf.SetAntiSmoothingSetting(2, true, 200, 1.5, 10); + }), + + }; + + + // + // Anti-smoothing preset combobox + // + comboBoxAntiSmoothingPresets.Items.Clear(); + foreach (var preset in presetsAntiSmoothing) + { + comboBoxAntiSmoothingPresets.Items.Add(preset.Name); + } + comboBoxAntiSmoothingPresets.SelectionChanged += (sender, e) => + { + int index = comboBoxAntiSmoothingPresets.SelectedIndex; + if (index >= 0 && index < presetsAntiSmoothing.Count) + { + Configuration.Preset preset = presetsAntiSmoothing[comboBoxAntiSmoothingPresets.SelectedIndex]; + config.AntiSmoothingOnlyWhenHover = false; + config.AntiSmoothingDragMultiplier = 1.0; + config.ClearAntiSmoothingSettings(); + preset.Action(config); + LoadSettingsFromConfiguration(); + SetStatus("Anti-smoothing filter preset '" + preset.Name + "' loaded!"); + } + }; + + + // + // Anti-smoothing filter settings + // + int antiSmoothingSettingCount = 5; + checkboxAntiSmoothingSettingEnabled = new CheckBox[antiSmoothingSettingCount]; + textAntiSmoothingVelocity = new TextBox[antiSmoothingSettingCount]; + textAntiSmoothingShape = new TextBox[antiSmoothingSettingCount]; + textAntiSmoothingCompensation = new TextBox[antiSmoothingSettingCount]; + + if (config.AntiSmoothingSettings.Length < antiSmoothingSettingCount) + { + Configuration.AntiSmoothingSetting[] newSettings = new Configuration.AntiSmoothingSetting[antiSmoothingSettingCount]; + for (int i = 0; i < antiSmoothingSettingCount; i++) + { + if (i < config.AntiSmoothingSettings.Length) + { + newSettings[i] = config.AntiSmoothingSettings[i]; + } + else + { + newSettings[i] = new Configuration.AntiSmoothingSetting + { + Enabled = false, + Velocity = 0, + Shape = 0.5, + Compensation = 0 + }; + } + } + config.AntiSmoothingSettings = newSettings; + } + + + // + // Loop through anti-smoothing settings + // + for (int i = 0; i < antiSmoothingSettingCount; i++) + { + + // Enabled + checkboxAntiSmoothingSettingEnabled[i] = new CheckBox + { + Width = 20, + Height = 25, + Margin = new Thickness(1), + VerticalContentAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Right + }; + checkboxAntiSmoothingSettingEnabled[i].Checked += CheckboxChanged; + checkboxAntiSmoothingSettingEnabled[i].Unchecked += CheckboxChanged; + stackPanelAntiSmoothingEnabled.Children.Add(checkboxAntiSmoothingSettingEnabled[i]); + + + // Velocity + Grid grid = new Grid(); + Label label = new Label { Content = "mm/s", HorizontalAlignment = HorizontalAlignment.Right }; + textAntiSmoothingVelocity[i] = new TextBox + { + Width = 80, + Height = 25, + Padding = new Thickness(2), + Margin = new Thickness(1) + }; + textAntiSmoothingVelocity[i].TextChanged += TextChanged; + textAntiSmoothingVelocity[i].DataContext = checkboxAntiSmoothingSettingEnabled[i]; + textAntiSmoothingVelocity[i].SetBinding(TextBox.IsEnabledProperty, "IsChecked"); + grid.Children.Add(textAntiSmoothingVelocity[i]); + grid.Children.Add(label); + stackPanelAntiSmoothingVelocity.Children.Add(grid); + + + // Shape + textAntiSmoothingShape[i] = new TextBox + { + Width = 60, + Height = 25, + Padding = new Thickness(2), + Margin = new Thickness(1) + }; + textAntiSmoothingShape[i].TextChanged += TextChanged; + textAntiSmoothingShape[i].DataContext = checkboxAntiSmoothingSettingEnabled[i]; + textAntiSmoothingShape[i].SetBinding(TextBox.IsEnabledProperty, "IsChecked"); + stackPanelAntiSmoothingShape.Children.Add(textAntiSmoothingShape[i]); + + + // Compensation + grid = new Grid(); + label = new Label { Content = "ms", HorizontalAlignment = HorizontalAlignment.Right }; + textAntiSmoothingCompensation[i] = new TextBox + { + Width = 100, + Height = 25, + Padding = new Thickness(2), + Margin = new Thickness(1) + }; + textAntiSmoothingCompensation[i].TextChanged += TextChanged; + textAntiSmoothingCompensation[i].DataContext = checkboxAntiSmoothingSettingEnabled[i]; + textAntiSmoothingCompensation[i].SetBinding(TextBox.IsEnabledProperty, "IsChecked"); + + grid.Children.Add(textAntiSmoothingCompensation[i]); + grid.Children.Add(label); + stackPanelAntiSmoothingCompensation.Children.Add(grid); + + } + + + // + // Tablet view window + // + tabletView = null; + + } + + + + #region Configuration Load, Update, Init // // Load settings from configuration // private void LoadSettingsFromConfiguration() { + if (isLoadingSettings) return; isLoadingSettings = true; // // Tablet area // - 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); + textTabletAreaWidth.Text = Utils.GetNumberString(config.SelectedTabletArea.Width); + textTabletAreaHeight.Text = Utils.GetNumberString(config.SelectedTabletArea.Height); + textTabletAreaX.Text = Utils.GetNumberString(config.SelectedTabletArea.X); + textTabletAreaY.Text = Utils.GetNumberString(config.SelectedTabletArea.Y); checkBoxForceAspect.IsChecked = config.ForceAspectRatio; - checkBoxForceFullArea.IsChecked = config.ForceFullArea; - switch (config.OutputMode) + + + // + // Positioning & Mode + // + comboBoxOutputPositioning.SelectedIndex = (int)config.Positioning; + + switch (config.Mode) { - case Configuration.OutputModes.Absolute: - radioModeAbsolute.IsChecked = true; + case Configuration.OutputModes.Standard: + radioOutputModeStandard.IsChecked = true; break; - case Configuration.OutputModes.Relative: - radioModeRelative.IsChecked = true; + case Configuration.OutputModes.WindowsInk: + radioOutputModeWindowsInk.IsChecked = true; break; - case Configuration.OutputModes.Digitizer: - radioModeDigitizer.IsChecked = true; + case Configuration.OutputModes.Compatibility: + radioOutputModeCombatibility.IsChecked = true; break; - case Configuration.OutputModes.SendInput: - radioModeSendInput.IsChecked = true; + default: break; } + // // Windows Ink pressure // - if (config.OutputMode == Configuration.OutputModes.Digitizer) + if (config.Mode == Configuration.OutputModes.WindowsInk) groupBoxWindowsInkSettings.IsEnabled = true; else groupBoxWindowsInkSettings.IsEnabled = false; @@ -62,38 +394,9 @@ private void LoadSettingsFromConfiguration() // // Rotation // - textTabletAreaRotation.Text = Utils.GetNumberString(config.TabletArea.Rotation); + textTabletAreaRotation.Text = Utils.GetNumberString(config.TabletAreas[0].Rotation); checkBoxInvert.IsChecked = config.Invert; - - // - // Force full area - // - if (config.ForceFullArea) - { - textTabletAreaWidth.IsEnabled = false; - textTabletAreaHeight.IsEnabled = false; - textTabletAreaX.IsEnabled = false; - textTabletAreaY.IsEnabled = false; - } - else - { - textTabletAreaWidth.IsEnabled = true; - textTabletAreaHeight.IsEnabled = true; - textTabletAreaX.IsEnabled = true; - textTabletAreaY.IsEnabled = true; - } - - - // - // Screen area - // - 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"); - - // // Desktop size // @@ -103,6 +406,8 @@ private void LoadSettingsFromConfiguration() textDesktopHeight.Text = Utils.GetNumberString(GetVirtualDesktopSize().Height); config.DesktopSize.Width = GetVirtualDesktopSize().Width; config.DesktopSize.Height = GetVirtualDesktopSize().Height; + config.DesktopSize.X = config.DesktopSize.Width / 2.0; + config.DesktopSize.Y = config.DesktopSize.Height / 2.0; textDesktopWidth.IsEnabled = false; textDesktopHeight.IsEnabled = false; } @@ -110,27 +415,55 @@ private void LoadSettingsFromConfiguration() { textDesktopWidth.Text = Utils.GetNumberString(config.DesktopSize.Width); textDesktopHeight.Text = Utils.GetNumberString(config.DesktopSize.Height); + config.DesktopSize.X = config.DesktopSize.Width / 2.0; + config.DesktopSize.Y = config.DesktopSize.Height / 2.0; } checkBoxAutomaticDesktopSize.IsChecked = config.AutomaticDesktopSize; + // + // Screen area + // + textScreenAreaWidth.Text = Utils.GetNumberString(config.SelectedScreenArea.Width, "0"); + textScreenAreaHeight.Text = Utils.GetNumberString(config.SelectedScreenArea.Height, "0"); + textScreenAreaX.Text = Utils.GetNumberString(config.SelectedScreenArea.X - config.SelectedScreenArea.Width / 2.0, "0"); + textScreenAreaY.Text = Utils.GetNumberString(config.SelectedScreenArea.Y - config.SelectedScreenArea.Height / 2.0, "0"); + + + // + // Move screen areas to valid positions + // + for (int i = 0; i < config.GetAreaCount(); i++) + config.ScreenAreas[i].MoveInside(config.DesktopSize); + + textScreenAreaX.Text = Utils.GetNumberString(config.SelectedScreenArea.X - config.SelectedScreenArea.Width / 2.0); + textScreenAreaY.Text = Utils.GetNumberString(config.SelectedScreenArea.Y - config.SelectedScreenArea.Height / 2.0); + + // // Force aspect ratio // if (config.ForceAspectRatio) { - config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height); - textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height); + for (int i = 0; i < config.GetAreaCount(); i++) + { + config.TabletAreas[i].Height = config.TabletAreas[i].Width / (config.ScreenAreas[i].Width / config.ScreenAreas[i].Height); + } + + textTabletAreaHeight.Text = Utils.GetNumberString(config.SelectedTabletArea.Height); textTabletAreaHeight.IsEnabled = false; } // - // Move tablet area to a valid position + // Move tablet areas to valid positions // - config.TabletArea.MoveInside(config.TabletFullArea); - textTabletAreaX.Text = Utils.GetNumberString(config.TabletArea.X); - textTabletAreaY.Text = Utils.GetNumberString(config.TabletArea.Y); + for (int i = 0; i < config.GetAreaCount(); i++) + config.TabletAreas[i].MoveInside(config.TabletFullArea); + + textTabletAreaX.Text = Utils.GetNumberString(config.SelectedTabletArea.X); + textTabletAreaY.Text = Utils.GetNumberString(config.SelectedTabletArea.Y); + // @@ -176,7 +509,8 @@ private void LoadSettingsFromConfiguration() // Pressure // sliderPressureSensitivity.Value = config.PressureSensitivity; - sliderPressureDeadzone.Value = config.PressureDeadzone; + sliderPressureDeadzoneLow.Value = config.PressureDeadzoneLow; + sliderPressureDeadzoneHigh.Value = config.PressureDeadzoneHigh; // @@ -185,6 +519,7 @@ private void LoadSettingsFromConfiguration() textScrollSensitivity.Text = Utils.GetNumberString(config.ScrollSensitivity); textScrollAcceleration.Text = Utils.GetNumberString(config.ScrollAcceleration); checkBoxScrollStopCursor.IsChecked = config.ScrollStopCursor; + checkBoxScrollDrag.IsChecked = config.ScrollDrag; // @@ -194,18 +529,6 @@ private void LoadSettingsFromConfiguration() textSmoothingLatency.Text = Utils.GetNumberString(config.SmoothingLatency); comboBoxSmoothingRate.SelectedIndex = config.SmoothingInterval - 1; checkBoxSmoothingOnlyWhenButtons.IsChecked = config.SmoothingOnlyWhenButtons; - if (config.SmoothingEnabled) - { - textSmoothingLatency.IsEnabled = true; - comboBoxSmoothingRate.IsEnabled = true; - checkBoxSmoothingOnlyWhenButtons.IsEnabled = true; - } - else - { - textSmoothingLatency.IsEnabled = false; - comboBoxSmoothingRate.IsEnabled = false; - checkBoxSmoothingOnlyWhenButtons.IsEnabled = false; - } // @@ -214,38 +537,22 @@ private void LoadSettingsFromConfiguration() checkBoxNoiseFilter.IsChecked = config.NoiseFilterEnabled; textNoiseBuffer.Text = Utils.GetNumberString(config.NoiseFilterBuffer); textNoiseThreshold.Text = Utils.GetNumberString(config.NoiseFilterThreshold); - if (config.NoiseFilterEnabled) - { - textNoiseBuffer.IsEnabled = true; - textNoiseThreshold.IsEnabled = true; - } - else - { - textNoiseBuffer.IsEnabled = false; - textNoiseThreshold.IsEnabled = false; - } - // // Anti-smoothing filter // checkBoxAntiSmoothing.IsChecked = config.AntiSmoothingEnabled; - textAntiSmoothingShape.Text = Utils.GetNumberString(config.AntiSmoothingShape, "0.000"); - textAntiSmoothingCompensation.Text = Utils.GetNumberString(config.AntiSmoothingCompensation); checkBoxAntiSmoothingOnlyWhenHover.IsChecked = config.AntiSmoothingOnlyWhenHover; - if (config.AntiSmoothingEnabled) - { - textAntiSmoothingShape.IsEnabled = true; - textAntiSmoothingCompensation.IsEnabled = true; - checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = true; - } - else + textAntiSmoothingDragMultiplier.Text = Utils.GetNumberString(config.AntiSmoothingDragMultiplier); + for (int i = 0; i < config.AntiSmoothingSettings.Length; i++) { - textAntiSmoothingShape.IsEnabled = false; - textAntiSmoothingCompensation.IsEnabled = false; - checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = false; + checkboxAntiSmoothingSettingEnabled[i].IsChecked = config.AntiSmoothingSettings[i].Enabled; + textAntiSmoothingVelocity[i].Text = Utils.GetNumberString(config.AntiSmoothingSettings[i].Velocity); + textAntiSmoothingShape[i].Text = Utils.GetNumberString(config.AntiSmoothingSettings[i].Shape, "0.000"); + textAntiSmoothingCompensation[i].Text = Utils.GetNumberString(config.AntiSmoothingSettings[i].Compensation); } + // // Automatic restart // @@ -272,12 +579,16 @@ private void LoadSettingsFromConfiguration() // checkBoxDebugging.IsChecked = config.DebuggingEnabled; + // Settings loaded + isLoadingSettings = false; // Update canvases UpdateCanvasElements(); + // Update binding data context + DataContext = null; + DataContext = config; - isLoadingSettings = false; } @@ -295,79 +606,67 @@ private void UpdateSettingsToConfiguration() // // Tablet area // - 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; + if (Utils.ParseNumber(textTabletAreaWidth.Text, out double value)) + config.SelectedTabletArea.Width = value; + if (Utils.ParseNumber(textTabletAreaHeight.Text, out value)) + config.SelectedTabletArea.Height = value; + if (Utils.ParseNumber(textTabletAreaX.Text, out value)) + config.SelectedTabletArea.X = value; + if (Utils.ParseNumber(textTabletAreaY.Text, out value)) + config.SelectedTabletArea.Y = value; + if (Utils.ParseNumber(textTabletAreaRotation.Text, out value)) + config.TabletAreas[0].Rotation = value; + // Update secondary area rotations + for (int i = 1; i < config.GetAreaCount(); i++) + { + config.TabletAreas[i].Rotation = config.TabletAreas[0].Rotation; + } - // - // Output Mode - // - if (radioModeAbsolute.IsChecked == true) config.OutputMode = Configuration.OutputModes.Absolute; - else if (radioModeRelative.IsChecked == true) config.OutputMode = Configuration.OutputModes.Relative; - else if (radioModeDigitizer.IsChecked == true) config.OutputMode = Configuration.OutputModes.Digitizer; - else if (radioModeSendInput.IsChecked == true) config.OutputMode = Configuration.OutputModes.SendInput; + config.Invert = (bool)checkBoxInvert.IsChecked; + config.ForceAspectRatio = (bool)checkBoxForceAspect.IsChecked; // - // Force full area + // Output positioning and mode // - if (config.ForceFullArea) - { - // Set tablet area size to full area - config.TabletArea.Width = config.TabletFullArea.Width; - config.TabletArea.Height = config.TabletFullArea.Height; - - // Force aspect - if (config.ForceAspectRatio) - config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height); - - // Fit area to full area - config.TabletArea.ScaleInside(config.TabletFullArea); - - textTabletAreaWidth.Text = Utils.GetNumberString(config.TabletArea.Width); - textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height); - - } + if (comboBoxOutputPositioning.SelectedIndex >= 0) + config.Positioning = (Configuration.OutputPositioning)comboBoxOutputPositioning.SelectedIndex; + else + config.Positioning = Configuration.OutputPositioning.Absolute; + if (radioOutputModeStandard.IsChecked == true) config.Mode = Configuration.OutputModes.Standard; + else if (radioOutputModeWindowsInk.IsChecked == true) config.Mode = Configuration.OutputModes.WindowsInk; + else if (radioOutputModeCombatibility.IsChecked == true) config.Mode = Configuration.OutputModes.Compatibility; // - // Force the tablet area to be inside of the full area + // Force the tablet areas to be inside of the full area // - config.TabletArea.MoveInside(config.TabletFullArea); + for (int i = 0; i < config.GetAreaCount(); i++) + config.TabletAreas[i].MoveInside(config.TabletFullArea); // // Screen area // - 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; + if (Utils.ParseNumber(textScreenAreaWidth.Text, out value)) + config.SelectedScreenArea.Width = value; + if (Utils.ParseNumber(textScreenAreaHeight.Text, out value)) + config.SelectedScreenArea.Height = value; + if (Utils.ParseNumber(textScreenAreaX.Text, out value)) + config.SelectedScreenArea.X = value + config.SelectedScreenArea.Width / 2.0; + if (Utils.ParseNumber(textScreenAreaY.Text, out value)) + config.SelectedScreenArea.Y = value + config.SelectedScreenArea.Height / 2.0; + // // Desktop size // - if (Utils.ParseNumber(textDesktopWidth.Text, out val)) - config.DesktopSize.Width = val; - if (Utils.ParseNumber(textDesktopHeight.Text, out val)) - config.DesktopSize.Height = val; + if (Utils.ParseNumber(textDesktopWidth.Text, out value)) + config.DesktopSize.Width = value; + if (Utils.ParseNumber(textDesktopHeight.Text, out value)) + config.DesktopSize.Height = value; config.AutomaticDesktopSize = (bool)checkBoxAutomaticDesktopSize.IsChecked; if (config.AutomaticDesktopSize == true) { @@ -383,8 +682,8 @@ private void UpdateSettingsToConfiguration() // if (config.ForceAspectRatio) { - config.TabletArea.Height = config.TabletArea.Width / (config.ScreenArea.Width / config.ScreenArea.Height); - textTabletAreaHeight.Text = Utils.GetNumberString(config.TabletArea.Height); + config.SelectedTabletArea.Height = config.SelectedTabletArea.Width / (config.SelectedScreenArea.Width / config.SelectedScreenArea.Height); + textTabletAreaHeight.Text = Utils.GetNumberString(config.SelectedTabletArea.Height); } @@ -416,17 +715,19 @@ private void UpdateSettingsToConfiguration() // Pressure sensitivity // config.PressureSensitivity = sliderPressureSensitivity.Value; - config.PressureDeadzone = sliderPressureDeadzone.Value; + config.PressureDeadzoneLow = sliderPressureDeadzoneLow.Value; + config.PressureDeadzoneHigh = sliderPressureDeadzoneHigh.Value; // // Scroll // - if (Utils.ParseNumber(textScrollSensitivity.Text, out val)) - config.ScrollSensitivity = val; - if (Utils.ParseNumber(textScrollAcceleration.Text, out val)) - config.ScrollAcceleration = val; + if (Utils.ParseNumber(textScrollSensitivity.Text, out value)) + config.ScrollSensitivity = value; + if (Utils.ParseNumber(textScrollAcceleration.Text, out value)) + config.ScrollAcceleration = value; config.ScrollStopCursor = (bool)checkBoxScrollStopCursor.IsChecked; + config.ScrollDrag = (bool)checkBoxScrollDrag.IsChecked; // @@ -434,64 +735,40 @@ private void UpdateSettingsToConfiguration() // config.SmoothingEnabled = (bool)checkBoxSmoothing.IsChecked; config.SmoothingInterval = comboBoxSmoothingRate.SelectedIndex + 1; - if (Utils.ParseNumber(textSmoothingLatency.Text, out val)) - config.SmoothingLatency = val; + if (Utils.ParseNumber(textSmoothingLatency.Text, out value)) + config.SmoothingLatency = value; config.SmoothingOnlyWhenButtons = (bool)checkBoxSmoothingOnlyWhenButtons.IsChecked; - if (config.SmoothingEnabled) - { - textSmoothingLatency.IsEnabled = true; - comboBoxSmoothingRate.IsEnabled = true; - checkBoxSmoothingOnlyWhenButtons.IsEnabled = true; - } - else - { - textSmoothingLatency.IsEnabled = false; - comboBoxSmoothingRate.IsEnabled = false; - checkBoxSmoothingOnlyWhenButtons.IsEnabled = false; - } - // // Noise filter // config.NoiseFilterEnabled = (bool)checkBoxNoiseFilter.IsChecked; - if (Utils.ParseNumber(textNoiseBuffer.Text, out val)) - config.NoiseFilterBuffer = (int)val; - if (Utils.ParseNumber(textNoiseThreshold.Text, out val)) - config.NoiseFilterThreshold = val; - if (config.NoiseFilterEnabled) - { - textNoiseBuffer.IsEnabled = true; - textNoiseThreshold.IsEnabled = true; - } - else - { - textNoiseBuffer.IsEnabled = false; - textNoiseThreshold.IsEnabled = false; - } + if (Utils.ParseNumber(textNoiseBuffer.Text, out value)) + config.NoiseFilterBuffer = (int)value; + if (Utils.ParseNumber(textNoiseThreshold.Text, out value)) + config.NoiseFilterThreshold = value; // // Anti-smoothing filter // config.AntiSmoothingEnabled = (bool)checkBoxAntiSmoothing.IsChecked; - if (Utils.ParseNumber(textAntiSmoothingShape.Text, out val)) - config.AntiSmoothingShape = val; - if (Utils.ParseNumber(textAntiSmoothingCompensation.Text, out val)) - config.AntiSmoothingCompensation = val; config.AntiSmoothingOnlyWhenHover = (bool)checkBoxAntiSmoothingOnlyWhenHover.IsChecked; - if (config.AntiSmoothingEnabled) - { - textAntiSmoothingShape.IsEnabled = true; - textAntiSmoothingCompensation.IsEnabled = true; - checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = true; - } - else + if (Utils.ParseNumber(textAntiSmoothingDragMultiplier.Text, out value)) + config.AntiSmoothingDragMultiplier = value; + + for (int i = 0; i < config.AntiSmoothingSettings.Length; i++) { - textAntiSmoothingShape.IsEnabled = false; - textAntiSmoothingCompensation.IsEnabled = false; - checkBoxAntiSmoothingOnlyWhenHover.IsEnabled = false; + config.AntiSmoothingSettings[i].Enabled = (bool)checkboxAntiSmoothingSettingEnabled[i].IsChecked; + + if (Utils.ParseNumber(textAntiSmoothingVelocity[i].Text, out value)) + config.AntiSmoothingSettings[i].Velocity = value; + if (Utils.ParseNumber(textAntiSmoothingShape[i].Text, out value)) + config.AntiSmoothingSettings[i].Shape = value; + if (Utils.ParseNumber(textAntiSmoothingCompensation[i].Text, out value)) + config.AntiSmoothingSettings[i].Compensation = value; + } @@ -532,7 +809,6 @@ private void UpdateSettingsToConfiguration() } - // // Initialize configuration // @@ -541,562 +817,196 @@ private void InitializeConfiguration() isLoadingSettings = true; Width = config.WindowWidth; Height = config.WindowHeight; + if (isFirstStart) + { + SizeToContent = SizeToContent.WidthAndHeight; + UpdateLayout(); + SizeToContent = SizeToContent.Manual; + Height += 50; + } isLoadingSettings = false; - // Invalid config -> Set defaults - if (config.ScreenArea.Width == 0 || config.ScreenArea.Height == 0) + // + // Convert old configuration format area values + // + if (config.ScreenArea != null) { - config.DesktopSize.Width = GetVirtualDesktopSize().Width; - config.DesktopSize.Height = GetVirtualDesktopSize().Height; - config.ScreenArea.Width = config.DesktopSize.Width; - config.ScreenArea.Height = config.DesktopSize.Height; - config.ScreenArea.X = 0; - config.ScreenArea.Y = 0; + config.ScreenAreas[0].Width = config.ScreenArea.Width; + config.ScreenAreas[0].Height = config.ScreenArea.Height; + config.ScreenAreas[0].X = config.ScreenArea.X + config.ScreenArea.Width / 2.0; + config.ScreenAreas[0].Y = config.ScreenArea.Y + config.ScreenArea.Height / 2.0; + config.ScreenArea = null; + } + if (config.TabletArea != null) + { + config.TabletAreas[0].Set(config.TabletArea); + config.TabletArea = null; } - // Create canvas elements - CreateCanvasElements(); - - // Load settings from configuration - LoadSettingsFromConfiguration(); - - // Update the settings back to the configuration - UpdateSettingsToConfiguration(); - - // Set run at startup - SetRunAtStartup(config.RunAtStartup); - } - - #endregion - - - - // - // Save settings - // - private void SaveSettings(object sender, RoutedEventArgs e) - { - try + // + // Conversion from config format V1 + // + if (config.ConfigVersion <= 1) { - config.Write(configFilename); - SendSettingsToDriver(); // - // Enable/Disable Windows Ink pressure settings + // Convert old button map // - if (config.OutputMode == Configuration.OutputModes.Digitizer) - groupBoxWindowsInkSettings.IsEnabled = true; - else - groupBoxWindowsInkSettings.IsEnabled = false; - - SetStatus("Settings saved!"); - } - catch (Exception) - { - string dir = Directory.GetCurrentDirectory(); - MessageBox.Show("Error occured while saving the configuration.\n" + - "Make sure that it is possible to create and edit files in the '" + dir + "' directory.\n", - "ERROR!", MessageBoxButton.OK, MessageBoxImage.Error - ); - } - } - - - // - // Set run at startup - // - private void SetRunAtStartup(bool enabled) - { - try - { - string path = System.Reflection.Assembly.GetExecutingAssembly().Location; - string entryName = "TabletDriverGUI"; - RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); - if (enabled) - rk.SetValue(entryName, "\"" + path + "\" --hide"); - else - rk.DeleteValue(entryName, false); - - rk.Close(); - } - catch (Exception) - { - } - } - - - // - // Get desktop size - // - System.Drawing.Rectangle GetVirtualDesktopSize() - { - System.Drawing.Rectangle rect = new System.Drawing.Rectangle(); - - // Windows 8 or greater needed for the multiscreen absolute mode - if (VersionHelper.IsWindows8OrGreater() || config.OutputMode != Configuration.OutputModes.Absolute) - { - rect.Width = System.Windows.Forms.SystemInformation.VirtualScreen.Width; - rect.Height = System.Windows.Forms.SystemInformation.VirtualScreen.Height; - } - else - { - rect.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; - rect.Height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; - + bool isOld = true; + if (config.ButtonMap.Length == 3) + { + for (int i = 0; i < 3; i++) + { + if (config.ButtonMap[i] != "1" && config.ButtonMap[i] != "2" && config.ButtonMap[i] != "3") + { + isOld = false; + } + } + if (isOld) + { + for (int i = 0; i < 3; i++) + config.ButtonMap[i] = "MOUSE" + config.ButtonMap[i].ToString(); + } + } } - return rect; - } - - // - // Get available screens - // - System.Windows.Forms.Screen[] GetAvailableScreens() - { - System.Windows.Forms.Screen[] screens; - - // Windows 8 or greater needed for the multiscreen absolute mode - if (VersionHelper.IsWindows8OrGreater() || config.OutputMode != Configuration.OutputModes.Absolute) - screens = System.Windows.Forms.Screen.AllScreens; - else - screens = new System.Windows.Forms.Screen[] { System.Windows.Forms.Screen.PrimaryScreen }; - return screens; - } - - - - #region Canvas stuff - - // - // Create canvas elements - // - void CreateCanvasElements() - { - // - // Screen map canvas - // - // Clear canvas - canvasScreenMap.Children.Clear(); - - - // Monitor rectangles - rectangleMonitors = new Rectangle[16]; - for (int i = 0; i < 16; i++) + // Fix screen area array length + if (config.ScreenAreas.Length != config.GetMaxAreaCount()) { - rectangleMonitors[i] = new Rectangle + Area[] newScreenAreas = new Area[config.GetMaxAreaCount()]; + for (int i = 0; i < config.GetMaxAreaCount(); i++) { - Width = 10, - Height = 10, - Stroke = Brushes.Black, - StrokeThickness = 1.0, - Fill = Brushes.Transparent, - Visibility = Visibility.Hidden - }; - canvasScreenMap.Children.Add(rectangleMonitors[i]); + if (i < config.ScreenAreas.Length) + { + newScreenAreas[i] = config.ScreenAreas[i]; + } + else + { + newScreenAreas[i] = new Area(0, 0, 0, 0); + } + } + config.ScreenAreas = newScreenAreas; } - // - // Desktop area rectangle - // - rectangleDesktop = new Rectangle - { - Stroke = Brushes.Black, - StrokeThickness = 2.0, - Fill = Brushes.Transparent - }; - canvasScreenMap.Children.Add(rectangleDesktop); - - - // - // Screen map area rectangle - // - Brush brushScreenMap = new SolidColorBrush(Color.FromArgb(50, 20, 20, 20)); - rectangleScreenMap = new Rectangle - { - Stroke = Brushes.Black, - StrokeThickness = 2.0, - Fill = brushScreenMap - }; - canvasScreenMap.Children.Add(rectangleScreenMap); - - // - // Screen aspect ratio text - // - textScreenAspectRatio = new TextBlock - { - Text = "", - Foreground = Brushes.Black, - FontSize = 13, - FontWeight = FontWeights.Bold - }; - canvasScreenMap.Children.Add(textScreenAspectRatio); - - - - // - // Tablet area canvas - // - // - // Clear - canvasTabletArea.Children.Clear(); - - // - // Tablet full area polygon - // - polygonTabletFullArea = new Polygon + // Fix tablet area array length + if (config.TabletAreas.Length != config.GetMaxAreaCount()) { - Stroke = new SolidColorBrush(Color.FromRgb(100, 100, 100)), - StrokeThickness = 2.0, - Points = new PointCollection + Area[] newTabletAreas = new Area[config.GetMaxAreaCount()]; + for (int i = 0; i < config.GetMaxAreaCount(); i++) { - new Point(0,0), - new Point(0,0), - new Point(0,0), - new Point(0,0) - }, - }; - canvasTabletArea.Children.Add(polygonTabletFullArea); + if (i < config.TabletAreas.Length) + { + newTabletAreas[i] = config.TabletAreas[i]; + } + else + { + newTabletAreas[i] = new Area(0, 0, 0, 0); + } + } + config.TabletAreas = newTabletAreas; - // - // Tablet area polygon - // - polygonTabletArea = new Polygon - { - Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)), - Fill = new SolidColorBrush(Color.FromArgb(50, 20, 20, 20)), - StrokeThickness = 2.0, - Points = new PointCollection - { - new Point(0,0), - new Point(0,0), - new Point(0,0), - new Point(0,0) - }, - }; - canvasTabletArea.Children.Add(polygonTabletArea); + } - // - // Tablet area arrow polygon - // - polygonTabletAreaArrow = new Polygon + // Invalid screen area -> Set defaults + for (int i = 0; i < config.GetAreaCount(); i++) { - Fill = new SolidColorBrush(Color.FromArgb(50, 20, 20, 20)), - Points = new PointCollection + if (config.ScreenAreas[i].Width == 0 || config.ScreenAreas[i].Height == 0) { - new Point(0,0), - new Point(0,0), - new Point(0,0) - }, - }; - canvasTabletArea.Children.Add(polygonTabletAreaArrow); - - - // - // Tablet area aspect ratio text - // - textTabletAspectRatio = new TextBlock - { - Text = "", - Foreground = Brushes.Black, - FontSize = 13, - FontWeight = FontWeights.Bold - }; - canvasTabletArea.Children.Add(textTabletAspectRatio); - - // - // Canvas mouse drag - // - mouseDrag = new MouseDrag(); - } - - - // - // Update canvas elements - // - void UpdateCanvasElements() - { - UpdateScreenMapCanvas(); - UpdateTabletAreaCanvas(); - } - - - // - // Update screen map canvas elements - // - void UpdateScreenMapCanvas() - { - // Canvas element scaling - double scaleX = (canvasScreenMap.ActualWidth - 2) / config.DesktopSize.Width; - double scaleY = (canvasScreenMap.ActualHeight - 2) / config.DesktopSize.Height; - double scale = scaleX; - if (scaleX > scaleY) - scale = scaleY; - - // Centered offset - double offsetX = canvasScreenMap.ActualWidth / 2.0 - config.DesktopSize.Width * scale / 2.0; - double offsetY = canvasScreenMap.ActualHeight / 2.0 - config.DesktopSize.Height * scale / 2.0; - - - // Full desktop area - rectangleDesktop.Width = config.DesktopSize.Width * scale; - rectangleDesktop.Height = config.DesktopSize.Height * scale; - Canvas.SetLeft(rectangleDesktop, offsetX); - Canvas.SetTop(rectangleDesktop, offsetY); - + config.DesktopSize.Width = GetVirtualDesktopSize().Width; + config.DesktopSize.Height = GetVirtualDesktopSize().Height; + config.ScreenAreas[i].Width = config.DesktopSize.Width; + config.ScreenAreas[i].Height = config.DesktopSize.Height; + config.ScreenAreas[i].X = config.DesktopSize.Width / 2.0; + config.ScreenAreas[i].Y = config.DesktopSize.Height / 2.0; + } - // Screen map area - rectangleScreenMap.Width = config.ScreenArea.Width * scale; - rectangleScreenMap.Height = config.ScreenArea.Height * scale; - Canvas.SetLeft(rectangleScreenMap, offsetX + config.ScreenArea.X * scale); - Canvas.SetTop(rectangleScreenMap, offsetY + config.ScreenArea.Y * scale); + FixTabletAreaDimensions(config.TabletAreas[i], config.ScreenAreas[i]); + } - // Screen aspect ratio text - 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 - ); - Canvas.SetTop(textScreenAspectRatio, offsetY + - (config.ScreenArea.Y + config.ScreenArea.Height / 2.0) * scale - - textScreenAspectRatio.ActualHeight / 2.0 - ); + // Primary area is always enabled + config.ScreenAreas[0].IsEnabled = true; + // Set selected area to primary area + config.SelectedScreenArea = config.ScreenAreas[0]; + config.SelectedTabletArea = config.TabletAreas[0]; + // Reset tablet name + config.TabletName = ""; + // Load settings from configuration + LoadSettingsFromConfiguration(); - // Screens - System.Windows.Forms.Screen[] screens = GetAvailableScreens(); + // Update the settings back to the configuration + UpdateSettingsToConfiguration(); - // Monitor minimums - double minX = 99999; - double minY = 99999; - foreach (System.Windows.Forms.Screen screen in screens) - { - if (screen.Bounds.X < minX) minX = screen.Bounds.X; - if (screen.Bounds.Y < minY) minY = screen.Bounds.Y; - } + // Set run at startup + SetRunAtStartup(config.RunAtStartup); + // Set window data context + this.DataContext = config; - // Monitor rectangles - int rectangeIndex = 0; - foreach (System.Windows.Forms.Screen screen in screens) - { - double x = screen.Bounds.X - minX; - double y = screen.Bounds.Y - minY; + } - rectangleMonitors[rectangeIndex].Visibility = Visibility.Visible; - rectangleMonitors[rectangeIndex].Width = screen.Bounds.Width * scale; - rectangleMonitors[rectangeIndex].Height = screen.Bounds.Height * scale; - Canvas.SetLeft(rectangleMonitors[rectangeIndex], offsetX + x * scale); - Canvas.SetTop(rectangleMonitors[rectangeIndex], offsetY + y * scale); - rectangeIndex++; - if (rectangeIndex >= 16) break; - } + #endregion - } // - // Update tablet area canvas elements + // Save settings // - void UpdateTabletAreaCanvas() + private void SaveSettings(object sender, RoutedEventArgs e) { - double fullWidth = config.TabletFullArea.Width; - double fullHeight = config.TabletFullArea.Height; - - // Canvas element scaling - double scaleX = (canvasTabletArea.ActualWidth - 2) / fullWidth; - double scaleY = (canvasTabletArea.ActualHeight - 2) / fullHeight; - double scale = scaleX; - if (scaleX > scaleY) - scale = scaleY; - - - double offsetX = canvasTabletArea.ActualWidth / 2.0 - fullWidth * scale / 2.0; - double offsetY = canvasTabletArea.ActualHeight / 2.0 - fullHeight * scale / 2.0; - - // - // Tablet full area - // - Point[] corners = config.TabletFullArea.Corners; - for (int i = 0; i < 4; i++) + try { - Point p = corners[i]; - p.X *= scale; - p.Y *= scale; - p.X += config.TabletFullArea.X * scale + offsetX; - p.Y += config.TabletFullArea.Y * scale + offsetY; - polygonTabletFullArea.Points[i] = p; - } + config.Write(configFilename); + SendSettingsToDriver(); + // + // Enable/Disable Windows Ink pressure settings + // + if (config.Mode == Configuration.OutputModes.WindowsInk) + groupBoxWindowsInkSettings.IsEnabled = true; + else + groupBoxWindowsInkSettings.IsEnabled = false; - // - // Tablet area - // - corners = config.TabletArea.Corners; - for (int i = 0; i < 4; i++) + SetStatus("Settings saved!"); + } + catch (Exception) { - Point p = corners[i]; - p.X *= scale; - p.Y *= scale; - p.X += config.TabletArea.X * scale + offsetX; - p.Y += config.TabletArea.Y * scale + offsetY; - polygonTabletArea.Points[i] = p; + string dir = Directory.GetCurrentDirectory(); + MessageBox.Show("Error occured while saving the configuration.\n" + + "Make sure that it is possible to create and edit files in the '" + dir + "' directory.\n", + "ERROR!", MessageBoxButton.OK, MessageBoxImage.Error + ); } - - // - // Tablet area arrow - // - polygonTabletAreaArrow.Points[0] = new Point( - offsetX + config.TabletArea.X * scale, - offsetY + config.TabletArea.Y * scale - ); - - polygonTabletAreaArrow.Points[1] = new Point( - offsetX + corners[2].X * scale + config.TabletArea.X * scale, - offsetY + corners[2].Y * scale + config.TabletArea.Y * scale - ); - - polygonTabletAreaArrow.Points[2] = new Point( - offsetX + corners[3].X * scale + config.TabletArea.X * scale, - offsetY + corners[3].Y * scale + config.TabletArea.Y * scale - ); - - - // - // Tablet area aspect ratio text - // - 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); - - } // - // Canvas mouse events - // + // Set run at startup // - // Canvas mouse down - private void Canvas_MouseDown(object sender, MouseButtonEventArgs e) + private void SetRunAtStartup(bool enabled) { - - mouseDrag.IsMouseDown = true; - mouseDrag.Source = (UIElement)sender; - mouseDrag.OriginMouse = e.GetPosition((UIElement)mouseDrag.Source); - - // Screen Map - if (mouseDrag.Source == canvasScreenMap) + try { - // Reset monitor selection - comboBoxMonitor.SelectedIndex = -1; - - mouseDrag.OriginDraggable = new Point(config.ScreenArea.X, config.ScreenArea.Y); - canvasScreenMap.CaptureMouse(); - } + string path = System.Reflection.Assembly.GetExecutingAssembly().Location; + string entryName = "TabletDriverGUI"; + RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); + if (enabled) + rk.SetValue(entryName, "\"" + path + "\" --hide"); + else + rk.DeleteValue(entryName, false); - // Tablet Area - else if (mouseDrag.Source == canvasTabletArea) - { - mouseDrag.OriginDraggable = new Point(config.TabletArea.X, config.TabletArea.Y); - canvasTabletArea.CaptureMouse(); + rk.Close(); } - } - - - // - // Canvas mouse up - // - private void Canvas_MouseUp(object sender, MouseButtonEventArgs e) - { - mouseDrag.IsMouseDown = false; - LoadSettingsFromConfiguration(); - isLoadingSettings = true; - 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(); - } - - - // - // Canvas mouse move - // - private void Canvas_MouseMove(object sender, MouseEventArgs e) - { - Point position; - double dx, dy; - double scaleX = 0, scaleY = 0, scale = 0; - - // Canvas mouse drag - if (mouseDrag.IsMouseDown && mouseDrag.Source == sender) + catch (Exception) { - position = e.GetPosition((UIElement)mouseDrag.Source); - - dx = position.X - mouseDrag.OriginMouse.X; - dy = position.Y - mouseDrag.OriginMouse.Y; - - if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) - dx = 0; - if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) - dy = 0; - - - // Screen map canvas - if (mouseDrag.Source == canvasScreenMap) - { - scaleX = config.DesktopSize.Width / canvasScreenMap.ActualWidth; - scaleY = config.DesktopSize.Height / canvasScreenMap.ActualHeight; - scale = scaleY; - if (scaleX > scaleY) - scale = scaleX; - - if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) - { - dx = Math.Round(dx * scale / 80.0) * 80.0 / scale; - dy = Math.Round(dy * scale / 80.0) * 80.0 / scale; - } - - config.ScreenArea.X = mouseDrag.OriginDraggable.X + dx * scale; - config.ScreenArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; - UpdateScreenMapCanvas(); - } - - // Tablet area canvas - else if (mouseDrag.Source == canvasTabletArea) - { - scaleX = config.TabletFullArea.Width / canvasTabletArea.ActualWidth; - scaleY = config.TabletFullArea.Height / canvasTabletArea.ActualHeight; - scale = scaleY; - if (scaleX > scaleY) - scale = scaleX; - - if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) - { - dx = Math.Round(dx * scale / 5.0) * 5.0 / scale; - dy = Math.Round(dy * scale / 5.0) * 5.0 / scale; - } - - config.TabletArea.X = mouseDrag.OriginDraggable.X + dx * scale; - config.TabletArea.Y = mouseDrag.OriginDraggable.Y + dy * scale; - - UpdateTabletAreaCanvas(); - } - - } } - #endregion - - // // TextBox setting changed @@ -1114,28 +1024,12 @@ private void CheckboxChanged(object sender, RoutedEventArgs e) { if (isLoadingSettings) return; - // Disable tablet area settings when full area is forced - if (checkBoxForceFullArea.IsChecked == true) - { - textTabletAreaWidth.IsEnabled = false; + // Disable tablet area height when aspect ratio is forced + if (checkBoxForceAspect.IsChecked == true) textTabletAreaHeight.IsEnabled = false; - textTabletAreaX.IsEnabled = false; - textTabletAreaY.IsEnabled = false; - } else - { - textTabletAreaWidth.IsEnabled = true; - textTabletAreaX.IsEnabled = true; - textTabletAreaY.IsEnabled = true; - - // Disable tablet area height when aspect ratio is forced - if (checkBoxForceAspect.IsChecked == true) - textTabletAreaHeight.IsEnabled = false; - else - textTabletAreaHeight.IsEnabled = true; - + textTabletAreaHeight.IsEnabled = true; - } // Disable desktop size settings when automatic is checked if (checkBoxAutomaticDesktopSize.IsChecked == true) @@ -1166,11 +1060,6 @@ private void CheckboxChanged(object sender, RoutedEventArgs e) UpdateSettingsToConfiguration(); - if (sender == checkBoxForceFullArea) - { - LoadSettingsFromConfiguration(); - UpdateSettingsToConfiguration(); - } } @@ -1223,21 +1112,6 @@ private void ComboBoxMonitor_MouseDown(object sender, MouseButtonEventArgs e) comboBoxMonitor.Items.Add(System.Windows.Forms.Screen.PrimaryScreen.DeviceName); } - // TabletDriverGUI window - comboBoxMonitor.Items.Add("This window"); - - // osu!.exe processes window - try - { - if (Process.GetProcessesByName("osu!").Length > 0) - { - comboBoxMonitor.Items.Add("osu! window"); - } - } - catch (Exception) - { - } - } @@ -1246,14 +1120,11 @@ private void ComboBoxMonitor_MouseDown(object sender, MouseButtonEventArgs e) // private void ComboBoxMonitor_SelectionChanged(object sender, SelectionChangedEventArgs e) { + if (sender != comboBoxMonitor) return; + if (e.AddedItems.Count <= 0) return; + System.Windows.Forms.Screen[] screens = GetAvailableScreens(); - double minX = 99999; - double minY = 99999; - foreach (System.Windows.Forms.Screen screen in screens) - { - if (screen.Bounds.X < minX) minX = screen.Bounds.X; - if (screen.Bounds.Y < minY) minY = screen.Bounds.Y; - } + Vector minimumScreenPosition = GetMinimumScreenPosition(screens); int index = comboBoxMonitor.SelectedIndex; if (index == 0) @@ -1268,48 +1139,17 @@ private void ComboBoxMonitor_SelectionChanged(object sender, SelectionChangedEve index--; // Monitors - if (index >= 0 && index <= screens.Length && screens.Length > 1) + if (index >= 0 && index < screens.Length && screens.Length > 1) { - textScreenAreaX.Text = Utils.GetNumberString(screens[index].Bounds.X - minX); - textScreenAreaY.Text = Utils.GetNumberString(screens[index].Bounds.Y - minY); + textScreenAreaX.Text = Utils.GetNumberString(screens[index].Bounds.X - minimumScreenPosition.X); + textScreenAreaY.Text = Utils.GetNumberString(screens[index].Bounds.Y - minimumScreenPosition.Y); textScreenAreaWidth.Text = Utils.GetNumberString(screens[index].Bounds.Width); textScreenAreaHeight.Text = Utils.GetNumberString(screens[index].Bounds.Height); } - // TabletDriverGUI window - else if (comboBoxMonitor.SelectedValue.ToString() == "This window") - { - textScreenAreaX.Text = Utils.GetNumberString(Application.Current.MainWindow.Left - minX); - textScreenAreaY.Text = Utils.GetNumberString(Application.Current.MainWindow.Top - minY); - textScreenAreaWidth.Text = Utils.GetNumberString(Application.Current.MainWindow.Width); - textScreenAreaHeight.Text = Utils.GetNumberString(Application.Current.MainWindow.Height); - } - - // osu! window - else if (comboBoxMonitor.SelectedValue.ToString() == "osu! window") - { - try - { - Process[] processes = Process.GetProcessesByName("osu!"); - - if (processes.Length > 0) - { - IntPtr osuHandle = processes[0].MainWindowHandle; - NativeMethods.GetWindowRect(osuHandle, out NativeMethods.RECT rect); - - textScreenAreaX.Text = Utils.GetNumberString(rect.X - minX); - textScreenAreaY.Text = Utils.GetNumberString(rect.Y - minY); - textScreenAreaWidth.Text = Utils.GetNumberString(rect.Width); - textScreenAreaHeight.Text = Utils.GetNumberString(rect.Height - 1); - - } - } - catch (Exception) - { - - } - } } + comboBoxMonitor.Text = ""; + comboBoxMonitor.SelectedIndex = -1; UpdateSettingsToConfiguration(); } @@ -1329,11 +1169,11 @@ private void ButtonMap_Click(object sender, RoutedEventArgs e) else if (sender == buttonPenButton3) isPenButton = true; - ButtonMapping buttonMapping = new ButtonMapping(button, isPenButton); - buttonMapping.ShowDialog(); - if (buttonMapping.DialogResult == true) + WindowButtonMapping windowButtonMapping = new WindowButtonMapping(button, isPenButton); + windowButtonMapping.ShowDialog(); + if (windowButtonMapping.DialogResult == true) { - button.Content = buttonMapping.Result.ToUpper(); + button.Content = windowButtonMapping.Result.ToUpper(); UpdateSettingsToConfiguration(); } @@ -1363,15 +1203,24 @@ private void ButtonMap_ToolTipOpening(object sender, ToolTipEventArgs e) private void MainMenuClick(object sender, RoutedEventArgs e) { + // + // Save settings + // + if (sender == mainMenuSaveSettings) + { + SaveSettings(sender, e); + } + // // Import settings // - if (sender == mainMenuImport) + else if (sender == mainMenuImport) { OpenFileDialog dialog = new OpenFileDialog { InitialDirectory = Directory.GetCurrentDirectory(), - Filter = "XML File|*.xml" + Filter = "XML File|*.xml", + Title = "Import settings" }; if (dialog.ShowDialog() == true) { @@ -1379,8 +1228,14 @@ private void MainMenuClick(object sender, RoutedEventArgs e) { Configuration tmpConfig = Configuration.CreateFromFile(dialog.FileName); config = tmpConfig; - LoadSettingsFromConfiguration(); + InitializeConfiguration(); + Application.Current.Dispatcher.Invoke(() => + { + LoadSettingsFromConfiguration(); + UpdateSettingsToConfiguration(); + }); SetStatus("Settings imported!"); + } catch (Exception) { @@ -1400,7 +1255,8 @@ private void MainMenuClick(object sender, RoutedEventArgs e) InitialDirectory = Directory.GetCurrentDirectory(), AddExtension = true, DefaultExt = "xml", - Filter = "XML File|*.xml" + Filter = "XML File|*.xml", + Title = "Export settings" }; if (dialog.ShowDialog() == true) @@ -1425,16 +1281,24 @@ private void MainMenuClick(object sender, RoutedEventArgs e) // else if (sender == mainMenuResetToDefault) { - config = null; - isFirstStart = true; - config = new Configuration(); + WindowMessageBox messageBox = new WindowMessageBox( + "Are you sure?", "Reset to default settings?", + "Yes", "No"); + messageBox.ShowDialog(); + if (messageBox.DialogResult == true) + { - // Initialize configuration - InitializeConfiguration(); + config = null; + isFirstStart = true; + config = new Configuration(); - // Restart driver - StopDriver(); - StartDriver(); + // Initialize configuration + InitializeConfiguration(); + + // Restart driver + StopDriver(); + StartDriver(); + } } @@ -1446,6 +1310,143 @@ private void MainMenuClick(object sender, RoutedEventArgs e) Close(); } + + + // + // Open current directory + // + else if (sender == mainMenuOpenCurrentDirectory) + { + try + { + Process.Start("explorer.exe", "."); + } + catch (Exception) + { + MessageBox.Show("Couldn't open TabletDriver folder!", "Error!", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + + // + // Open config folder + // + else if (sender == mainMenuOpenConfig) + { + try + { + Process.Start("explorer.exe", "config"); + } + catch (Exception) + { + MessageBox.Show("Couldn't open config folder!", "Error!", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + // + // Open tools folder + // + else if (sender == mainMenuOpenTools) + { + try + { + Process.Start("explorer.exe", "tools"); + } + catch (Exception) + { + MessageBox.Show("Couldn't open tools folder!", "Error!", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + + // + // Tablet View + // + else if (sender == mainMenuTabletView) + { + if (tabletView != null) + tabletView.Close(); + + tabletView = new WindowTabletView(config, driver) + { + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Owner = Application.Current.MainWindow, + }; + tabletView.ShowDialog(); + tabletView.Close(); + tabletView = null; + GC.Collect(10, GCCollectionMode.Forced); + } + + + // + // Tablet View Settings + // + else if (sender == mainMenuTabletViewSettings) + { + + WindowTabletViewSettings tabletViewSettings = new WindowTabletViewSettings(config) + { + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Owner = Application.Current.MainWindow, + }; + tabletViewSettings.ShowDialog(); + } + + + // + // Fit window to content + // + else if (sender == mainMenuFitToContent) + { + SizeToContent = SizeToContent.WidthAndHeight; + UpdateLayout(); + SizeToContent = SizeToContent.Manual; + } + + + // + // Update desktop image + // + else if (sender == mainMenuUpdateDesktopImage) + { + DispatcherTimer timer = new DispatcherTimer + { + Interval = new TimeSpan(0, 0, 0, 0, 200) + }; + timer.Tick += (s, ev) => + { + imageDesktopScreenshot.Source = null; + GC.Collect(10, GCCollectionMode.Forced); + UpdateDesktopImage(); + timer.Stop(); + }; + timer.Start(); + } + + + + } + + + // + // Tab selection changed + // + private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + + if (tabControl.SelectedItem == tabArea) + { + UpdateCanvasElements(); + } + + // + // Console tab is selected + // + if (tabControl.SelectedItem == tabConsole) + { + ConsoleBufferToText(); + } } } diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml index 9dd10bb..8e790ec 100644 --- a/TabletDriverGUI/MainWindow.xaml +++ b/TabletDriverGUI/MainWindow.xaml @@ -1,14 +1,20 @@  + + + + + + + + + + + + + @@ -39,31 +70,46 @@ - - + + + + + + + + + + + + + + + - + - + @@ -87,7 +133,13 @@ - Alt + Drag = Move area 80 pixels at a time. - - Use the "Set Area" to set the screen area to single monitor or full desktop. + - Mouse scroll = Resize area 10 pixels per scroll. + + - Control + Mouse scroll = Resize area 100 pixels per scroll. + + - Use the "Move Area To" to set move screen area to single monitor or full desktop. + + - Right-clicking the area will show more options @@ -96,23 +148,46 @@ + MouseUp="Canvas_MouseUp"> + + + + + + + + + + + + + + + + + - 1920 + @@ -120,7 +195,7 @@ - 1080 + @@ -128,7 +203,7 @@ - 0 + @@ -136,13 +211,13 @@ - 0 + - - + + - Alt + Drag = Move area 5 mm at a time. - + + - Mouse scroll = Resize area 1 mm per scroll. + + - Control + Mouse scroll = Resize area 10 mm per scroll. + - 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. + + - Draw Area will allow you to set the tablet area by clicking two points with a pen. + + - Right-clicking the area will show more options. @@ -184,13 +267,14 @@ - - - + + + + - + @@ -251,49 +335,69 @@ - - - - + MouseUp="Canvas_MouseUp"> + + + + + + + + + + + + + + + - - - - + + + + + + Absolute (Pen) + Relative (Mouse) + + + + + Mode - Absolute: Use this with osu! - - Relative: Mouse mode. Sensitivity is determined by your tablet area size. + Standard: Gaming and other programs that don't need pen pressure. - Windows Ink: Pen pressure is enabled in this mode. Check if your drawing application supports Windows Ink. + Windows Ink: Enables pen pressure for applications that support Windows Ink. - SendInput: This is for debugging and testing purposes. Use this if you have problems with the absolute mode. + Compatibility: Use this mode if you have problems with cursor movement in standard mode. - - - - + @@ -301,13 +405,22 @@ + + + - 80 + @@ -315,7 +428,7 @@ - 45 + @@ -323,7 +436,7 @@ - 0 + @@ -331,21 +444,15 @@ - 0 + - - + + - - Full area - - + @@ -475,6 +582,10 @@ - Use negative number in sensitivity to invert the direction. - Acceleration 1.0 = No acceleration. Recommended range is from 1 to 5. + + - Stop cursor: cursor will be stopped when scrolling. + + - Drag scroll: scrolls only when the pen tip is pressed down. @@ -504,6 +615,16 @@ Stop cursor + + + Drag scroll + + + @@ -541,34 +662,78 @@ - - - - - - - - + + + + + + + + + + + + + + - + - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -688,7 +853,7 @@ - + @@ -738,7 +903,9 @@ - + 0 @@ -746,7 +913,9 @@ - + 1000 Hz 500 Hz @@ -759,10 +928,11 @@ + IsEnabled="{Binding ElementName=checkBoxSmoothing, Path=IsChecked}" + VerticalAlignment="Center" + VerticalContentAlignment="Center" + Margin="5,10,5,0" + Checked="CheckboxChanged" Unchecked="CheckboxChanged"> Only when buttons down @@ -826,9 +996,9 @@ - + + - + 10 - + 1.0 - + + + + + @@ -877,18 +1058,14 @@ Check the checkbox to enable the filter. - - Shape - + Shape Shape value is the how the filter handles pen acceleration. Start with a neutral 0.5 shape value and try to keep it around that. Use a lower value when there are are too much artifacts in sharp corners and increase the value if corners are too smooth. - - Compensation - + Compensation Compensation value is the amount of latency compensation. @@ -901,90 +1078,114 @@ Try to find a balance between response time and artifacting. - - Only when hovering - + Only when hovering This feature is mainly meant for Wacom CTL-490, but might be useful with other tablets too. - - Recommended value ranges - + Recommended value ranges - Shape: 0.1 - 0.9 + Shape: 0.1 - 2.0 Compensation: 10 - 30 ms - - - Example values: - - - - Gaomon S56K: - Shape 0.7, Compensation = 15 - 20 ms - - Huion 420 & H420: - Shape = 0.5, Compensation = 15 - 20 ms - - Huion H430P & H640P: - Shape = 0.7, Compensation = 20 - 30 ms - - XP-PEN G430 & G640: - Shape = 0.5, Compensation = 10 - 20 ms, Invalid low speed data, use noise reduction 10/0.5 mm - - Wacom CTL-490 Hover: - Shape = 0.5, Compensation = 10 - 20 ms, Enable "Only when hovering" - - VEIKK S640: - - - Shape = 0.5, Compensation = 10 - 20 ms, Filter will automatically adjust the compensation level. - - The tablet does have a 166 RPS mode with higher smoothing latency. - - - + - - - + - - - 0.3 - + + + Enabled + - - - - 20 - - - - - + - + Only when hovering - - + + - + + + + + + + 1.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -992,7 +1193,7 @@ - + @@ -1046,7 +1247,7 @@ - + @@ -1092,7 +1293,7 @@ - + @@ -1117,25 +1318,24 @@ - - - + + + - - - - - + + + + - - - + + + - - - + + + - + diff --git a/TabletDriverGUI/MainWindow.xaml.cs b/TabletDriverGUI/MainWindow.xaml.cs index 4dcc0b8..81d9f51 100644 --- a/TabletDriverGUI/MainWindow.xaml.cs +++ b/TabletDriverGUI/MainWindow.xaml.cs @@ -1,11 +1,8 @@ -using Microsoft.Win32; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; @@ -13,7 +10,7 @@ using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; -using System.Windows.Shapes; +using System.Windows.Media.Animation; using System.Windows.Threading; namespace TabletDriverGUI @@ -25,7 +22,7 @@ public partial class MainWindow : Window { // Version - public string Version = "0.2.3"; + public string Version = "0.2.4"; // Console stuff private List commandHistory; @@ -46,6 +43,7 @@ public partial class MainWindow : Window private DispatcherTimer timerStatusbar; private DispatcherTimer timerRestart; private DispatcherTimer timerConsoleUpdate; + private DispatcherTimer timerUpdatePenPositions; // Config private Configuration config; @@ -53,46 +51,9 @@ public partial class MainWindow : Window private bool isFirstStart = false; private bool isLoadingSettings; - // Screen map canvas elements - private Rectangle[] rectangleMonitors; - private Rectangle rectangleDesktop; - private Rectangle rectangleScreenMap; - private TextBlock textScreenAspectRatio; - - // Tablet area canvas elements - private Polygon polygonTabletFullArea; - private Polygon polygonTabletArea; - private Polygon polygonTabletAreaArrow; - private TextBlock textTabletAspectRatio; - - // Mouse drag - private class MouseDrag - { - public bool IsMouseDown; - public object Source; - public Point OriginMouse; - public Point OriginDraggable; - public MouseDrag() - { - IsMouseDown = false; - Source = null; - OriginMouse = new Point(0, 0); - OriginDraggable = new Point(0, 0); - } - } - MouseDrag mouseDrag; - // Measurement to area private bool isEnabledMeasurementToArea = false; - // Ink canvas undo history - StrokeCollection inkCanvasUndoHistory; - - // Ink canvas DrawingAttributes - DrawingAttributes inkCanvasDrawingAttributes; - - - // // Constructor // @@ -187,51 +148,6 @@ public MainWindow() // groupBoxTabletButtons.Visibility = Visibility.Collapsed; - // - // Create tablet button map WrapPanel items - // - for (int i = 0; i < 16; i++) - { - GroupBox groupBox = new GroupBox - { - Width = 90, - Header = "Button " + (i + 1).ToString() - }; - Button button = new Button - { - Height = 22, - Content = "", - Padding = new Thickness(2, 0, 2, 0), - ToolTip = "Empty", - Background = Brushes.White - }; - button.Click += ButtonMap_Click; - button.ToolTipOpening += ButtonMap_ToolTipOpening; - - groupBox.Content = button; - wrapPanelTabletButtons.Children.Add(groupBox); - } - CheckBox checkBox = new CheckBox - { - Content = "Disable buttons" - }; - checkBox.Checked += CheckboxChanged; - checkBox.Unchecked += CheckboxChanged; - checkBox.VerticalAlignment = VerticalAlignment.Bottom; - checkBox.Margin = new Thickness(5, 5, 5, 10); - wrapPanelTabletButtons.Children.Add(checkBox); - - - // - // Smoothing rate ComboBox - // - comboBoxSmoothingRate.Items.Clear(); - for (int i = 1; i <= 8; i++) - { - comboBoxSmoothingRate.Items.Add((1000.0 / i).ToString("0") + " Hz"); - } - comboBoxSmoothingRate.SelectedIndex = 3; - // Ink canvas undo history inkCanvasUndoHistory = new StrokeCollection(); @@ -242,7 +158,8 @@ public MainWindow() Width = 10, Height = 10, Color = Color.FromRgb(0x55, 0x55, 0x55), - StylusTip = StylusTip.Ellipse + StylusTip = StylusTip.Ellipse, + FitToCurve = false }; inkCanvas.DefaultDrawingAttributes = inkCanvasDrawingAttributes; @@ -254,10 +171,6 @@ public MainWindow() Loaded += MainWindow_Loaded; SizeChanged += MainWindow_SizeChanged; - // - // Allow input field events - // - isLoadingSettings = false; } @@ -266,14 +179,18 @@ public MainWindow() // Window is closing -> Stop driver private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + // Close tablet view + if (tabletView != null) + tabletView.Close(); + + // Hide notify icon notifyIcon.Visible = false; - try - { - config.Write(configFilename); - } - catch (Exception) - { - } + + // Write configuration to XML file + try { config.Write(configFilename); } + catch (Exception) { } + + // Stop driver StopDriver(); } @@ -291,11 +208,17 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) } catch (Exception) { - driver.ConsoleAddText("New config created!"); + driver.ConsoleAddLine("New config created!"); isFirstStart = true; config = new Configuration(); } + // Create setting elements + CreateSettingElements(); + + // Create canvas elements + CreateCanvasElements(); + // Initialize configuration InitializeConfiguration(); @@ -306,8 +229,14 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) Hide(); } + // + // Allow input field events + // + isLoadingSettings = false; + // Start the driver StartDriver(); + } @@ -318,12 +247,26 @@ private void Window_PreviewKeyDown(object sender, KeyEventArgs e) { // Control + S -> Save settings - if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control) + if (e.KeyboardDevice.Modifiers == ModifierKeys.Control && e.Key == Key.S) { SaveSettings(sender, null); e.Handled = true; } + // Control + I -> Import settings + if (e.KeyboardDevice.Modifiers == ModifierKeys.Control && e.Key == Key.I) + { + MainMenuClick(mainMenuImport, null); + e.Handled = true; + } + + // Control + E -> Export settings + if (e.KeyboardDevice.Modifiers == ModifierKeys.Control && e.Key == Key.E) + { + MainMenuClick(mainMenuExport, null); + e.Handled = true; + } + } @@ -479,70 +422,6 @@ private void TimerStatusbar_Tick(object sender, EventArgs e) - #region Wacom / Draw area - - // - // Wacom Area - // - private void ButtonWacomArea_Click(object sender, RoutedEventArgs e) - { - WacomArea wacom = new WacomArea(); - 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 = 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(); - - // Set button clicked - if (wacom.DialogResult == true) - { - if ( - 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; - width = right - left; - height = bottom - top; - config.ForceAspectRatio = false; - config.ForceFullArea = false; - config.TabletArea.X = (left + width / 2.0) / 100.0; - config.TabletArea.Y = (top + height / 2.0) / 100.0; - config.TabletArea.Width = width / 100.0; - config.TabletArea.Height = height / 100.0; - LoadSettingsFromConfiguration(); - } - else - { - MessageBox.Show("Invalid values!", "Wacom area error!", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - wacom.Close(); - } - - - // - // Draw area - // - private void ButtonDrawArea_Click(object sender, RoutedEventArgs e) - { - if (!isEnabledMeasurementToArea) - { - isEnabledMeasurementToArea = true; - driver.SendCommand("Measure 2"); - SetStatus("Click the top left and the bottom right corners of the area with your tablet pen!"); - buttonDrawArea.IsEnabled = false; - } - } - - #endregion - - #region WndProc @@ -579,12 +458,17 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b } + #endregion + private void MouseTest(object sender, MouseButtonEventArgs e) + { + SetStatus("Event: " + e.RoutedEvent.ToString() + ", Mouse at " + ((UIElement)sender).ToString() + "! " + e.ChangedButton.ToString() + " " + e.ButtonState.ToString()); + } - - - #endregion - + private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) + { + MessageBox.Show(e.Command.ToString()); + } } } \ No newline at end of file diff --git a/TabletDriverGUI/MultimediaTimer.cs b/TabletDriverGUI/MultimediaTimer.cs new file mode 100644 index 0000000..a1af83b --- /dev/null +++ b/TabletDriverGUI/MultimediaTimer.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace TabletDriverGUI +{ + + public class MultimediaTimer : IDisposable + { + private bool disposed = false; + private int interval, resolution; + private UInt32 timerId; + + // Hold the timer callback to prevent garbage collection. + private readonly NativeMethods.MultimediaTimerCallback Callback; + + public MultimediaTimer() + { + Callback = new NativeMethods.MultimediaTimerCallback(TimerCallbackMethod); + Resolution = 5; + Interval = 10; + } + + ~MultimediaTimer() + { + Dispose(false); + } + + public int Interval + { + get + { + return interval; + } + set + { + CheckDisposed(); + + if (value < 0) + throw new ArgumentOutOfRangeException("value"); + + interval = value; + if (Resolution > Interval) + Resolution = value; + } + } + + // Note minimum resolution is 0, meaning highest possible resolution. + public int Resolution + { + get + { + return resolution; + } + set + { + CheckDisposed(); + + if (value < 0) + throw new ArgumentOutOfRangeException("value"); + + resolution = value; + } + } + + public bool IsRunning + { + get { return timerId != 0; } + } + + public void Start() + { + CheckDisposed(); + + if (IsRunning) + throw new InvalidOperationException("Timer is already running"); + + // Event type = 0, one off event + // Event type = 1, periodic event + UInt32 userCtx = 0; + timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1); + if (timerId == 0) + { + int error = Marshal.GetLastWin32Error(); + throw new Win32Exception(error); + } + } + + public void Stop() + { + CheckDisposed(); + + if (!IsRunning) + throw new InvalidOperationException("Timer has not been started"); + + StopInternal(); + } + + private void StopInternal() + { + NativeMethods.TimeKillEvent(timerId); + timerId = 0; + } + + public event EventHandler Tick; + + public void Dispose() + { + Dispose(true); + } + + private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2) + { + Tick?.Invoke(this, EventArgs.Empty); + } + + private void CheckDisposed() + { + if (disposed) + throw new ObjectDisposedException("MultimediaTimer"); + } + + private void Dispose(bool disposing) + { + if (disposed) + return; + + disposed = true; + if (IsRunning) + { + StopInternal(); + } + + if (disposing) + { + Tick = null; + GC.SuppressFinalize(this); + } + } + } +} diff --git a/TabletDriverGUI/NativeMethods.cs b/TabletDriverGUI/NativeMethods.cs index 0223194..5a0ce3b 100644 --- a/TabletDriverGUI/NativeMethods.cs +++ b/TabletDriverGUI/NativeMethods.cs @@ -56,5 +56,22 @@ public int Width [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); + + // + // Multimedia timer + // + internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")] + internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")] + internal static extern void TimeKillEvent(UInt32 uTimerId); + + // + // GDI + // + [DllImport("gdi32.dll")] + public static extern bool DeleteObject(IntPtr hObject); } } diff --git a/TabletDriverGUI/TabletDriver.cs b/TabletDriverGUI/TabletDriver.cs index 2d90888..1eb55ff 100644 --- a/TabletDriverGUI/TabletDriver.cs +++ b/TabletDriverGUI/TabletDriver.cs @@ -4,6 +4,10 @@ using System.Diagnostics; using System.IO; using System.Timers; +using System.IO.Pipes; +using System.Windows; +using System.Text; +using System.Runtime.InteropServices; namespace TabletDriverGUI { @@ -41,8 +45,40 @@ public DriverEventArgs(DriverEventType type, string message, string parameters) public bool HasConsoleUpdated; private readonly int ConsoleMaxLines; private System.Threading.Mutex mutexConsoleUpdate; - private Dictionary commands; - public Dictionary Commands { get { return commands; } } + private Dictionary commands; + public Dictionary Commands { get { return commands; } } + + // Pipe + System.Threading.Thread pipeInputThread = null; + System.Threading.Thread pipeStateThread = null; + + NamedPipeClientStream pipeStreamInput; + NamedPipeClientStream pipeStreamOutput; + NamedPipeClientStream pipeStreamState; + + StreamReader pipeInputReader = null; + StreamWriter pipeOutputWriter = null; + BinaryReader pipeStateReader = null; + + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] + [Serializable] + public struct TabletState + { + public int index; + + public int inputButtons; + public double inputX; + public double inputY; + public double inputPressure; + public double inputVelocity; + + public int outputButtons; + public double outputX; + public double outputY; + public double outputPressure; + } + public TabletState tabletState; + // Other variables private readonly string servicePath; @@ -67,7 +103,7 @@ public TabletDriver(string servicePath) commands = new Dictionary(); - + } // @@ -85,14 +121,17 @@ private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e) } processService.Refresh(); - switch(processService.PriorityClass) + if (processService != null && !processService.HasExited) { - case ProcessPriorityClass.High: - case ProcessPriorityClass.RealTime: - break; - default: - RaiseError("TabletDriverService priority too low! Run the GUI as an administrator or change the priority to high!"); - break; + switch (processService.PriorityClass) + { + case ProcessPriorityClass.High: + case ProcessPriorityClass.RealTime: + break; + default: + RaiseError("TabletDriverService priority too low! Run the GUI as an administrator or change the priority to high!"); + break; + } } } } @@ -103,13 +142,30 @@ private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e) public void SendCommand(string line) { if (running) + { processService.StandardInput.WriteLine(line); + } + } + + // + // Send command to the driver service through a named pipe + // + public void SendPipeCommand(string line) + { + if (running) + { + if (pipeOutputWriter != null) + { + pipeOutputWriter.WriteLine(line); + pipeOutputWriter.Flush(); + } + } } // // Add text to console // - public void ConsoleAddText(string line) + public void ConsoleAddLine(string line) { mutexConsoleUpdate.WaitOne(); ConsoleBuffer.Add(line); @@ -152,7 +208,7 @@ public string CompleteCommandName(string inputText, bool showCommands) commandsFound.Add(item.Value); } } - + // Only one command found if (commandsFound.Count == 1) @@ -180,7 +236,7 @@ public string CompleteCommandName(string inputText, bool showCommands) int columns = (int)Math.Ceiling(100.0 / maxWidth); int rows = (int)Math.Ceiling((double)commandsFound.Count / columns); - string[,] commandMatrix = new string[rows,columns]; + string[,] commandMatrix = new string[rows, columns]; int row = 0; int column = 0; @@ -203,9 +259,10 @@ public string CompleteCommandName(string inputText, bool showCommands) } } - for(row = 0; row < rows; row++) { - if(row != 0) - commandsString += "\r\n | "; + for (row = 0; row < rows; row++) + { + if (row != 0) + commandsString += "\r\n | "; for (column = 0; column < columns; column++) { string commandString = commandMatrix[row, column]; @@ -219,9 +276,9 @@ public string CompleteCommandName(string inputText, bool showCommands) // Add commands to console output if (showCommands) { - ConsoleAddText(""); - ConsoleAddText("Commands: "); - ConsoleAddText(commandsString); + ConsoleAddLine(""); + ConsoleAddLine("Commands: "); + ConsoleAddLine(commandsString); } // Fill input text @@ -288,7 +345,7 @@ private void ProcessService_Exited(object sender, EventArgs e) // private void ProcessService_ErrorDataReceived(object sender, DataReceivedEventArgs e) { - ConsoleAddText("ERROR! " + e.Data); + ConsoleAddLine("ERROR! " + e.Data); ErrorReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Error, e.Data, "")); } @@ -307,7 +364,8 @@ private void ProcessService_OutputDataReceived(object sender, DataReceivedEventA string line = e.Data; // Status line? - if (line.Contains("[STATUS]")) { + if (line.Contains("[STATUS]")) + { // Parse status variable and value Match match = Regex.Match(line, "^.+\\[STATUS\\] ([^ ]+) (.*?)$"); @@ -341,11 +399,201 @@ private void ProcessService_OutputDataReceived(object sender, DataReceivedEventA } - ConsoleAddText(e.Data); + //ConsoleAddLine(e.Data); MessageReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Message, e.Data, "")); } } + + // + // Pipe input thread + // + private void RunPipeInputThread() + { + StringBuilder stringBuilder = new StringBuilder(); + char[] buffer = new char[1024]; + + + while (running) + { + pipeStreamInput = new NamedPipeClientStream(".", "TabletDriverOutput", PipeDirection.InOut); + pipeStreamOutput = new NamedPipeClientStream(".", "TabletDriverInput", PipeDirection.InOut); + + //ConsoleAddLine("Connecting..."); + try + { + pipeStreamInput.Connect(); + pipeStreamOutput.Connect(); + } + catch (Exception) + { + // ConsoleAddLine("Input pipe connect error! " + ex.Message); + break; + } + //ConsoleAddLine("Connected!"); + + pipeInputReader = new StreamReader(pipeStreamInput); + pipeOutputWriter = new StreamWriter(pipeStreamOutput); + + + while (running && pipeStreamInput.IsConnected) + { + //ConsoleAddLine("Read!"); + int bytesRead = 0; + try + { + bytesRead = pipeInputReader.Read(buffer, 0, buffer.Length); + } + catch (Exception) + { + // ConsoleAddLine("Can't read input pipe!"); + break; + } + if (bytesRead == 0) continue; + //ConsoleAddLine("Read: " + bytesRead); + + for (int i = 0; i < bytesRead; i++) + { + char c = buffer[i]; + if (c == '\n' || c == '\r' || c == 0) + { + if (stringBuilder.Length > 0) + { + ConsoleAddLine(stringBuilder.ToString()); + } + stringBuilder.Clear(); + } + else + { + stringBuilder.Append(c); + } + } + + } + + ConsoleAddLine("Input pipe disconnected!"); + + // Close input reader + try + { + pipeInputReader.Close(); + pipeInputReader.Dispose(); + pipeInputReader = null; + } + catch (Exception) { } + + // Close input stream + try + { + pipeStreamInput.Close(); + pipeStreamInput.Dispose(); + pipeStreamInput = null; + } + catch (Exception) { } + + + + // Close output writer + try + { + pipeOutputWriter.Close(); + pipeOutputWriter.Dispose(); + pipeOutputWriter = null; + } + catch (Exception) { } + + // Close output stream + try + { + pipeStreamOutput.Close(); + pipeStreamOutput.Dispose(); + pipeStreamOutput = null; + } + catch (Exception) { } + + } + + } + + + // + // Pipe state thread + // + private void RunPipeStateThread() + { + int size = 0; + byte[] bytes; + GCHandle gcHandle; + TabletState readState; + + while (running) + { + if (!running) break; + pipeStreamState = new NamedPipeClientStream(".", "TabletDriverState", PipeDirection.InOut); + + try + { + pipeStreamState.Connect(); + } + catch (Exception) + { + // ConsoleAddLine("State pipe connection error! " + ex.Message); + break; + } + pipeStateReader = new BinaryReader(pipeStreamState); + + + while (running && pipeStreamState.IsConnected) + { + try + { + + size = Marshal.SizeOf(typeof(TabletState)); + bytes = pipeStateReader.ReadBytes(size); + gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + readState = (TabletState)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(TabletState)); + gcHandle.Free(); + } + catch (Exception) + { + //ConsoleAddLine("Can't read state pipe! " + ex.Message); + size = 0; + break; + } + if (size > 0) + { + tabletState = readState; + //ConsoleAddLine("State X: " + tabletState.inputX + ", Y: " + tabletState.inputY); + } + + + } + + ConsoleAddLine("State pipe disconnected!"); + + // Close state reader + try + { + pipeStateReader.Close(); + pipeStateReader.Dispose(); + pipeStateReader = null; + } + catch (Exception) { } + + + // Close state stream + try + { + pipeStreamState.Close(); + pipeStreamState.Dispose(); + pipeStreamState = null; + } + catch (Exception) { } + + } + } + + // // Start the driver service // @@ -401,6 +649,15 @@ public void Start(string processPath, string arguments) running = true; timerWatchdog.Start(); + // Pipe input thread + pipeInputThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeInputThread)); + pipeInputThread.Start(); + + // Pipe state thread + pipeStateThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeStateThread)); + pipeStateThread.Start(); + + Started?.Invoke(this, new EventArgs()); } @@ -427,16 +684,113 @@ public void Stop() if (!running) return; running = false; timerWatchdog.Stop(); + + // Close pipe reader + try + { + if (pipeInputReader != null) + { + pipeInputReader.Close(); + pipeInputReader.Dispose(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe input reader error! " + e.Message); } + + // Close pipe input stream + try + { + if (pipeStreamInput != null) + { + pipeStreamInput.Close(); + pipeStreamInput.Dispose(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe input stream error! " + e.Message); } + + // Close pipe writer + try + { + if (pipeOutputWriter != null) + { + pipeOutputWriter.Close(); + pipeOutputWriter.Dispose(); + } + + } + catch (Exception e) { Debug.WriteLine("Pipe writer error! " + e.Message); } + + + // Close pipe output stream + try + { + if (pipeStreamOutput != null) + { + pipeStreamOutput.Close(); + pipeStreamOutput.Dispose(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe output stream error! " + e.Message); } + + + // Close state reader + try + { + if (pipeStateReader != null) + { + pipeStateReader.Close(); + pipeStateReader.Dispose(); + } + + } + catch (Exception e) { Debug.WriteLine("Pipe state reader error! " + e.Message); } + + // Close pipe state stream + try + { + if (pipeStreamState != null) + { + pipeStreamState.Close(); + pipeStreamState.Dispose(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe state stream error! " + e.Message); } + + // Close pipe input thread + try + { + if (pipeInputThread != null) + { + pipeInputThread.Abort(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe input thread error! " + e.Message); } + + // Close pipe state thread + try + { + if (pipeStateThread != null) + { + pipeStateThread.Abort(); + } + } + catch (Exception e) { Debug.WriteLine("Pipe state thread error! " + e.Message); } + + try { processService.CancelOutputRead(); processService.Kill(); processService.Dispose(); } - catch (Exception) + catch (Exception e) { + Debug.WriteLine("Service process error! " + e.Message); } Stopped?.Invoke(this, new EventArgs()); + + + System.Threading.Thread.Sleep(10); + } diff --git a/TabletDriverGUI/TabletDriverGUI.csproj b/TabletDriverGUI/TabletDriverGUI.csproj index b85d398..c28e1b5 100644 --- a/TabletDriverGUI/TabletDriverGUI.csproj +++ b/TabletDriverGUI/TabletDriverGUI.csproj @@ -77,8 +77,13 @@ MSBuild:Compile Designer - - ButtonMapping.xaml + + + + WindowTabletViewSettings.xaml + + + WindowButtonMapping.xaml @@ -88,10 +93,23 @@ - - WacomArea.xaml + + WindowMessageBox.xaml + + + WindowTabletView.xaml + + + WindowWacomArea.xaml + + + WindowAreaEditor.xaml - + + Designer + MSBuild:Compile + + Designer MSBuild:Compile @@ -109,7 +127,19 @@ MainWindow.xaml Code - + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + Designer MSBuild:Compile diff --git a/TabletDriverGUI/Utils.cs b/TabletDriverGUI/Utils.cs index 1481209..d8c8862 100644 --- a/TabletDriverGUI/Utils.cs +++ b/TabletDriverGUI/Utils.cs @@ -1,4 +1,6 @@ using System.Globalization; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; namespace TabletDriverGUI { @@ -56,5 +58,25 @@ public static string GetNumberString(double val, string format) CheckCultureInfo(); return val.ToString(format, cultureInfo.NumberFormat); } + + + + + // + // RGB to hex string + // + public static string RGBToHex(byte r, byte g, byte b) + { + return string.Format("{0:X2}{1:X2}{2:X2}", r, g, b); ; + } + + // + // RGB to color hex string + // + public static string RGBToHexColor(byte r, byte g, byte b) + { + return "#" + RGBToHex(r, g, b); + } + } } diff --git a/TabletDriverGUI/WindowAreaEditor.xaml b/TabletDriverGUI/WindowAreaEditor.xaml new file mode 100644 index 0000000..f63366b --- /dev/null +++ b/TabletDriverGUI/WindowAreaEditor.xaml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + 123 + + + + + + + + + + + + diff --git a/TabletDriverGUI/WindowAreaEditor.xaml.cs b/TabletDriverGUI/WindowAreaEditor.xaml.cs new file mode 100644 index 0000000..39cf97c --- /dev/null +++ b/TabletDriverGUI/WindowAreaEditor.xaml.cs @@ -0,0 +1,156 @@ +using System.Windows; +using System.Windows.Controls; + +namespace TabletDriverGUI +{ + /// + /// Interaction logic for WindowAreaEditor.xaml + /// + public partial class WindowAreaEditor : Window + { + private Area screenArea, tabletArea; + private Configuration config; + private bool isLoadingSettings; + + public WindowAreaEditor(Configuration configuration, Area ScreenArea, Area TabletArea) + { + WindowStartupLocation = WindowStartupLocation.CenterOwner; + Owner = Application.Current.MainWindow; + + InitializeComponent(); + screenArea = ScreenArea; + tabletArea = TabletArea; + config = configuration; + isLoadingSettings = false; + LoadValues(); + } + + // + // Save click + // + private void ButtonSave_Click(object sender, RoutedEventArgs e) + { + StoreValues(); + DialogResult = true; + Close(); + } + + // + // Cancel click + // + private void ButtonCancel_Click(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + + // + // Load values from configuration + // + private void LoadValues() + { + isLoadingSettings = true; + + // Screen area + textScreenAreaWidth.Text = Utils.GetNumberString(screenArea.Width); + textScreenAreaHeight.Text = Utils.GetNumberString(screenArea.Height); + textScreenAreaX.Text = Utils.GetNumberString(screenArea.X - screenArea.Width / 2.0); + textScreenAreaY.Text = Utils.GetNumberString(screenArea.Y - screenArea.Height / 2.0); + + // Tablet area + textTabletAreaWidth.Text = Utils.GetNumberString(tabletArea.Width); + textTabletAreaHeight.Text = Utils.GetNumberString(tabletArea.Height); + textTabletAreaX.Text = Utils.GetNumberString(tabletArea.X); + textTabletAreaY.Text = Utils.GetNumberString(tabletArea.Y); + + if(config.ForceAspectRatio) + { + textTabletAreaHeight.IsEnabled = false; + } else + { + textTabletAreaHeight.IsEnabled = true; + } + + + isLoadingSettings = false; + + + } + + // + // Store values + // + private void StoreValues() + { + + // Screen area + if (Utils.ParseNumber(textScreenAreaWidth.Text, out double value)) + screenArea.Width = value; + if (Utils.ParseNumber(textScreenAreaHeight.Text, out value)) + screenArea.Height = value; + if (Utils.ParseNumber(textScreenAreaX.Text, out value)) + screenArea.X = value + screenArea.Width / 2.0; + if (Utils.ParseNumber(textScreenAreaY.Text, out value)) + screenArea.Y = value + screenArea.Height / 2.0; + + // Tablet area + if (Utils.ParseNumber(textTabletAreaWidth.Text, out value)) + tabletArea.Width = value; + if (Utils.ParseNumber(textTabletAreaHeight.Text, out value)) + tabletArea.Height = value; + if (Utils.ParseNumber(textTabletAreaX.Text, out value)) + tabletArea.X = value; + if (Utils.ParseNumber(textTabletAreaY.Text, out value)) + tabletArea.Y = value; + + } + + // + // TextBox key up + // + private void OnTextBoxKeyUp(object sender, System.Windows.Input.KeyEventArgs e) + { + if(e.Key == System.Windows.Input.Key.Enter) + { + ButtonSave_Click(sender, null); + } + } + + // + // TextBox changed + // + private void OnTextBoxChanged(object sender, TextChangedEventArgs e) + { + if (!IsLoaded || isLoadingSettings) return; + + if (config.ForceAspectRatio) + { + double screenWidth, screenHeight; + double tabletWidth, tabletHeight; + double aspectRatio; + + screenWidth = screenArea.Width; + screenHeight = screenArea.Height; + tabletWidth = tabletArea.Width; + tabletHeight = tabletArea.Height; + + if (Utils.ParseNumber(textScreenAreaWidth.Text, out double value)) + screenWidth = value; + if (Utils.ParseNumber(textScreenAreaHeight.Text, out value)) + screenHeight = value; + + if (Utils.ParseNumber(textTabletAreaWidth.Text, out value)) + tabletWidth = value; + if (Utils.ParseNumber(textTabletAreaHeight.Text, out value)) + tabletHeight = value; + + aspectRatio = screenWidth / screenHeight; + tabletHeight = tabletWidth / aspectRatio; + textTabletAreaHeight.Text = Utils.GetNumberString(tabletHeight); + } + } + + + } +} diff --git a/TabletDriverGUI/ButtonMapping.xaml b/TabletDriverGUI/WindowButtonMapping.xaml similarity index 91% rename from TabletDriverGUI/ButtonMapping.xaml rename to TabletDriverGUI/WindowButtonMapping.xaml index 5be4473..5ca9fb3 100644 --- a/TabletDriverGUI/ButtonMapping.xaml +++ b/TabletDriverGUI/WindowButtonMapping.xaml @@ -1,4 +1,4 @@ -Scroll Up/Down Scroll Left/Right Scroll Both + Scroll One Up + Scroll One Down + Scroll One Left + Scroll One Right diff --git a/TabletDriverGUI/ButtonMapping.xaml.cs b/TabletDriverGUI/WindowButtonMapping.xaml.cs similarity index 57% rename from TabletDriverGUI/ButtonMapping.xaml.cs rename to TabletDriverGUI/WindowButtonMapping.xaml.cs index 82dc8cd..7a0ccbe 100644 --- a/TabletDriverGUI/ButtonMapping.xaml.cs +++ b/TabletDriverGUI/WindowButtonMapping.xaml.cs @@ -1,85 +1,135 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; 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 { /// /// Interaction logic for ButtonMapping.xaml /// - public partial class ButtonMapping : Window + public partial class WindowButtonMapping : Window { // WPF Button Button button; - public string Result; - public ButtonMapping() + public class MouseAction + { + public string Action; + public string Name; + public bool Visible; + + public MouseAction(string name) : this("", name) + { + } + public MouseAction(string action, string name) + { + Action = action; + Name = name; + Visible = true; + } + public override string ToString() + { + return Name; + } + } + + public OrderedDictionary mouseActions; + + public WindowButtonMapping() { WindowStartupLocation = WindowStartupLocation.CenterOwner; Owner = Application.Current.MainWindow; InitializeComponent(); Result = ""; + + + mouseActions = new OrderedDictionary() + { + { "", new MouseAction("None") }, + { "MOUSE1", new MouseAction("Mouse 1 (Left / Tip)") }, + { "MOUSE2", new MouseAction("Mouse 2 (Right / Barrel)") }, + { "MOUSE3", new MouseAction("Mouse 3 (Middle / Eraser)") }, + { "MOUSE4", new MouseAction("Mouse 4 (Back)") }, + { "MOUSE5", new MouseAction("Mouse 5 (Forward)") }, + { "MOUSESCROLLV", new MouseAction("Scroll Up/Down") }, + { "MOUSESCROLLH", new MouseAction("Scroll Left/Right") }, + { "MOUSESCROLLB", new MouseAction("Scroll Both") }, + { "SCROLLUP", new MouseAction("Scroll One Up") }, + { "SCROLLDOWN", new MouseAction("Scroll One Down") }, + { "SCROLLLEFT", new MouseAction("Scroll One Left") }, + { "SCROLLRIGHT", new MouseAction("Scroll One Right") } + }; + + UpdateMouseActions(); + } - public ButtonMapping(Button button, bool isPenButton) : this() + public WindowButtonMapping(Button button, bool isPenButton) : this() { this.button = button; // Tablet buttons don't need tip, barrel and eraser info if (!isPenButton) { - ((ComboBoxItem)comboBoxMouse.Items[1]).Content = "Mouse 1 (Left)"; - ((ComboBoxItem)comboBoxMouse.Items[2]).Content = "Mouse 2 (Right)"; - ((ComboBoxItem)comboBoxMouse.Items[3]).Content = "Mouse 3 (Middle)"; - - // Disable scroll - ((ComboBoxItem)comboBoxMouse.Items[6]).Visibility = Visibility.Collapsed; - ((ComboBoxItem)comboBoxMouse.Items[7]).Visibility = Visibility.Collapsed; - ((ComboBoxItem)comboBoxMouse.Items[8]).Visibility = Visibility.Collapsed; + ((MouseAction)mouseActions["MOUSE1"]).Name = "Mouse 1 (Left)"; + ((MouseAction)mouseActions["MOUSE2"]).Name = "Mouse 2 (Right)"; + ((MouseAction)mouseActions["MOUSE3"]).Name = "Mouse 3 (Middle)"; + + // Disable mouse scroll + ((MouseAction)mouseActions["MOUSESCROLLV"]).Visible = false; + ((MouseAction)mouseActions["MOUSESCROLLH"]).Visible = false; + ((MouseAction)mouseActions["MOUSESCROLLB"]).Visible = false; } + UpdateMouseActions(); - // Enable scroll - else + CheckKeyValue(); + } + + + // + // Update mouse actions and combobox + // + private void UpdateMouseActions() + { + comboBoxMouse.Items.Clear(); + foreach (DictionaryEntry entry in mouseActions) { - ((ComboBoxItem)comboBoxMouse.Items[6]).Visibility = Visibility.Visible; - ((ComboBoxItem)comboBoxMouse.Items[7]).Visibility = Visibility.Visible; - ((ComboBoxItem)comboBoxMouse.Items[8]).Visibility = Visibility.Visible; + MouseAction mouseAction = (MouseAction)entry.Value; + mouseAction.Action = (string)entry.Key; + if (mouseAction.Visible) + { + comboBoxMouse.Items.Add(mouseAction); + } } - CheckButtonValue(); + } - public void CheckButtonValue() + + // + // Check key value + // + public void CheckKeyValue() { string keys = button.Content.ToString().ToUpper().Trim(); - if (keys.StartsWith("MOUSE")) + if (mouseActions.Contains(keys)) { - switch (keys) + foreach(var item in comboBoxMouse.Items) { - case "MOUSE1": comboBoxMouse.SelectedIndex = 1; break; - case "MOUSE2": comboBoxMouse.SelectedIndex = 2; break; - case "MOUSE3": comboBoxMouse.SelectedIndex = 3; break; - case "MOUSE4": comboBoxMouse.SelectedIndex = 4; break; - case "MOUSE5": comboBoxMouse.SelectedIndex = 5; break; - case "MOUSESCROLLV": comboBoxMouse.SelectedIndex = 6; break; - case "MOUSESCROLLH": comboBoxMouse.SelectedIndex = 7; break; - case "MOUSESCROLLB": comboBoxMouse.SelectedIndex = 8; break; - default: break; + MouseAction mouseAction = (MouseAction)item; + if(mouseAction.Action == keys) + { + comboBoxMouse.SelectedItem = item; + comboBoxMouse.Focus(); + } } - comboBoxMouse.Focus(); } else { @@ -99,18 +149,11 @@ private void ComboBoxMouse_SelectionChanged(object sender, SelectionChangedEvent if (!IsLoaded) return; string key = ""; - - switch (comboBoxMouse.SelectedIndex) + // Combobox item item to MouseAction + if(comboBoxMouse.SelectedIndex >= 0 && comboBoxMouse.SelectedItem != null) { - case 1: key = "MOUSE1"; break; - case 2: key = "MOUSE2"; break; - case 3: key = "MOUSE3"; break; - case 4: key = "MOUSE4"; break; - case 5: key = "MOUSE5"; break; - case 6: key = "MOUSESCROLLV"; break; - case 7: key = "MOUSESCROLLH"; break; - case 8: key = "MOUSESCROLLB"; break; - default: break; + MouseAction mouseAction = (MouseAction)comboBoxMouse.SelectedItem; + key = mouseAction.Action; } textKeyboard.Text = key; textCustom.Text = key; diff --git a/TabletDriverGUI/WindowMessageBox.xaml b/TabletDriverGUI/WindowMessageBox.xaml new file mode 100644 index 0000000..f208c05 --- /dev/null +++ b/TabletDriverGUI/WindowMessageBox.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + diff --git a/TabletDriverGUI/WindowMessageBox.xaml.cs b/TabletDriverGUI/WindowMessageBox.xaml.cs new file mode 100644 index 0000000..3b4ac41 --- /dev/null +++ b/TabletDriverGUI/WindowMessageBox.xaml.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +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 +{ + /// + /// Interaction logic for WindowMessageBox.xaml + /// + public partial class WindowMessageBox : Window + { + public WindowMessageBox(string title, string message, string trueName, string falseName) + { + WindowStartupLocation = WindowStartupLocation.CenterOwner; + Owner = Application.Current.MainWindow; + + InitializeComponent(); + Title = title; + labelMessage.Content = message; + buttonTrue.Content = trueName; + buttonFalse.Content = falseName; + } + + private void ButtonTrue_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + Close(); + } + + private void ButtonFalse_Click(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + } +} diff --git a/TabletDriverGUI/WindowTabletView.xaml b/TabletDriverGUI/WindowTabletView.xaml new file mode 100644 index 0000000..10dd1bb --- /dev/null +++ b/TabletDriverGUI/WindowTabletView.xaml @@ -0,0 +1,62 @@ + + + + + + + Test Tablet - 100x55mm - 1920x1080 + + + + + Input + + + + + Output + + + + + ±100ms + + + diff --git a/TabletDriverGUI/WindowTabletView.xaml.cs b/TabletDriverGUI/WindowTabletView.xaml.cs new file mode 100644 index 0000000..4478c10 --- /dev/null +++ b/TabletDriverGUI/WindowTabletView.xaml.cs @@ -0,0 +1,634 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace TabletDriverGUI +{ + + /// + /// Interaction logic for WindowTabletView.xaml + /// + public partial class WindowTabletView : Window + { + Configuration config; + TabletDriver driver; + TabletRenderer tabletRenderer; + MultimediaTimer timer; + Vector lastPosition; + DateTime lastUpdate; + DateTime lastInputStartTime; + double lastPressure; + bool hadInputLoss; + double velocity; + double latency; + + + // + // TabletViewElement + // + public class TabletRenderer : UIElement + { + + public Point[] LastPositionsInput; + public Point[] LastPositionsOutput; + public Point[] DrawPositions; + public Point PositionInput; + public Point PositionOutput; + public double PressureInput; + public double PressureOutput; + public double ScaleX; + public double ScaleY; + + public readonly Brush brushInput; + public readonly Brush brushOutput; + public readonly Brush brushDraw; + + private readonly Pen penInputLine; + private readonly Pen penOutputLine; + private readonly Pen penDrawLine; + private readonly Pen penInputCircle; + private readonly Pen penOutputCircle; + private readonly Pen penInputPressure; + private readonly Pen penOutputPressure; + + public Rect rectInputPressure; + public Rect rectOutputPressure; + public readonly Rect rectInputPressureBorder; + public readonly Rect rectOutputPressureBorder; + + + // + // Tablet renderer constructor + // + public TabletRenderer(Configuration config) + { + // + // Last position arrays + // + LastPositionsInput = new Point[config.TabletView.InputTrailLength]; + LastPositionsOutput = new Point[config.TabletView.OutputTrailLength]; + for (int i = 0; i < LastPositionsInput.Length; i++) + LastPositionsInput[i] = new Point(-1, -1); + for (int i = 0; i < LastPositionsOutput.Length; i++) + LastPositionsOutput[i] = new Point(-1, -1); + + + // + // Draw position array + // + DrawPositions = new Point[config.TabletView.DrawLength]; + for (int i = 0; i < DrawPositions.Length; i++) + { + DrawPositions[i] = new Point(-1, -1); + } + + // Input/output positions + PositionInput = new Point(0, 0); + PositionOutput = new Point(0, 0); + + // Colors + Color colorInput = Colors.Green; + try { colorInput = (Color)ColorConverter.ConvertFromString(config.TabletView.InputColor); } catch (Exception) { } + Color colorOutput = Colors.Red; + try { colorOutput = (Color)ColorConverter.ConvertFromString(config.TabletView.OutputColor); } catch (Exception) { } + Color colorDraw = Colors.White; + try { colorDraw = (Color)ColorConverter.ConvertFromString(config.TabletView.DrawColor); } catch (Exception) { } + + // Brushes + brushInput = new SolidColorBrush(colorInput); + brushInput.Freeze(); + brushOutput = new SolidColorBrush(colorOutput); + brushOutput.Freeze(); + brushDraw = new SolidColorBrush(colorDraw); + brushDraw.Freeze(); + + // Line pens + penInputLine = new Pen(brushInput, 3) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round, + LineJoin = PenLineJoin.Round + }; + penInputLine.Freeze(); + penOutputLine = new Pen(brushOutput, 3) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round, + LineJoin = PenLineJoin.Round + }; + penOutputLine.Freeze(); + penDrawLine = new Pen(brushDraw, 3) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round, + LineJoin = PenLineJoin.Round + }; + penDrawLine.Freeze(); + + // Circle pens + penInputCircle = new Pen(brushInput, 3); + penInputCircle.Freeze(); + penOutputCircle = new Pen(brushOutput, 3); + penOutputCircle.Freeze(); + + // Pressure border pens + penInputPressure = new Pen(brushInput, 3) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round + }; + penInputPressure.Freeze(); + penOutputPressure = new Pen(brushOutput, 3) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round + }; + penOutputPressure.Freeze(); + + // Rects + rectInputPressure = + new Rect(135, 54, 200, 20); + rectInputPressureBorder = + new Rect(135, 54, 200, 20); + rectOutputPressure = + new Rect(135, 84, 200, 20); + rectOutputPressureBorder = + new Rect(135, 84, 200, 20); + + // Offset pressure bars + rectInputPressure.X += config.TabletView.OffsetPressure.X; + rectInputPressure.Y += config.TabletView.OffsetPressure.Y; + rectInputPressureBorder.X += config.TabletView.OffsetPressure.X; + rectInputPressureBorder.Y += config.TabletView.OffsetPressure.Y; + rectOutputPressure.X += config.TabletView.OffsetPressure.X; + rectOutputPressure.Y += config.TabletView.OffsetPressure.Y; + rectOutputPressureBorder.X += config.TabletView.OffsetPressure.X; + rectOutputPressureBorder.Y += config.TabletView.OffsetPressure.Y; + + } + + + // + // Update last position arrays + // + public void UpdateLastPositions() + { + if (LastPositionsInput.Length != 0) + { + Array.Copy(LastPositionsInput, 1, LastPositionsInput, 0, LastPositionsInput.Length - 1); + LastPositionsInput[LastPositionsInput.Length - 1].X = PositionInput.X; + LastPositionsInput[LastPositionsInput.Length - 1].Y = PositionInput.Y; + } + if (LastPositionsOutput.Length != 0) + { + Array.Copy(LastPositionsOutput, 1, LastPositionsOutput, 0, LastPositionsOutput.Length - 1); + LastPositionsOutput[LastPositionsOutput.Length - 1].X = PositionOutput.X; + LastPositionsOutput[LastPositionsOutput.Length - 1].Y = PositionOutput.Y; + } + } + + + // + // Update draw position array + // + public void AddDrawPosition(double x, double y) + { + if (DrawPositions.Length == 0) return; + + // Shift position array + Array.Copy(DrawPositions, 1, DrawPositions, 0, DrawPositions.Length - 1); + + // Set currect position as last item in the array + DrawPositions[DrawPositions.Length - 1].X = x; + DrawPositions[DrawPositions.Length - 1].Y = y; + } + + + // + // Render + // + protected override void OnRender(DrawingContext context) + { + PathFigure path; + PathGeometry geometry; + + // Pressure bars + rectInputPressure.Width = PressureInput * rectInputPressureBorder.Width; + rectOutputPressure.Width = PressureOutput * rectInputPressureBorder.Width; + context.DrawRectangle(brushInput, null, rectInputPressure); + context.DrawRectangle(brushOutput, null, rectOutputPressure); + context.DrawRectangle(null, penInputPressure, rectInputPressureBorder); + context.DrawRectangle(null, penOutputPressure, rectOutputPressureBorder); + + // + // Drawing line + // + if (DrawPositions.Length > 0) + { + path = new PathFigure + { + StartPoint = DrawPositions[0] + }; + for (int i = 1; i < DrawPositions.Length; i++) + { + // Last empty + if (DrawPositions[i].X >= 0 && DrawPositions[i - 1].X <= 0) + { + path.Segments.Add(new LineSegment(DrawPositions[i], false)); + path.Segments.Add(new LineSegment(DrawPositions[i], true)); + } + // Current and last OK + else if (DrawPositions[i].X >= 0 && DrawPositions[i - 1].X >= 0) + path.Segments.Add(new LineSegment(DrawPositions[i], true)); + + // Current and last empty + else + path.Segments.Add(new LineSegment(DrawPositions[i], false)); + } + path.Freeze(); + geometry = new PathGeometry(); + geometry.Figures.Add(path); + geometry.Freeze(); + context.DrawGeometry(null, penDrawLine, geometry); + } + + // + // Input line + // + if (LastPositionsInput.Length > 0) + { + path = new PathFigure + { + StartPoint = LastPositionsInput[0] + }; + for (int i = 1; i < LastPositionsInput.Length; i++) + path.Segments.Add(new LineSegment(LastPositionsInput[i], true)); + path.Freeze(); + geometry = new PathGeometry(); + geometry.Figures.Add(path); + geometry.Freeze(); + context.DrawGeometry(null, penInputLine, geometry); + } + + + // + // Output line + // + if (LastPositionsOutput.Length > 0) + { + path = new PathFigure + { + IsClosed = false, + IsFilled = false, + StartPoint = LastPositionsOutput[0] + }; + for (int i = 1; i < LastPositionsOutput.Length; i++) + path.Segments.Add(new LineSegment(LastPositionsOutput[i], true)); + path.Freeze(); + geometry = new PathGeometry(); + geometry.Figures.Add(path); + geometry.Freeze(); + context.DrawGeometry(null, penOutputLine, geometry); + } + + // Input circle + context.DrawEllipse(null, penInputCircle, PositionInput, 12, 12); + + // Output circle + context.DrawEllipse(null, penOutputCircle, PositionOutput, 12, 12); + + } + + } + + // + // Tablet View Constructor + // + public WindowTabletView(Configuration config, TabletDriver driver) + { + InitializeComponent(); + + this.config = config; + this.driver = driver; + + // Tablet renderer + tabletRenderer = new TabletRenderer(config); + canvasTabletView.Children.Add(tabletRenderer); + + // Offset texts + Canvas.SetLeft(textTabletInfo, Canvas.GetLeft(textTabletInfo) + config.TabletView.OffsetText.X); + Canvas.SetTop(textTabletInfo, Canvas.GetTop(textTabletInfo) + config.TabletView.OffsetText.Y); + Canvas.SetLeft(textInput, Canvas.GetLeft(textInput) + config.TabletView.OffsetText.X); + Canvas.SetTop(textInput, Canvas.GetTop(textInput) + config.TabletView.OffsetText.Y); + Canvas.SetLeft(textOutput, Canvas.GetLeft(textOutput) + config.TabletView.OffsetText.X); + Canvas.SetTop(textOutput, Canvas.GetTop(textOutput) + config.TabletView.OffsetText.Y); + Canvas.SetLeft(textLatency, Canvas.GetLeft(textLatency) + config.TabletView.OffsetText.X); + Canvas.SetTop(textLatency, Canvas.GetTop(textLatency) + config.TabletView.OffsetText.Y); + + // Background color + Brush brush; + try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.BackgroundColor)); } + catch (Exception) { brush = Brushes.White; } + canvasTabletView.Background = brush; + Background = brush; + + // Text colors + try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.InfoColor)); } + catch (Exception) { brush = Brushes.Black; } + textTabletInfo.Foreground = brush; + try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.LatencyColor)); } + catch (Exception) { brush = Brushes.Black; } + textLatency.Foreground = brush; + textInput.Foreground = tabletRenderer.brushInput; + textOutput.Foreground = tabletRenderer.brushOutput; + + // Text font + try + { + FontFamilyConverter fontConverter = new FontFamilyConverter(); + FontFamily fontFamily = (FontFamily)fontConverter.ConvertFromString(config.TabletView.Font); + textTabletInfo.FontFamily = fontFamily; + textInput.FontFamily = fontFamily; + textOutput.FontFamily = fontFamily; + textLatency.FontFamily = fontFamily; + } + catch (Exception) { } + + // Font size + textTabletInfo.FontSize = config.TabletView.FontSize; + textInput.FontSize = config.TabletView.FontSize; + textOutput.FontSize = config.TabletView.FontSize; + textLatency.FontSize = config.TabletView.FontSize; + + // Info text + textTabletInfo.Text = config.TabletName + " - " + + Utils.GetNumberString(config.TabletAreas[0].Width) + " x " + + Utils.GetNumberString(config.TabletAreas[0].Height) + " mm → " + + Utils.GetNumberString(config.ScreenAreas[0].Width, "0") + " x " + + Utils.GetNumberString(config.ScreenAreas[0].Height, "0") + " px"; + + + // + // Update/draw timer + // + timer = new MultimediaTimer { Interval = 2 }; + timer.Tick += UpdateTimer_Tick; + + // Last values + lastPosition = new Vector(0, 0); + lastUpdate = DateTime.Now; + lastPressure = 0; + + // Average values + velocity = 0; + latency = 0; + + + // Input loss + hadInputLoss = true; + lastInputStartTime = DateTime.Now; + + // Window events + Loaded += WindowTabletView_Loaded; + Closing += WindowTabletView_Closing; + KeyDown += WindowTabletView_KeyDown; + + + // Set GC mode to low latency + GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency; + + } + + + // + // Update/draw timer tick + // + private void UpdateTimer_Tick(object sender, EventArgs e) + { + // Output position haven't changed -> skip + if ( + lastPosition.X == driver.tabletState.outputX + && + lastPosition.Y == driver.tabletState.outputY + ) + { + double timeDelta = (DateTime.Now - lastUpdate).TotalMilliseconds; + + // Input loss + if (timeDelta >= 30) + { + hadInputLoss = true; + } + + // Hide renderer + if ( + config.TabletView.FadeInOut + && + timeDelta >= 200 + && + timeDelta <= 1000 + ) + { + Application.Current.Dispatcher.Invoke(() => + { + canvasTabletView.Opacity -= (timeDelta - 200) / 5000.0; + if (canvasTabletView.Opacity < 0) + canvasTabletView.Opacity = 0; + + //canvasTabletView.Opacity = 1.0 - ((timeDelta - 200.0) / 800.0); + canvasTabletView.InvalidateVisual(); + }); + } + + + return; + } + else + { + if (hadInputLoss) + { + lastInputStartTime = DateTime.Now; + hadInputLoss = false; + } + } + + // + // Update renderer positions + // + // Inverted + if (config.Invert) + { + tabletRenderer.PositionInput.X = config.TabletFullArea.Width - driver.tabletState.inputX - config.TabletAreas[0].X; + tabletRenderer.PositionInput.Y = config.TabletFullArea.Height - driver.tabletState.inputY - config.TabletAreas[0].Y; + tabletRenderer.PositionOutput.X = config.TabletFullArea.Width - driver.tabletState.outputX - config.TabletAreas[0].X; + tabletRenderer.PositionOutput.Y = config.TabletFullArea.Height - driver.tabletState.outputY - config.TabletAreas[0].Y; + } + // Normal + else + { + tabletRenderer.PositionInput.X = driver.tabletState.inputX - config.TabletAreas[0].X; + tabletRenderer.PositionInput.Y = driver.tabletState.inputY - config.TabletAreas[0].Y; + tabletRenderer.PositionOutput.X = driver.tabletState.outputX - config.TabletAreas[0].X; + tabletRenderer.PositionOutput.Y = driver.tabletState.outputY - config.TabletAreas[0].Y; + } + + // Rotate and offset positions + Point rotatedPoint = new Point(0, 0); + config.TabletAreas[0].GetRotatedPointReverse(ref rotatedPoint, tabletRenderer.PositionInput.X, tabletRenderer.PositionInput.Y); + tabletRenderer.PositionInput.X = rotatedPoint.X + config.TabletAreas[0].Width / 2.0; + tabletRenderer.PositionInput.Y = rotatedPoint.Y + config.TabletAreas[0].Height / 2.0; + + config.TabletAreas[0].GetRotatedPointReverse(ref rotatedPoint, tabletRenderer.PositionOutput.X, tabletRenderer.PositionOutput.Y); + tabletRenderer.PositionOutput.X = rotatedPoint.X + config.TabletAreas[0].Width / 2.0; + tabletRenderer.PositionOutput.Y = rotatedPoint.Y + config.TabletAreas[0].Height / 2.0; + + + // Scale to canvas + tabletRenderer.PositionInput.X *= tabletRenderer.ScaleX; + tabletRenderer.PositionInput.Y *= tabletRenderer.ScaleY; + tabletRenderer.PositionOutput.X *= tabletRenderer.ScaleX; + tabletRenderer.PositionOutput.Y *= tabletRenderer.ScaleY; + + + // Update renderer pressures + tabletRenderer.PressureInput = driver.tabletState.inputPressure; + tabletRenderer.PressureOutput = driver.tabletState.outputPressure; + + // + // Run in the main thread + // + Application.Current.Dispatcher.Invoke(() => + { + // Show renderer + if (config.TabletView.FadeInOut) + { + double timeDelta = (DateTime.Now - lastInputStartTime).TotalMilliseconds; + if (timeDelta >= 0 && timeDelta <= 800 || canvasTabletView.Opacity >= 1) + { + canvasTabletView.Opacity += timeDelta / 5000.0; + if (canvasTabletView.Opacity >= 1) + canvasTabletView.Opacity = 1; + } + else + { + canvasTabletView.Opacity = 1.0; + } + } + + // Update pen positions + tabletRenderer.UpdateLastPositions(); + + // Add draw positions + if (driver.tabletState.outputPressure > 0) + { + if (lastPressure <= 0) + { + tabletRenderer.AddDrawPosition(-1, -1); + } + tabletRenderer.AddDrawPosition(tabletRenderer.PositionOutput.X, tabletRenderer.PositionOutput.Y); + } + lastPressure = driver.tabletState.outputPressure; + + // Latency text + if (driver.tabletState.inputVelocity > 0) + { + double dx = driver.tabletState.inputX - driver.tabletState.outputX; + double dy = driver.tabletState.inputY - driver.tabletState.outputY; + double distance = Math.Sqrt(dx * dx + dy * dy); + double targetLatency = distance / driver.tabletState.inputVelocity * 1000.0; + latency += (targetLatency - latency) / 10.0; + velocity += (driver.tabletState.inputVelocity - velocity) / 20.0; + textLatency.Text = "±" + Utils.GetNumberString(latency, "0") + " ms\n" + + Utils.GetNumberString(Math.Round(velocity / 10.0) * 10, "0") + " mm/s"; + } + + // Render + tabletRenderer.InvalidateVisual(); + + // Update last values + lastPosition.X = driver.tabletState.outputX; + lastPosition.Y = driver.tabletState.outputY; + lastUpdate = DateTime.Now; + }); + + } + + + // + // Window key down + // + private void WindowTabletView_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + Close(); + } + if (e.Key == Key.B) + { + if (canvasTabletView.Background == Brushes.White) + canvasTabletView.Background = Brushes.Black; + else + canvasTabletView.Background = Brushes.White; + } + } + + + // + // Window loaded + // + private void WindowTabletView_Loaded(object sender, RoutedEventArgs e) + { + double scaleX, scaleY; + double aspectScreen, aspectTablet; + double newHeight; + + // Canvas default size + canvasTabletView.Width = 1280; + canvasTabletView.Height = 720; + + // Tablet renderer + scaleX = canvasTabletView.Width / config.TabletAreas[0].Width; + + // Set canvas height + newHeight = config.TabletAreas[0].Height * scaleX; + aspectScreen = config.ScreenAreas[0].Width / config.ScreenAreas[0].Height; + aspectTablet = config.TabletAreas[0].Width / config.TabletAreas[0].Height; + newHeight *= aspectTablet / aspectScreen; + scaleY = newHeight / config.TabletAreas[0].Height; + canvasTabletView.Height = newHeight; + + // Set renderer scale + tabletRenderer.ScaleX = scaleX; + tabletRenderer.ScaleY = scaleY; + + // Timer + timer.Start(); + + // Enable state output + driver.SendCommand("StateOutput true"); + } + + // + // Window closed + // + private void WindowTabletView_Closing(object sender, EventArgs e) + { + if (timer.IsRunning) + timer.Stop(); + + // Disable state output + driver.SendCommand("StateOutput false"); + + // Set GC mode back to normal + GCSettings.LatencyMode = GCLatencyMode.Interactive; + GC.Collect(10, GCCollectionMode.Forced); + } + + } +} diff --git a/TabletDriverGUI/WindowTabletViewSettings.xaml b/TabletDriverGUI/WindowTabletViewSettings.xaml new file mode 100644 index 0000000..ce0ff75 --- /dev/null +++ b/TabletDriverGUI/WindowTabletViewSettings.xaml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + #FFFFFF + + + #ffffff + + + #ffffff + + + #ffffff + + + + #ffffff + + + + #ffffff + + + + 30 + + + + 30 + + + + 30 + + + + Arial + + + + 25 + + + + + + + + + 0 + 0 + + + + + + + + + + 0 + 0 + + + + Fade in/out when input is detected/lost + + + + + + + + diff --git a/TabletDriverGUI/WindowTabletViewSettings.xaml.cs b/TabletDriverGUI/WindowTabletViewSettings.xaml.cs new file mode 100644 index 0000000..354b07b --- /dev/null +++ b/TabletDriverGUI/WindowTabletViewSettings.xaml.cs @@ -0,0 +1,206 @@ +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace TabletDriverGUI +{ + /// + /// Interaction logic for TabletViewSettings.xaml + /// + public partial class WindowTabletViewSettings : Window + { + Configuration config; + + List presets; + + public WindowTabletViewSettings(Configuration config) + { + InitializeComponent(); + this.config = config; + + presets = new List + { + new Configuration.Preset("Default", (c) => + { + }), + + new Configuration.Preset("OBS Overlay", (c) => + { + Configuration.TabletViewSettings settings = c.TabletView; + settings.BackgroundColor = "#000000"; + settings.InfoColor = "#3333FF"; + settings.InputColor = "#33FF33"; + settings.OutputColor = "#FF3333"; + settings.LatencyColor = "#FFFF33"; + settings.DrawColor = "#FFFFFF"; + }), + + new Configuration.Preset("OBS Overlay 2", (c) => + { + Configuration.TabletViewSettings settings = c.TabletView; + settings.BackgroundColor = "#000000"; + settings.InfoColor = Utils.RGBToHexColor(50,100,255); + settings.InputColor = Utils.RGBToHexColor(190,255,0); + settings.OutputColor = Utils.RGBToHexColor(255,190,0); + settings.LatencyColor = Utils.RGBToHexColor(230,200,0); + settings.DrawColor = "#6666AA"; + settings.InputTrailLength = 100; + settings.OutputTrailLength = 100; + settings.DrawLength = 0; + settings.Font = "Exo 2"; + settings.FontSize = 25; + settings.OffsetText.X = 0; + settings.OffsetText.Y = 10; + settings.OffsetPressure.X = -15; + settings.OffsetPressure.Y = 12; + settings.FadeInOut = true; + }), + + new Configuration.Preset("OBS Input Cursor Only", (c) => + { + Configuration.TabletViewSettings settings = c.TabletView; + settings.BackgroundColor = "#000000"; + settings.InputColor = "#33FF33"; + settings.OutputColor = "transparent"; + settings.InputTrailLength = 100; + settings.OutputTrailLength = 0; + settings.OffsetText = new Point(0, -200); + settings.OffsetPressure = new Point(0, -200); + settings.FadeInOut = true; + }), + + new Configuration.Preset("OBS Output Cursor Only", (c) => + { + Configuration.TabletViewSettings settings = c.TabletView; + settings.BackgroundColor = "#000000"; + settings.InputColor = "transparent"; + settings.OutputColor = "#FF3333"; + settings.InputTrailLength = 0; + settings.OutputTrailLength = 100; + settings.OffsetText = new Point(0, -200); + settings.OffsetPressure = new Point(0, -200); + settings.FadeInOut = true; + }), + + + }; + + // Fill combobox + foreach (var preset in presets) + { + comboBoxPresets.Items.Add(preset); + } + comboBoxPresets.Focus(); + + LoadValues(config); + + KeyDown += WindowTabletViewSettings_KeyDown; + } + + private void WindowTabletViewSettings_KeyDown(object sender, KeyEventArgs e) + { + // Esc -> Cancel + if (e.Key == Key.Escape) + { + ButtonCancel_Click(sender, null); + } + + } + + public void LoadValues(Configuration config) + { + textBackgroundColor.Text = config.TabletView.BackgroundColor; + textInfoColor.Text = config.TabletView.InfoColor; + textInputColor.Text = config.TabletView.InputColor; + textOutputColor.Text = config.TabletView.OutputColor; + textLatencyColor.Text = config.TabletView.LatencyColor; + textDrawColor.Text = config.TabletView.DrawColor; + textInputTrailLength.Text = Utils.GetNumberString(config.TabletView.InputTrailLength); + textOutputTrailLength.Text = Utils.GetNumberString(config.TabletView.OutputTrailLength); + textDrawLength.Text = Utils.GetNumberString(config.TabletView.DrawLength); + textFont.Text = config.TabletView.Font; + textFontSize.Text = Utils.GetNumberString(config.TabletView.FontSize); + textOffsetTextX.Text = Utils.GetNumberString(config.TabletView.OffsetText.X); + textOffsetTextY.Text = Utils.GetNumberString(config.TabletView.OffsetText.Y); + textOffsetPressureX.Text = Utils.GetNumberString(config.TabletView.OffsetPressure.X); + textOffsetPressureY.Text = Utils.GetNumberString(config.TabletView.OffsetPressure.Y); + checkBoxFadeInOut.IsChecked = config.TabletView.FadeInOut; + } + + public void StoreValues() + { + + config.TabletView.BackgroundColor = textBackgroundColor.Text; + config.TabletView.InfoColor = textInfoColor.Text; + config.TabletView.InputColor = textInputColor.Text; + config.TabletView.OutputColor = textOutputColor.Text; + config.TabletView.LatencyColor = textLatencyColor.Text; + config.TabletView.DrawColor = textDrawColor.Text; + config.TabletView.Font = textFont.Text; + + if (Utils.ParseNumber(textInputTrailLength.Text, out double value)) + config.TabletView.InputTrailLength = (int)value; + if (config.TabletView.InputTrailLength < 0) + config.TabletView.InputTrailLength = 0; + + if (Utils.ParseNumber(textOutputTrailLength.Text, out value)) + config.TabletView.OutputTrailLength = (int)value; + if (config.TabletView.OutputTrailLength < 0) + config.TabletView.OutputTrailLength = 0; + + if (Utils.ParseNumber(textDrawLength.Text, out value)) + config.TabletView.DrawLength = (int)value; + if (config.TabletView.DrawLength < 0) + config.TabletView.DrawLength = 0; + + if (Utils.ParseNumber(textFontSize.Text, out value)) + config.TabletView.FontSize = value; + if (Utils.ParseNumber(textOffsetTextX.Text, out value)) + config.TabletView.OffsetText.X = value; + if (Utils.ParseNumber(textOffsetTextY.Text, out value)) + config.TabletView.OffsetText.Y = value; + if (Utils.ParseNumber(textOffsetPressureX.Text, out value)) + config.TabletView.OffsetPressure.X = value; + if (Utils.ParseNumber(textOffsetPressureY.Text, out value)) + config.TabletView.OffsetPressure.Y = value; + config.TabletView.FadeInOut = (bool)checkBoxFadeInOut.IsChecked; + + } + + private void ButtonSave_Click(object sender, RoutedEventArgs e) + { + StoreValues(); + DialogResult = true; + Close(); + } + + private void ButtonCancel_Click(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + private void ComboBoxPresets_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (comboBoxPresets.SelectedItem != null) + { + Configuration tmpConfig = new Configuration(); + Configuration.Preset preset = (Configuration.Preset)comboBoxPresets.SelectedItem; + preset.Action(tmpConfig); + LoadValues(tmpConfig); + } + } + + private void ComboBoxPresets_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + ButtonSave_Click(sender, null); + } + } + } +} diff --git a/TabletDriverGUI/WacomArea.xaml b/TabletDriverGUI/WindowWacomArea.xaml similarity index 83% rename from TabletDriverGUI/WacomArea.xaml rename to TabletDriverGUI/WindowWacomArea.xaml index 7e8b267..67ddd25 100644 --- a/TabletDriverGUI/WacomArea.xaml +++ b/TabletDriverGUI/WindowWacomArea.xaml @@ -1,57 +1,60 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/TabletDriverGUI/WindowButtonMapping.xaml.cs b/TabletDriverGUI/WindowButtonMapping.xaml.cs index 7a0ccbe..15ba082 100644 --- a/TabletDriverGUI/WindowButtonMapping.xaml.cs +++ b/TabletDriverGUI/WindowButtonMapping.xaml.cs @@ -18,18 +18,18 @@ public partial class WindowButtonMapping : Window Button button; public string Result; - public class MouseAction + public class ButtonBinding { - public string Action; + public string Key; public string Name; public bool Visible; - public MouseAction(string name) : this("", name) - { + public ButtonBinding(string name) : this("", name) + { } - public MouseAction(string action, string name) + public ButtonBinding(string key, string name) { - Action = action; + Key = key; Name = name; Visible = true; } @@ -39,7 +39,8 @@ public override string ToString() } } - public OrderedDictionary mouseActions; + public OrderedDictionary mouseBindings; + public OrderedDictionary multimediaBindings; public WindowButtonMapping() { @@ -50,24 +51,41 @@ public WindowButtonMapping() Result = ""; - mouseActions = new OrderedDictionary() + mouseBindings = new OrderedDictionary() + { + { "", new ButtonBinding("None") }, + { "MOUSE1", new ButtonBinding("Mouse 1 (Left / Tip)") }, + { "MOUSE2", new ButtonBinding("Mouse 2 (Right / Barrel)") }, + { "MOUSE3", new ButtonBinding("Mouse 3 (Middle / Eraser)") }, + { "MOUSE4", new ButtonBinding("Mouse 4 (Back)") }, + { "MOUSE5", new ButtonBinding("Mouse 5 (Forward)") }, + { "MOUSESCROLLV", new ButtonBinding("Scroll Up/Down") }, + { "MOUSESCROLLH", new ButtonBinding("Scroll Left/Right") }, + { "MOUSESCROLLB", new ButtonBinding("Scroll Both") }, + { "SCROLLUP", new ButtonBinding("Scroll One Up") }, + { "SCROLLDOWN", new ButtonBinding("Scroll One Down") }, + { "SCROLLLEFT", new ButtonBinding("Scroll One Left") }, + { "SCROLLRIGHT", new ButtonBinding("Scroll One Right") }, + }; + + multimediaBindings = new OrderedDictionary() { - { "", new MouseAction("None") }, - { "MOUSE1", new MouseAction("Mouse 1 (Left / Tip)") }, - { "MOUSE2", new MouseAction("Mouse 2 (Right / Barrel)") }, - { "MOUSE3", new MouseAction("Mouse 3 (Middle / Eraser)") }, - { "MOUSE4", new MouseAction("Mouse 4 (Back)") }, - { "MOUSE5", new MouseAction("Mouse 5 (Forward)") }, - { "MOUSESCROLLV", new MouseAction("Scroll Up/Down") }, - { "MOUSESCROLLH", new MouseAction("Scroll Left/Right") }, - { "MOUSESCROLLB", new MouseAction("Scroll Both") }, - { "SCROLLUP", new MouseAction("Scroll One Up") }, - { "SCROLLDOWN", new MouseAction("Scroll One Down") }, - { "SCROLLLEFT", new MouseAction("Scroll One Left") }, - { "SCROLLRIGHT", new MouseAction("Scroll One Right") } + { "", new ButtonBinding("None") }, + { "VOLUMEUP", new ButtonBinding("Volume Up") }, + { "VOLUMEUP0.5", new ButtonBinding("Volume Up 0.5%") }, + { "VOLUMEUP5.0", new ButtonBinding("Volume Up 5.0%") }, + { "VOLUMEDOWN", new ButtonBinding("Volume Down") }, + { "VOLUMEDOWN0.5", new ButtonBinding("Volume Down 0.5%") }, + { "VOLUMEDOWN5.0", new ButtonBinding("Volume Down 5.0%") }, + { "VOLUMEMUTE", new ButtonBinding("Volume Mute") }, + { "VOLUMECONTROL", new ButtonBinding("Volume Control Up/Down") }, + { "MEDIANEXT", new ButtonBinding("Media Next Track") }, + { "MEDIAPREV", new ButtonBinding("Media Previous Track") }, + { "MEDIASTOP", new ButtonBinding("Media Stop") }, + { "MEDIAPLAY", new ButtonBinding("Media Play/Pause") }, }; - UpdateMouseActions(); + UpdateBindings(); } @@ -78,34 +96,43 @@ public WindowButtonMapping(Button button, bool isPenButton) : this() // Tablet buttons don't need tip, barrel and eraser info if (!isPenButton) { - ((MouseAction)mouseActions["MOUSE1"]).Name = "Mouse 1 (Left)"; - ((MouseAction)mouseActions["MOUSE2"]).Name = "Mouse 2 (Right)"; - ((MouseAction)mouseActions["MOUSE3"]).Name = "Mouse 3 (Middle)"; - - // Disable mouse scroll - ((MouseAction)mouseActions["MOUSESCROLLV"]).Visible = false; - ((MouseAction)mouseActions["MOUSESCROLLH"]).Visible = false; - ((MouseAction)mouseActions["MOUSESCROLLB"]).Visible = false; + ((ButtonBinding)mouseBindings["MOUSE1"]).Name = "Mouse 1 (Left)"; + ((ButtonBinding)mouseBindings["MOUSE2"]).Name = "Mouse 2 (Right)"; + ((ButtonBinding)mouseBindings["MOUSE3"]).Name = "Mouse 3 (Middle)"; } - UpdateMouseActions(); + UpdateBindings(); CheckKeyValue(); } // - // Update mouse actions and combobox + // Update bindings and comboboxes // - private void UpdateMouseActions() + private void UpdateBindings() { + + // Mouse comboBoxMouse.Items.Clear(); - foreach (DictionaryEntry entry in mouseActions) + foreach (DictionaryEntry entry in mouseBindings) { - MouseAction mouseAction = (MouseAction)entry.Value; - mouseAction.Action = (string)entry.Key; - if (mouseAction.Visible) + ButtonBinding binding = (ButtonBinding)entry.Value; + binding.Key = (string)entry.Key; + if (binding.Visible) { - comboBoxMouse.Items.Add(mouseAction); + comboBoxMouse.Items.Add(binding); + } + } + + // Multimedia + comboBoxMultimedia.Items.Clear(); + foreach (DictionaryEntry entry in multimediaBindings) + { + ButtonBinding binding = (ButtonBinding)entry.Value; + binding.Key = (string)entry.Key; + if (binding.Visible) + { + comboBoxMultimedia.Items.Add(binding); } } @@ -118,19 +145,40 @@ private void UpdateMouseActions() public void CheckKeyValue() { string keys = button.Content.ToString().ToUpper().Trim(); + comboBoxMouse.SelectedIndex = 0; + comboBoxMultimedia.SelectedIndex = 0; - if (mouseActions.Contains(keys)) + // Mouse + if (mouseBindings.Contains(keys)) { - foreach(var item in comboBoxMouse.Items) + + foreach (var item in comboBoxMouse.Items) { - MouseAction mouseAction = (MouseAction)item; - if(mouseAction.Action == keys) + ButtonBinding binding = (ButtonBinding)item; + if (binding.Key == keys) { comboBoxMouse.SelectedItem = item; comboBoxMouse.Focus(); } } } + + // Multimedia + else if (multimediaBindings.Contains(keys)) + { + comboBoxMultimedia.SelectedIndex = 0; + foreach (ButtonBinding item in comboBoxMultimedia.Items) + { + //ButtonBinding binding = (ButtonBinding)item; + if (item.Key == keys) + { + comboBoxMultimedia.SelectedItem = item; + comboBoxMultimedia.Focus(); + } + } + } + + // Keyboard else { textKeyboard.Focus(); @@ -147,16 +195,30 @@ public void CheckKeyValue() private void ComboBoxMouse_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsLoaded) return; - string key = ""; + if (comboBoxMouse.SelectedIndex >= 0 && comboBoxMouse.SelectedItem != null) + { + ButtonBinding binding = (ButtonBinding)comboBoxMouse.SelectedItem; + if (comboBoxMouse.SelectedIndex > 0) + comboBoxMultimedia.SelectedIndex = 0; + textKeyboard.Text = binding.Key; + textCustom.Text = binding.Key; + } + } - // Combobox item item to MouseAction - if(comboBoxMouse.SelectedIndex >= 0 && comboBoxMouse.SelectedItem != null) + // + // Multimedia key changed + // + private void ComboBoxMultimedia_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (!IsLoaded) return; + if (comboBoxMultimedia.SelectedIndex >= 0 && comboBoxMultimedia.SelectedItem is ButtonBinding) { - MouseAction mouseAction = (MouseAction)comboBoxMouse.SelectedItem; - key = mouseAction.Action; + ButtonBinding binding = (ButtonBinding)comboBoxMultimedia.SelectedItem; + if (comboBoxMultimedia.SelectedIndex > 0) + comboBoxMouse.SelectedIndex = 0; + textKeyboard.Text = binding.Key; + textCustom.Text = binding.Key; } - textKeyboard.Text = key; - textCustom.Text = key; } @@ -275,5 +337,7 @@ private void OnEnterKeyUp(object sender, KeyEventArgs e) ButtonSet_Click(sender, null); } } + + } } diff --git a/TabletDriverService/InputEmulator.cpp b/TabletDriverService/InputEmulator.cpp index 2063ef6..636b5c9 100644 --- a/TabletDriverService/InputEmulator.cpp +++ b/TabletDriverService/InputEmulator.cpp @@ -8,9 +8,6 @@ InputEmulator::InputEmulator() { CreateKeyMap(); CreateVirtualKeyMap(); - - // - CoInitializeEx(NULL, COINIT_MULTITHREADED); } @@ -27,11 +24,11 @@ void InputEmulator::CreateKeyMap() AddKey("DISABLED", "Disabled", 0, 0); // Mouse buttons - AddKey("MOUSE1", "Mouse 1", 0, 1); - AddKey("MOUSE2", "Mouse 2", 0, 2); - AddKey("MOUSE3", "Mouse 3", 0, 3); - AddKey("MOUSE4", "Mouse 4", 0, 4); - AddKey("MOUSE5", "Mouse 5", 0, 5); + AddKey("MOUSE1", "Mouse 1", 0, MouseButtons::Mouse1); + AddKey("MOUSE2", "Mouse 2", 0, MouseButtons::Mouse2); + AddKey("MOUSE3", "Mouse 3", 0, MouseButtons::Mouse3); + AddKey("MOUSE4", "Mouse 4", 0, MouseButtons::Mouse4); + AddKey("MOUSE5", "Mouse 5", 0, MouseButtons::Mouse5); // Mouse scroll AddKey("SCROLLUP", "Mouse Scroll Up", 0, MouseButtons::MouseScrollUp); @@ -45,6 +42,9 @@ void InputEmulator::CreateKeyMap() AddKey("MOUSESCROLLH", "Mouse Scroll Horizontal", 0, MouseButtons::MouseScrollHorizontal); AddKey("MOUSESCROLLB", "Mouse Scroll Both", 0, MouseButtons::MouseScrollBoth); + // Media volume control + AddKey("VOLUMECONTROL", "Media Volume Control", 0, MouseButtons::MediaVolumeControl); + // Shift AddKey("SHIFT", "Shift", VK_SHIFT); AddKey("LSHIFT", "Left shift", VK_LSHIFT); @@ -616,6 +616,8 @@ void InputEmulator::VolumeSet(float volume) { // Get endpoint volume if(CreateEndpointVolume()) { + if(volume < 0) volume = 0.0f; + else if(volume > 1) volume = 1.0f; pAudioEndpointVolume->SetMasterVolumeLevelScalar(volume, &GUID_NULL); } ReleaseEndpointVolume(); @@ -642,7 +644,10 @@ void InputEmulator::VolumeChange(float delta) float volume; if(CreateEndpointVolume()) { pAudioEndpointVolume->GetMasterVolumeLevelScalar(&volume); - pAudioEndpointVolume->SetMasterVolumeLevelScalar(volume + delta, &GUID_NULL); + volume += delta; + if(volume < 0) volume = 0.0f; + else if(volume > 1) volume = 1.0f; + pAudioEndpointVolume->SetMasterVolumeLevelScalar(volume, &GUID_NULL); } ReleaseEndpointVolume(); } diff --git a/TabletDriverService/InputEmulator.h b/TabletDriverService/InputEmulator.h index 02f7143..e0c05fb 100644 --- a/TabletDriverService/InputEmulator.h +++ b/TabletDriverService/InputEmulator.h @@ -46,6 +46,7 @@ class InputEmulator MouseScrollVertical = 0x101, MouseScrollHorizontal = 0x102, MouseScrollBoth = 0x103, + MediaVolumeControl = 0x110, }; class KeyMapValue { diff --git a/TabletDriverService/Main.cpp b/TabletDriverService/Main.cpp index 12e8d52..39c4b18 100644 --- a/TabletDriverService/Main.cpp +++ b/TabletDriverService/Main.cpp @@ -46,6 +46,9 @@ int main(int argc, char**argv) { // Init console InitConsole(); + // Initialize COM library (Volume control) + CoInitializeEx(NULL, COINIT_MULTITHREADED); + // Tablet handler tabletHandler = new TabletHandler(); diff --git a/TabletDriverService/Tablet.h b/TabletDriverService/Tablet.h index da8a9e1..ddf8819 100644 --- a/TabletDriverService/Tablet.h +++ b/TabletDriverService/Tablet.h @@ -86,6 +86,7 @@ class Tablet { bool isValid; bool isHandled; USHORT buttons; + USHORT lastButtons; }; TabletAuxState auxState; diff --git a/TabletDriverService/TabletHandler.cpp b/TabletDriverService/TabletHandler.cpp index 428d0ef..c3c190a 100644 --- a/TabletDriverService/TabletHandler.cpp +++ b/TabletDriverService/TabletHandler.cpp @@ -156,18 +156,233 @@ void TabletHandler::ChangeTimerInterval(int newInterval) { // // Button helper functions // -bool IsButtonDown(UCHAR buttons, int buttonIndex) { +bool TabletHandler::IsButtonDown(UINT32 buttons, int buttonIndex) { return buttons & (1 << buttonIndex); } -bool IsButtonPressed(UCHAR buttons, UCHAR lastButtons, int buttonIndex) { +bool TabletHandler::IsButtonPressed(UINT32 buttons, UINT32 lastButtons, int buttonIndex) { return ((buttons & (1 << buttonIndex)) > 0 && (lastButtons & (1 << buttonIndex)) == 0); } -bool IsButtonReleased(UCHAR buttons, UCHAR lastButtons, int buttonIndex) { +bool TabletHandler::IsButtonReleased(UINT32 buttons, UINT32 lastButtons, int buttonIndex) { return ((buttons & (1 << buttonIndex)) == 0 && (lastButtons & (1 << buttonIndex)) > 0); } +// +// Process pen and auxiliary buttons +// +void TabletHandler::ProcessPenButtons(UINT32 *outButtons) { + ProcessButtons(outButtons, true); +} +void TabletHandler::ProcessAuxButtons() { + UINT32 tmpButtons = 0; + ProcessButtons(&tmpButtons, false); +} +void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) +{ + UINT32 buttons; + UINT32 lastButtons; + int buttonCount; + bool isDown; + bool isPressed; + bool isReleased; + bool hasBinding; + string key; + + // Pen buttons + if(isPen) { + buttons = tablet->state.inputButtons; + lastButtons = tablet->state.lastButtons; + buttonCount = tablet->settings.buttonCount; + } + + // Auxiliary buttons + else { + buttons = tablet->auxState.buttons; + lastButtons = tablet->auxState.lastButtons; + buttonCount = tablet->settings.auxButtonCount; + } + + + // Button state changed? + if(buttons > 0 || lastButtons > 0) { + + // Loop through buttons + for(int buttonIndex = 0; buttonIndex < buttonCount; buttonIndex++) { + + // Button is not down, pressed or released? + if((buttons & (1 << buttonIndex)) == 0 && (lastButtons & (1 << buttonIndex)) == 0) { + continue; + } + + // Button state + isDown = IsButtonDown(buttons, buttonIndex); + isPressed = IsButtonPressed(buttons, lastButtons, buttonIndex); + isReleased = IsButtonReleased(buttons, lastButtons, buttonIndex); + + // Pen button map + hasBinding = false; + if(isPen && tablet->settings.buttonMap[buttonIndex].size() > 0) { + key = tablet->settings.buttonMap[buttonIndex]; + if(inputEmulator.keyMap.count(key) > 0) { + hasBinding = true; + } + } + + // Auxiliary button map + else if(!isPen && tablet->settings.auxButtonMap[buttonIndex].size() > 0) { + key = tablet->settings.auxButtonMap[buttonIndex]; + if(inputEmulator.keyMap.count(key) > 0) { + hasBinding = true; + } + } + + // Button has a binding? + if(hasBinding) { + + InputEmulator::KeyMapValue *keyMapValue = inputEmulator.keyMap[key]; + + WORD virtualKey = keyMapValue->virtualKey; + int mouseButton = keyMapValue->mouseButton; + + // + // Mouse buttons + // + if(mouseButton >= InputEmulator::Mouse1 && mouseButton <= InputEmulator::Mouse5) { + + // Pen button + if(isPen && isDown) { + *outButtons |= (1 << (mouseButton - 1)); + } + + // Auxiliary button + else if(isPressed || isReleased) { + inputEmulator.MouseSet(mouseButton, isDown); + } + } + + // + // Keyboard key + // + if(virtualKey > 0) { + if(isPressed) { + inputEmulator.SetKeyState(virtualKey, true); + } + else if(isReleased) { + inputEmulator.SetKeyState(virtualKey, false); + } + } + + // + // Mouse scroll + // + if( + mouseButton == InputEmulator::MouseScrollVertical + || + mouseButton == InputEmulator::MouseScrollHorizontal + || + mouseButton == InputEmulator::MouseScrollBoth + || + mouseButton == InputEmulator::MediaVolumeControl + ) { + + // Scroll pen button down? + if( + isDown + && + // Scroll when tip is down + (!tablet->settings.scrollDrag || IsButtonDown(buttons, 0)) + ) { + + // Get rotated pen position + Vector2D scrollPosition; + scrollPosition.Set(tablet->state.position); + mapper->GetRotatedTabletPosition(&scrollPosition.x, &scrollPosition.y); + + // Reset last scroll position and set the scroll start position + if( + isPressed + || + (tablet->settings.scrollDrag && IsButtonPressed(buttons, lastButtons, 0)) + ) { + lastScrollPosition.Set(scrollPosition); + scrollStartPosition.Set(tablet->state.position); + + // + // Move normal mouse to digitizer position + // + if(outputManager->mode == OutputManager::ModeVMultiDigitizer) { + TabletState tmpState; + tmpState.position.Set(tablet->state.position); + outputManager->sendInputAbsolute.Set(&tmpState); + outputManager->sendInputAbsolute.Write(); + } + + } + + // Disable mouse tip button when using drag scroll + if(tablet->settings.scrollDrag) { + *outButtons &= ~1; + } + + // Delta from the last scroll position + Vector2D delta( + (scrollPosition.x - lastScrollPosition.x) * tablet->settings.scrollSensitivity, + (scrollPosition.y - lastScrollPosition.y) * tablet->settings.scrollSensitivity + ); + + // X Acceleration + if(delta.x > 0) + delta.x = round(pow(delta.x, tablet->settings.scrollAcceleration)); + else + delta.x = -round(pow(-delta.x, tablet->settings.scrollAcceleration)); + + // Y Acceleration + if(delta.y > 0) + delta.y = round(pow(delta.y, tablet->settings.scrollAcceleration)); + else + delta.y = -round(pow(-delta.y, tablet->settings.scrollAcceleration)); + + + // Vertical Scroll + if(delta.y != 0 && (mouseButton & InputEmulator::MouseScrollVertical) == InputEmulator::MouseScrollVertical) { + inputEmulator.MouseScroll((int)delta.y, true); + lastScrollPosition.Set(scrollPosition); + } + + // Horizontal scroll + if(delta.x != 0 && (mouseButton & InputEmulator::MouseScrollHorizontal) == InputEmulator::MouseScrollHorizontal) { + inputEmulator.MouseScroll((int)-delta.x, false); + lastScrollPosition.Set(scrollPosition); + } + + // Media volume control + if(delta.y != 0 && mouseButton == InputEmulator::MediaVolumeControl) { + inputEmulator.VolumeChange(-(float)delta.y / 100.0f); + lastScrollPosition.Set(scrollPosition); + } + + // Stop cursor + if(tablet->settings.scrollStopCursor) { + tablet->state.position.Set(scrollStartPosition); + } + + } + } + } + } + + // Set last buttons + if(isPen) { + tablet->state.lastButtons = buttons; + } + else { + tablet->auxState.lastButtons = buttons; + } + } +} + + // // Tablet input thread // @@ -179,11 +394,9 @@ void TabletHandler::RunTabletInputThread() { TabletState filterState; TabletState oldState; bool filterTimedEnabled; - UCHAR buttons = 0; - UCHAR outButtons = 0; - UCHAR lastButtons = 0; - Vector2D lastScrollPosition; - Vector2D scrollStartPosition; + UINT32 buttons = 0; + UINT32 lastButtons = 0; + UINT32 outButtons = 0; timeBegin = chrono::high_resolution_clock::now(); @@ -277,155 +490,13 @@ void TabletHandler::RunTabletInputThread() { tablet->state.buttons = 0; } - // - // Button map - // - buttons = tablet->state.buttons; + // Process buttons outButtons = 0; - - if(buttons > 0 || lastButtons > 0) { - - // Loop through buttons - for(int buttonIndex = 0; buttonIndex < tablet->settings.buttonCount; buttonIndex++) { - - // Button is not down, pressed or released? - if((buttons & (1 << buttonIndex)) == 0 && (lastButtons & (1 << buttonIndex)) == 0) { - continue; - } - - // Button state - bool isDown = IsButtonDown(buttons, buttonIndex); - bool isPressed = IsButtonPressed(buttons, lastButtons, buttonIndex); - bool isReleased = IsButtonReleased(buttons, lastButtons, buttonIndex); - - // Button is set - if(tablet->settings.buttonMap[buttonIndex].size() > 0) { - - string key = tablet->settings.buttonMap[buttonIndex]; - - if(inputEmulator.keyMap.count(key) > 0) { - InputEmulator::KeyMapValue *keyMapValue = inputEmulator.keyMap[key]; - - // Mouse buttons - if(keyMapValue->mouseButton > 0 && keyMapValue->mouseButton < 8) { - if(isDown) { - outButtons |= (1 << (keyMapValue->mouseButton - 1)); - } - } - - // - // Mouse scroll - // - else if((keyMapValue->mouseButton & 0x103) > 0) { - - // Scroll pen button down? - if( - isDown - && - - // Scroll when tip is down - (!tablet->settings.scrollDrag || IsButtonDown(buttons, 0)) - ) { - - // Get rotated pen position - Vector2D scrollPosition; - scrollPosition.Set(tablet->state.position); - mapper->GetRotatedTabletPosition(&scrollPosition.x, &scrollPosition.y); - - // Reset last scroll position and set the scroll start position - if(isPressed - || - (tablet->settings.scrollDrag && isDown && IsButtonPressed(buttons, lastButtons, 0)) - ) { - lastScrollPosition.Set(scrollPosition); - scrollStartPosition.Set(tablet->state.position); - - // - // Move normal mouse to digitizer position - // - if(outputManager->mode == OutputManager::ModeVMultiDigitizer) { - TabletState tmpState; - tmpState.position.Set(tablet->state.position); - outputManager->sendInputAbsolute.Set(&tmpState); - outputManager->sendInputAbsolute.Write(); - } - - } - - // Disable mouse tip button when using drag scroll - if(tablet->settings.scrollDrag) { - outButtons &= ~1; - } - - // Delta from the last scroll position - Vector2D delta( - (scrollPosition.x - lastScrollPosition.x) * tablet->settings.scrollSensitivity, - (scrollPosition.y - lastScrollPosition.y) * tablet->settings.scrollSensitivity - ); - - // X Acceleration - if(delta.x > 0) - delta.x = round(pow(delta.x, tablet->settings.scrollAcceleration)); - else - delta.x = -round(pow(-delta.x, tablet->settings.scrollAcceleration)); - - // Y Acceleration - if(delta.y > 0) - delta.y = round(pow(delta.y, tablet->settings.scrollAcceleration)); - else - delta.y = -round(pow(-delta.y, tablet->settings.scrollAcceleration)); - - - // Vertical Scroll - if(delta.y != 0 && (keyMapValue->mouseButton & 0x101) == 0x101) { - inputEmulator.MouseScroll((int)delta.y, true); - lastScrollPosition.Set(scrollPosition); - } - - // Horizontal scroll - if(delta.x != 0 && (keyMapValue->mouseButton & 0x102) == 0x102) { - inputEmulator.MouseScroll((int)-delta.x, false); - lastScrollPosition.Set(scrollPosition); - } - - // Stop cursor - if(tablet->settings.scrollStopCursor) { - tablet->state.position.Set(scrollStartPosition); - } - - } - } - - // Keyboard key - if(keyMapValue->virtualKey > 0) { - if(isPressed) { - inputEmulator.SetKeyState(keyMapValue->virtualKey, true); - } - else if(isReleased) { - inputEmulator.SetKeyState(keyMapValue->virtualKey, false); - } - } - - } - - // Keyboard keys - else { - if(isPressed) { - inputEmulator.SetInputStates(key, true); - } - else if(isReleased) { - inputEmulator.SetInputStates(key, false); - } - } - } - - } - } - - // Set button values + ProcessPenButtons(&outButtons); tablet->state.buttons = outButtons; - lastButtons = buttons; + // Reprocess auxiliary buttons (for mouse scroll) + ProcessAuxButtons(); // // Report filters @@ -495,6 +566,7 @@ void TabletHandler::RunTabletInputThread() { } + // // Auxiliary input thread (tablet buttons, etc.) // @@ -534,54 +606,17 @@ void TabletHandler::RunAuxInputThread() memcpy(&auxState, &tablet->auxState, sizeof(Tablet::TabletAuxState)); tablet->auxState.isValid = false; - // Buttons - buttons = auxState.buttons; - + // Process buttons if(logger.debugEnabled) { - LOG_DEBUG("Aux buttons: 0x%04X\n", buttons); - } - - // Loop through buttons - for(int button = 1; button <= tablet->settings.auxButtonCount; button++) { - - int buttonMask = 1 << (button - 1); - - // - // Button pressed - // - if((buttons & buttonMask) == buttonMask && (lastButtons & buttonMask) != buttonMask) { - - // Button mapped? - if(tablet->settings.auxButtonMap[button - 1].size() > 0) { - - // Set key down - inputEmulator.SetInputStates(tablet->settings.auxButtonMap[button - 1], true); - } - - } - - // - // Button released - // - else if((buttons & buttonMask) != buttonMask && (lastButtons & buttonMask) == buttonMask) { - - // Button mapped? - if(tablet->settings.auxButtonMap[button - 1].size() > 0) { - - // Set key up - inputEmulator.SetInputStates(tablet->settings.auxButtonMap[button - 1], false); - } - - } - + LOG_DEBUG("Aux buttons: 0x%04X\n", auxState.buttons); } - - lastButtons = buttons; + ProcessAuxButtons(); } } + // // Timer tick // @@ -656,6 +691,7 @@ void TabletHandler::OnTimerTick() { isTimerTickRunning = false; } + // // Write output state with output manager // diff --git a/TabletDriverService/TabletHandler.h b/TabletDriverService/TabletHandler.h index c465ab7..8dd339e 100644 --- a/TabletDriverService/TabletHandler.h +++ b/TabletDriverService/TabletHandler.h @@ -5,6 +5,16 @@ #include "InputEmulator.h" class TabletHandler { +private: + Vector2D lastScrollPosition; + Vector2D scrollStartPosition; + void ProcessPenButtons(UINT32 *outButtons); + void ProcessAuxButtons(); + void ProcessButtons(UINT32 *outButtons, bool isPen); + bool IsButtonDown(UINT32 buttons, int buttonIndex); + bool IsButtonPressed(UINT32 buttons, UINT32 lastButtons, int buttonIndex); + bool IsButtonReleased(UINT32 buttons, UINT32 lastButtons, int buttonIndex); + public: Tablet *tablet; thread *tabletInputThread; @@ -33,6 +43,7 @@ class TabletHandler { void RunAuxInputThread(); void OnTimerTick(); void WriteOutputState(TabletState *outputState); + private: diff --git a/TabletDriverService/TabletState.h b/TabletDriverService/TabletState.h index 951fedd..f730b67 100644 --- a/TabletDriverService/TabletState.h +++ b/TabletDriverService/TabletState.h @@ -13,6 +13,7 @@ class TabletState { double inputVelocity; unsigned char buttons; + unsigned char lastButtons; Vector2D position; double pressure; From 1318afa9b237c63ae67c1a3159ccb08fe3c4f13e Mon Sep 17 00:00:00 2001 From: hawku Date: Thu, 3 Jan 2019 19:09:44 +0200 Subject: [PATCH 76/83] Drag scroll fix --- TabletDriverService/TabletHandler.cpp | 54 ++++++++++++++------------- TabletDriverService/TabletHandler.h | 2 +- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/TabletDriverService/TabletHandler.cpp b/TabletDriverService/TabletHandler.cpp index c3c190a..43bd138 100644 --- a/TabletDriverService/TabletHandler.cpp +++ b/TabletDriverService/TabletHandler.cpp @@ -174,12 +174,13 @@ bool TabletHandler::IsButtonReleased(UINT32 buttons, UINT32 lastButtons, int but void TabletHandler::ProcessPenButtons(UINT32 *outButtons) { ProcessButtons(outButtons, true); } -void TabletHandler::ProcessAuxButtons() { - UINT32 tmpButtons = 0; - ProcessButtons(&tmpButtons, false); +void TabletHandler::ProcessAuxButtons(UINT32 *outButtons) { + ProcessButtons(outButtons, false); } void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) { + UINT32 penButtons = 0; + UINT32 penLastButtons = 0; UINT32 buttons; UINT32 lastButtons; int buttonCount; @@ -188,11 +189,15 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) bool isReleased; bool hasBinding; string key; + bool scrolled = false; + Vector2D scrollPosition; // Pen buttons + penButtons = tablet->state.inputButtons; + penLastButtons = tablet->state.lastButtons; if(isPen) { - buttons = tablet->state.inputButtons; - lastButtons = tablet->state.lastButtons; + buttons = penButtons; + lastButtons = penLastButtons; buttonCount = tablet->settings.buttonCount; } @@ -203,7 +208,6 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) buttonCount = tablet->settings.auxButtonCount; } - // Button state changed? if(buttons > 0 || lastButtons > 0) { @@ -286,16 +290,14 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) mouseButton == InputEmulator::MediaVolumeControl ) { - // Scroll pen button down? - if( - isDown + // Scroll button down? + if(isDown && - // Scroll when tip is down - (!tablet->settings.scrollDrag || IsButtonDown(buttons, 0)) + // Scroll when tip is down? + (!tablet->settings.scrollDrag || IsButtonDown(penButtons, 0)) ) { // Get rotated pen position - Vector2D scrollPosition; scrollPosition.Set(tablet->state.position); mapper->GetRotatedTabletPosition(&scrollPosition.x, &scrollPosition.y); @@ -303,7 +305,7 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) if( isPressed || - (tablet->settings.scrollDrag && IsButtonPressed(buttons, lastButtons, 0)) + (tablet->settings.scrollDrag && IsButtonPressed(penButtons, penLastButtons, 0)) ) { lastScrollPosition.Set(scrollPosition); scrollStartPosition.Set(tablet->state.position); @@ -347,19 +349,19 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) // Vertical Scroll if(delta.y != 0 && (mouseButton & InputEmulator::MouseScrollVertical) == InputEmulator::MouseScrollVertical) { inputEmulator.MouseScroll((int)delta.y, true); - lastScrollPosition.Set(scrollPosition); + scrolled = true; } // Horizontal scroll if(delta.x != 0 && (mouseButton & InputEmulator::MouseScrollHorizontal) == InputEmulator::MouseScrollHorizontal) { inputEmulator.MouseScroll((int)-delta.x, false); - lastScrollPosition.Set(scrollPosition); + scrolled = true; } // Media volume control if(delta.y != 0 && mouseButton == InputEmulator::MediaVolumeControl) { inputEmulator.VolumeChange(-(float)delta.y / 100.0f); - lastScrollPosition.Set(scrollPosition); + scrolled = true; } // Stop cursor @@ -370,15 +372,14 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) } } } - } - // Set last buttons - if(isPen) { - tablet->state.lastButtons = buttons; } - else { - tablet->auxState.lastButtons = buttons; + + // Update last scroll position + if(scrolled) { + lastScrollPosition.Set(scrollPosition); } + } } @@ -493,11 +494,10 @@ void TabletHandler::RunTabletInputThread() { // Process buttons outButtons = 0; ProcessPenButtons(&outButtons); + ProcessAuxButtons(&outButtons); + tablet->state.lastButtons = tablet->state.inputButtons; tablet->state.buttons = outButtons; - // Reprocess auxiliary buttons (for mouse scroll) - ProcessAuxButtons(); - // // Report filters // @@ -610,7 +610,9 @@ void TabletHandler::RunAuxInputThread() if(logger.debugEnabled) { LOG_DEBUG("Aux buttons: 0x%04X\n", auxState.buttons); } - ProcessAuxButtons(); + UINT32 tmpButtons = 0; + ProcessAuxButtons(&tmpButtons); + tablet->auxState.lastButtons = tablet->auxState.buttons; } diff --git a/TabletDriverService/TabletHandler.h b/TabletDriverService/TabletHandler.h index 8dd339e..51a4a2b 100644 --- a/TabletDriverService/TabletHandler.h +++ b/TabletDriverService/TabletHandler.h @@ -9,7 +9,7 @@ class TabletHandler { Vector2D lastScrollPosition; Vector2D scrollStartPosition; void ProcessPenButtons(UINT32 *outButtons); - void ProcessAuxButtons(); + void ProcessAuxButtons(UINT32 *outButtons); void ProcessButtons(UINT32 *outButtons, bool isPen); bool IsButtonDown(UINT32 buttons, int buttonIndex); bool IsButtonPressed(UINT32 buttons, UINT32 lastButtons, int buttonIndex); From 3c4faaf2bc859f46646b65ee5e2cce43657ba347 Mon Sep 17 00:00:00 2001 From: hawku Date: Thu, 3 Jan 2019 23:40:33 +0200 Subject: [PATCH 77/83] Thread safety, named pipe fixes, audio balance, etc. - Improved thread safety - Some fixes for named pipes - Added ability to use the pen to control audio left/right balance - Some minor bug fixes --- TabletDriverGUI/TabletDriver.cs | 287 +++++++++--------- TabletDriverGUI/WindowButtonMapping.xaml.cs | 1 + TabletDriverService/CommandHandler.Device.cpp | 4 +- .../CommandHandler.Filters.cpp | 24 ++ TabletDriverService/CommandHandler.Other.cpp | 7 +- TabletDriverService/CommandHandler.Tablet.cpp | 2 +- TabletDriverService/HIDDevice.cpp | 5 +- TabletDriverService/InputEmulator.cpp | 56 ++++ TabletDriverService/InputEmulator.h | 2 + TabletDriverService/Logger.cpp | 27 +- TabletDriverService/Logger.h | 7 +- TabletDriverService/Main.cpp | 17 +- TabletDriverService/OutputDummy.cpp | 2 +- .../OutputSendInputAbsolute.cpp | 2 +- .../OutputSendInputRelative.cpp | 2 +- TabletDriverService/OutputVMultiAbsolute.cpp | 2 +- TabletDriverService/OutputVMultiDigitizer.cpp | 2 +- .../OutputVMultiDigitizerRelative.cpp | 2 +- TabletDriverService/OutputVMultiRelative.cpp | 2 +- TabletDriverService/PipeHandler.cpp | 82 +++-- TabletDriverService/PipeHandler.h | 1 + TabletDriverService/Tablet.cpp | 4 +- .../TabletDriverService.vcxproj.user | 2 +- .../TabletFilterAdvancedSmoothing.cpp | 2 +- .../TabletFilterAntiSmoothing.cpp | 6 +- TabletDriverService/TabletFilterGravity.cpp | 2 +- .../TabletFilterNoiseReduction.cpp | 4 +- TabletDriverService/TabletFilterSmoothing.cpp | 2 +- TabletDriverService/TabletFilterTester.cpp | 6 +- TabletDriverService/TabletHandler.cpp | 37 ++- TabletDriverService/USBDevice.cpp | 9 +- TabletDriverService/VMulti.cpp | 2 +- TabletDriverService/stdafx.h | 3 + 33 files changed, 401 insertions(+), 214 deletions(-) diff --git a/TabletDriverGUI/TabletDriver.cs b/TabletDriverGUI/TabletDriver.cs index 1eb55ff..5963936 100644 --- a/TabletDriverGUI/TabletDriver.cs +++ b/TabletDriverGUI/TabletDriver.cs @@ -50,15 +50,13 @@ public DriverEventArgs(DriverEventType type, string message, string parameters) // Pipe System.Threading.Thread pipeInputThread = null; + System.Threading.Thread pipeOutputThread = null; System.Threading.Thread pipeStateThread = null; NamedPipeClientStream pipeStreamInput; NamedPipeClientStream pipeStreamOutput; NamedPipeClientStream pipeStreamState; - StreamReader pipeInputReader = null; - StreamWriter pipeOutputWriter = null; - BinaryReader pipeStateReader = null; [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] [Serializable] @@ -85,7 +83,24 @@ public struct TabletState private Process processService; private Timer timerWatchdog; private bool running; - public bool IsRunning { get { return running; } } + private readonly object locker = new object(); + public bool IsRunning + { + get + { + lock (locker) + { + return running; + } + } + set + { + lock (locker) + { + running = value; + } + } + } // // Constructor @@ -111,7 +126,7 @@ public TabletDriver(string servicePath) // private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e) { - if (running) + if (IsRunning) { //Console.WriteLine("ID: " + processDriver.Id); if (processService.HasExited) @@ -141,7 +156,7 @@ private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e) // public void SendCommand(string line) { - if (running) + if (IsRunning) { processService.StandardInput.WriteLine(line); } @@ -152,13 +167,12 @@ public void SendCommand(string line) // public void SendPipeCommand(string line) { - if (running) + if (IsRunning) { - if (pipeOutputWriter != null) - { - pipeOutputWriter.WriteLine(line); - pipeOutputWriter.Flush(); - } + + byte[] buffer = Encoding.UTF8.GetBytes(line); + pipeStreamOutput.Write(buffer, 0, buffer.Length); + pipeStreamOutput.Flush(); } } @@ -167,15 +181,19 @@ public void SendPipeCommand(string line) // public void ConsoleAddLine(string line) { - mutexConsoleUpdate.WaitOne(); - ConsoleBuffer.Add(line); - HasConsoleUpdated = true; + try + { + mutexConsoleUpdate.WaitOne(); + ConsoleBuffer.Add(line); + HasConsoleUpdated = true; - // Limit console buffer size - if (ConsoleBuffer.Count >= ConsoleMaxLines) - ConsoleBuffer.RemoveRange(0, ConsoleBuffer.Count - ConsoleMaxLines); + // Limit console buffer size + if (ConsoleBuffer.Count >= ConsoleMaxLines) + ConsoleBuffer.RemoveRange(0, ConsoleBuffer.Count - ConsoleMaxLines); + } + catch (Exception) { } - mutexConsoleUpdate.ReleaseMutex(); + try { mutexConsoleUpdate.ReleaseMutex(); } catch (Exception) { } } @@ -356,7 +374,7 @@ private void ProcessService_OutputDataReceived(object sender, DataReceivedEventA { if (e.Data == null) { - if (running) + if (IsRunning) Stop(); } else @@ -411,50 +429,44 @@ private void ProcessService_OutputDataReceived(object sender, DataReceivedEventA private void RunPipeInputThread() { StringBuilder stringBuilder = new StringBuilder(); - char[] buffer = new char[1024]; + byte[] buffer = new byte[1024]; - while (running) + while (IsRunning) { - pipeStreamInput = new NamedPipeClientStream(".", "TabletDriverOutput", PipeDirection.InOut); - pipeStreamOutput = new NamedPipeClientStream(".", "TabletDriverInput", PipeDirection.InOut); + pipeStreamInput = new NamedPipeClientStream(".", "TabletDriverOutput", PipeDirection.InOut, PipeOptions.Asynchronous); - //ConsoleAddLine("Connecting..."); - try + Console.WriteLine("Input pipe connecting..."); + try { pipeStreamInput.Connect(); } + catch (Exception ex) { - pipeStreamInput.Connect(); - pipeStreamOutput.Connect(); - } - catch (Exception) - { - // ConsoleAddLine("Input pipe connect error! " + ex.Message); + Console.WriteLine("Input pipe connect error! " + ex.Message); break; } - //ConsoleAddLine("Connected!"); - - pipeInputReader = new StreamReader(pipeStreamInput); - pipeOutputWriter = new StreamWriter(pipeStreamOutput); + Console.WriteLine("Input pipe connected!"); - - while (running && pipeStreamInput.IsConnected) + // + // Input thread read loop + // + while (IsRunning && pipeStreamInput.IsConnected) { - //ConsoleAddLine("Read!"); + //Console.WriteLine("Input pipe read!"); int bytesRead = 0; try { - bytesRead = pipeInputReader.Read(buffer, 0, buffer.Length); + bytesRead = pipeStreamInput.Read(buffer, 0, buffer.Length); } catch (Exception) { - // ConsoleAddLine("Can't read input pipe!"); + Console.WriteLine("Can't read input pipe!"); break; } + //Console.WriteLine("Input pipe read length: " + bytesRead); if (bytesRead == 0) continue; - //ConsoleAddLine("Read: " + bytesRead); for (int i = 0; i < bytesRead; i++) { - char c = buffer[i]; + char c = (char)buffer[i]; if (c == '\n' || c == '\r' || c == 0) { if (stringBuilder.Length > 0) @@ -471,17 +483,9 @@ private void RunPipeInputThread() } + Console.WriteLine("Input pipe disconnected!"); ConsoleAddLine("Input pipe disconnected!"); - // Close input reader - try - { - pipeInputReader.Close(); - pipeInputReader.Dispose(); - pipeInputReader = null; - } - catch (Exception) { } - // Close input stream try { @@ -491,16 +495,40 @@ private void RunPipeInputThread() } catch (Exception) { } + } + } - // Close output writer - try + + // + // Pipe input thread + // + private void RunPipeOutputThread() + { + StringBuilder stringBuilder = new StringBuilder(); + char[] buffer = new char[1024]; + + + while (IsRunning) + { + pipeStreamOutput = new NamedPipeClientStream(".", "TabletDriverInput", PipeDirection.InOut, PipeOptions.Asynchronous); + + Console.WriteLine("Output pipe connecting..."); + try { pipeStreamOutput.Connect(); } + catch (Exception ex) { - pipeOutputWriter.Close(); - pipeOutputWriter.Dispose(); - pipeOutputWriter = null; + Console.WriteLine("Output pipe connect error! " + ex.Message); + break; } - catch (Exception) { } + Console.WriteLine("Output pipe connected!"); + + // Wait for pipe to disconnect + while (IsRunning && pipeStreamOutput.IsConnected) + { + System.Threading.Thread.Sleep(100); + } + + Console.WriteLine("Output pipe disconnected!"); // Close output stream try @@ -525,61 +553,58 @@ private void RunPipeStateThread() byte[] bytes; GCHandle gcHandle; TabletState readState; + size = Marshal.SizeOf(typeof(TabletState)); + bytes = new byte[Marshal.SizeOf(typeof(TabletState))]; - while (running) + + while (IsRunning) { - if (!running) break; - pipeStreamState = new NamedPipeClientStream(".", "TabletDriverState", PipeDirection.InOut); + pipeStreamState = new NamedPipeClientStream(".", "TabletDriverState", PipeDirection.InOut, PipeOptions.Asynchronous); + Console.WriteLine("State pipe connecting..."); try { pipeStreamState.Connect(); } - catch (Exception) + catch (Exception ex) { - // ConsoleAddLine("State pipe connection error! " + ex.Message); + Console.WriteLine("State pipe connection error! " + ex.Message); break; } - pipeStateReader = new BinaryReader(pipeStreamState); + Console.WriteLine("State pipe connected!"); - while (running && pipeStreamState.IsConnected) + // + // State pipe read loop + // + while (IsRunning && pipeStreamState.IsConnected) { + //Console.WriteLine("Reading state pipe!"); try { + // Read + if (pipeStreamState.Read(bytes, 0, bytes.Length) == size) + { + + // Convert bytes to TabletState + gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + readState = (TabletState)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(TabletState)); + gcHandle.Free(); + tabletState = readState; - size = Marshal.SizeOf(typeof(TabletState)); - bytes = pipeStateReader.ReadBytes(size); - gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - readState = (TabletState)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(TabletState)); - gcHandle.Free(); + } } - catch (Exception) + catch (Exception ex) { - //ConsoleAddLine("Can't read state pipe! " + ex.Message); + Console.WriteLine("Can't read state pipe! " + ex.Message); size = 0; break; } - if (size > 0) - { - tabletState = readState; - //ConsoleAddLine("State X: " + tabletState.inputX + ", Y: " + tabletState.inputY); - } } - ConsoleAddLine("State pipe disconnected!"); - - // Close state reader - try - { - pipeStateReader.Close(); - pipeStateReader.Dispose(); - pipeStateReader = null; - } - catch (Exception) { } - + Console.WriteLine("State pipe disconnected!"); // Close state stream try @@ -646,13 +671,17 @@ public void Start(string processPath, string arguments) { } - running = true; + IsRunning = true; timerWatchdog.Start(); // Pipe input thread pipeInputThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeInputThread)); pipeInputThread.Start(); + // Pipe output thread + pipeOutputThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeOutputThread)); + pipeOutputThread.Start(); + // Pipe state thread pipeStateThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeStateThread)); pipeStateThread.Start(); @@ -681,22 +710,13 @@ public void Start(string processPath, string arguments) // public void Stop() { - if (!running) return; - running = false; + if (!IsRunning) return; timerWatchdog.Stop(); + IsRunning = false; - // Close pipe reader - try - { - if (pipeInputReader != null) - { - pipeInputReader.Close(); - pipeInputReader.Dispose(); - } - } - catch (Exception e) { Debug.WriteLine("Pipe input reader error! " + e.Message); } // Close pipe input stream + Console.WriteLine("Closing input pipe stream"); try { if (pipeStreamInput != null) @@ -705,22 +725,11 @@ public void Stop() pipeStreamInput.Dispose(); } } - catch (Exception e) { Debug.WriteLine("Pipe input stream error! " + e.Message); } - - // Close pipe writer - try - { - if (pipeOutputWriter != null) - { - pipeOutputWriter.Close(); - pipeOutputWriter.Dispose(); - } - - } - catch (Exception e) { Debug.WriteLine("Pipe writer error! " + e.Message); } + catch (Exception e) { Console.WriteLine("Pipe input stream error! " + e.Message); } // Close pipe output stream + Console.WriteLine("Closing output pipe stream"); try { if (pipeStreamOutput != null) @@ -729,22 +738,11 @@ public void Stop() pipeStreamOutput.Dispose(); } } - catch (Exception e) { Debug.WriteLine("Pipe output stream error! " + e.Message); } + catch (Exception e) { Console.WriteLine("Pipe output stream error! " + e.Message); } - // Close state reader - try - { - if (pipeStateReader != null) - { - pipeStateReader.Close(); - pipeStateReader.Dispose(); - } - - } - catch (Exception e) { Debug.WriteLine("Pipe state reader error! " + e.Message); } - // Close pipe state stream + Console.WriteLine("Closing state pipe stream"); try { if (pipeStreamState != null) @@ -753,29 +751,39 @@ public void Stop() pipeStreamState.Dispose(); } } - catch (Exception e) { Debug.WriteLine("Pipe state stream error! " + e.Message); } + catch (Exception e) { Console.WriteLine("Pipe state stream error! " + e.Message); } - // Close pipe input thread + // Close input pipe thread + Console.WriteLine("Closing input pipe thread"); try { - if (pipeInputThread != null) - { - pipeInputThread.Abort(); - } + pipeInputThread.Abort(); + pipeInputThread.Join(1000); } - catch (Exception e) { Debug.WriteLine("Pipe input thread error! " + e.Message); } + catch (Exception e) { Console.WriteLine("Pipe input thread error! " + e.Message); } + + + // Close output pipe thread + Console.WriteLine("Closing output pipe thread"); + try + { + pipeOutputThread.Abort(); + pipeOutputThread.Join(1000); + } + catch (Exception e) { Console.WriteLine("Pipe output thread error! " + e.Message); } // Close pipe state thread + Console.WriteLine("Closing state pipe thread"); try { - if (pipeStateThread != null) - { - pipeStateThread.Abort(); - } + pipeStateThread.Abort(); + pipeStateThread.Join(1000); } - catch (Exception e) { Debug.WriteLine("Pipe state thread error! " + e.Message); } + catch (Exception e) { Console.WriteLine("Pipe state thread error! " + e.Message); } + // Kill service process + Console.WriteLine("Killing TabletDriverService"); try { processService.CancelOutputRead(); @@ -786,6 +794,7 @@ public void Stop() { Debug.WriteLine("Service process error! " + e.Message); } + Stopped?.Invoke(this, new EventArgs()); diff --git a/TabletDriverGUI/WindowButtonMapping.xaml.cs b/TabletDriverGUI/WindowButtonMapping.xaml.cs index 15ba082..086785e 100644 --- a/TabletDriverGUI/WindowButtonMapping.xaml.cs +++ b/TabletDriverGUI/WindowButtonMapping.xaml.cs @@ -79,6 +79,7 @@ public WindowButtonMapping() { "VOLUMEDOWN5.0", new ButtonBinding("Volume Down 5.0%") }, { "VOLUMEMUTE", new ButtonBinding("Volume Mute") }, { "VOLUMECONTROL", new ButtonBinding("Volume Control Up/Down") }, + { "BALANCECONTROL", new ButtonBinding("Balance Control Left/Right") }, { "MEDIANEXT", new ButtonBinding("Media Next Track") }, { "MEDIAPREV", new ButtonBinding("Media Previous Track") }, { "MEDIASTOP", new ButtonBinding("Media Stop") }, diff --git a/TabletDriverService/CommandHandler.Device.cpp b/TabletDriverService/CommandHandler.Device.cpp index 3b5f230..f60c834 100644 --- a/TabletDriverService/CommandHandler.Device.cpp +++ b/TabletDriverService/CommandHandler.Device.cpp @@ -149,9 +149,7 @@ void CommandHandler::CreateDeviceCommands() { HIDDevice *hid = new HIDDevice(); hid->debugEnabled = true; hid->OpenDevice(&hidHandle, 1, 1, 1, 1); - if(hidHandle > 0) { - CloseHandle(hidHandle); - } + SAFE_CLOSE_HANDLE(hidHandle); delete hid; return true; diff --git a/TabletDriverService/CommandHandler.Filters.cpp b/TabletDriverService/CommandHandler.Filters.cpp index ee7f6c8..720ad3a 100644 --- a/TabletDriverService/CommandHandler.Filters.cpp +++ b/TabletDriverService/CommandHandler.Filters.cpp @@ -16,6 +16,8 @@ void CommandHandler::CreateFilterCommands() { // AddAlias("Smoothing", "SmoothingFilter"); AddCommand(new Command("SmoothingFilter", [&](CommandLine *cmd) { + + // Tablet valid? if(!ExecuteCommand("TabletValid")) return false; double latency = cmd->GetDouble(0, tablet->smoothing.GetLatency()); @@ -65,7 +67,10 @@ void CommandHandler::CreateFilterCommands() { // AddAlias("AdvancedSmoothing", "AdvancedSmoothingFilter"); AddCommand(new Command("AdvancedSmoothingFilter", [&](CommandLine *cmd) { + + // Tablet valid? if(!ExecuteCommand("TabletValid")) return false; + tablet->advancedSmoothing.isEnabled = cmd->GetBoolean(0, tablet->advancedSmoothing.isEnabled); tablet->advancedSmoothing.ClearSettings(); @@ -85,6 +90,8 @@ void CommandHandler::CreateFilterCommands() { // Sets advanced smoothing filter parameters // AddCommand(new Command("AdvancedSmoothingAdd", [&](CommandLine *cmd) { + + // Tablet valid? if(!ExecuteCommand("TabletValid")) return false; if(cmd->valueCount >= 3) { @@ -109,7 +116,9 @@ void CommandHandler::CreateFilterCommands() { AddAlias("Gravity", "GravityFilter"); AddCommand(new Command("GravityFilter", [&](CommandLine *cmd) { + // Tablet valid? if(!ExecuteCommand("TabletValid")) return false; + double gravity = cmd->GetDouble(0, tablet->gravityFilter.gravity); double friction = cmd->GetDouble(1, 0); double pressureGravity = cmd->GetDouble(2, 0); @@ -157,7 +166,9 @@ void CommandHandler::CreateFilterCommands() { AddAlias("Noise", "NoiseReduction"); AddCommand(new Command("NoiseReduction", [&](CommandLine *cmd) { + // Tablet valid? if(!ExecuteCommand("TabletValid")) return false; + string stringValue = cmd->GetStringLower(0, ""); // Off / False @@ -232,6 +243,9 @@ void CommandHandler::CreateFilterCommands() { AddAlias("AntiSmoothing", "AntiSmoothingFilter"); AddAlias("Anti", "AntiSmoothingFilter"); AddCommand(new Command("AntiSmoothingFilter", [&](CommandLine *cmd) { + + // Tablet valid? + if(!ExecuteCommand("TabletValid")) return false; string stringValue = cmd->GetStringLower(0, ""); @@ -292,6 +306,9 @@ void CommandHandler::CreateFilterCommands() { AddAlias("AntiAdd", "AntiSmoothingAdd"); AddCommand(new Command("AntiSmoothingAdd", [&](CommandLine *cmd) { + // Tablet valid? + if(!ExecuteCommand("TabletValid")) return false; + double velocity = cmd->GetDouble(0, 0); double shape = cmd->GetDouble(1, 0.5); double compensation = cmd->GetDouble(2, 0); @@ -322,6 +339,10 @@ void CommandHandler::CreateFilterCommands() { AddAlias("Interval", "FilterTimerInterval"); AddAlias("TimerInterval", "FilterTimerInterval"); AddCommand(new Command("FilterTimerInterval", [&](CommandLine *cmd) { + + // Tablet valid? + if(!ExecuteCommand("TabletValid")) return false; + if(tabletHandler == NULL) return true; int oldInterval = (int)round(tabletHandler->timerInterval); @@ -350,6 +371,9 @@ void CommandHandler::CreateFilterCommands() { AddAlias("Tester", "FilterTester"); AddCommand(new Command("FilterTester", [&](CommandLine *cmd) { + // Tablet valid? + if(!ExecuteCommand("TabletValid")) return false; + string inputFilepath = cmd->GetString(0, "tester_input.txt"); string outputFilepath = cmd->GetString(1, "tester_output.txt"); TabletFilterAntiSmoothing *filterAntiSmoothing = NULL; diff --git a/TabletDriverService/CommandHandler.Other.cpp b/TabletDriverService/CommandHandler.Other.cpp index 61a21c1..f08ca92 100644 --- a/TabletDriverService/CommandHandler.Other.cpp +++ b/TabletDriverService/CommandHandler.Other.cpp @@ -329,7 +329,9 @@ void CommandHandler::CreateOtherCommands() { // AddCommand(new Command("StateOutput", [&](CommandLine *cmd) { if(!ExecuteCommand("TabletValid")) return false; + pipeHandler->lock.lock(); pipeHandler->isStateOutputEnabled = cmd->GetBoolean(0, pipeHandler->isStateOutputEnabled); + pipeHandler->lock.unlock(); LOG_INFO("State output = %s\n", pipeHandler->isStateOutputEnabled ? "Enabled" : "Disabled"); return true; })); @@ -342,8 +344,9 @@ void CommandHandler::CreateOtherCommands() { // AddCommand(new Command("Debug", [&](CommandLine *cmd) { if(!ExecuteCommand("TabletValid")) return false; - logger.debugEnabled = cmd->GetBoolean(0, logger.debugEnabled); - LOG_INFO("Debug logging = %s\n", logger.debugEnabled ? "True" : "False"); + bool debugEnabled = cmd->GetBoolean(0, logger.IsDebugOutputEnabled()); + logger.SetDebugOutput(debugEnabled); + LOG_INFO("Debug logging = %s\n", logger.IsDebugOutputEnabled() ? "True" : "False"); return true; })); diff --git a/TabletDriverService/CommandHandler.Tablet.cpp b/TabletDriverService/CommandHandler.Tablet.cpp index 0001f7e..e69f64a 100644 --- a/TabletDriverService/CommandHandler.Tablet.cpp +++ b/TabletDriverService/CommandHandler.Tablet.cpp @@ -28,7 +28,7 @@ void CommandHandler::CreateTabletCommands() { 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"); - CleanupAndExit(1); + CleanupAndExit(0); return false; } return true; diff --git a/TabletDriverService/HIDDevice.cpp b/TabletDriverService/HIDDevice.cpp index 703c231..0f771e8 100644 --- a/TabletDriverService/HIDDevice.cpp +++ b/TabletDriverService/HIDDevice.cpp @@ -204,7 +204,7 @@ bool HIDDevice::OpenDevice(HANDLE *handle, USHORT vendorId, USHORT productId, US // Close the HID handle if the device is incorrect else { - CloseHandle(deviceHandle); + SAFE_CLOSE_HANDLE(deviceHandle); } // Free HID preparsed data @@ -340,8 +340,7 @@ void HIDDevice::CloseDevice() { 0, NULL); - CloseHandle(_deviceHandle); - _deviceHandle = NULL; + SAFE_CLOSE_HANDLE(_deviceHandle); } catch(exception) { _deviceHandle = NULL; } diff --git a/TabletDriverService/InputEmulator.cpp b/TabletDriverService/InputEmulator.cpp index 636b5c9..d01ca06 100644 --- a/TabletDriverService/InputEmulator.cpp +++ b/TabletDriverService/InputEmulator.cpp @@ -44,6 +44,7 @@ void InputEmulator::CreateKeyMap() // Media volume control AddKey("VOLUMECONTROL", "Media Volume Control", 0, MouseButtons::MediaVolumeControl); + AddKey("BALANCECONTROL", "Media Balance Control", 0, MouseButtons::MediaBalanceControl); // Shift AddKey("SHIFT", "Shift", VK_SHIFT); @@ -623,6 +624,61 @@ void InputEmulator::VolumeSet(float volume) ReleaseEndpointVolume(); } +// +// Set main audio device left/right balance +// +void InputEmulator::VolumeBalance(float newBalance) +{ + UINT channelCount = 0; + float levelLeft = -1; + float levelRight = -1; + + // Get endpoint volume + if(CreateEndpointVolume()) { + + // Channel count + pAudioEndpointVolume->GetChannelCount(&channelCount); + if(channelCount >= 2) { + + // Get channel levels + pAudioEndpointVolume->GetChannelVolumeLevelScalar(0, &levelLeft); + pAudioEndpointVolume->GetChannelVolumeLevelScalar(1, &levelRight); + + // Levels valid? + if(levelLeft >= 0 || levelRight >= 0) { + + // Calculate balance + float levelSum = levelLeft + levelRight; + + // Limit resolution + newBalance = round(newBalance * 100.0f) / 100.0f; + if(newBalance < 0.0f) newBalance = 0.0f; + else if(newBalance > 1.0f) newBalance = 1.0f; + + // Set channel level values + levelLeft = levelSum * newBalance; + levelRight = levelSum * (1 - newBalance); + + // Limit levels + if(levelLeft < 0) levelLeft = 0; + else if(levelLeft > 1) levelLeft = 1; + if(levelRight < 0) levelRight = 0; + else if(levelRight > 1) levelRight = 1; + + // Debug message + if(logger.IsDebugOutputEnabled()) { + LOG_DEBUG("Audio balance: Left %0.5f <- %0.3f -> %0.5f Right\n", levelLeft, newBalance, levelRight); + } + + // Set endpoint volume channel levels + pAudioEndpointVolume->SetChannelVolumeLevelScalar(0, levelLeft, &GUID_NULL); + pAudioEndpointVolume->SetChannelVolumeLevelScalar(1, levelRight, &GUID_NULL); + } + } + } + ReleaseEndpointVolume(); +} + // // Get main audio device volume // diff --git a/TabletDriverService/InputEmulator.h b/TabletDriverService/InputEmulator.h index e0c05fb..b13e7f2 100644 --- a/TabletDriverService/InputEmulator.h +++ b/TabletDriverService/InputEmulator.h @@ -47,6 +47,7 @@ class InputEmulator MouseScrollHorizontal = 0x102, MouseScrollBoth = 0x103, MediaVolumeControl = 0x110, + MediaBalanceControl = 0x111, }; class KeyMapValue { @@ -84,6 +85,7 @@ class InputEmulator bool CreateEndpointVolume(); bool ReleaseEndpointVolume(); void VolumeSet(float volume); + void VolumeBalance(float leftRight); float VolumeGet(); void VolumeChange(float delta); diff --git a/TabletDriverService/Logger.cpp b/TabletDriverService/Logger.cpp index 90c77cd..7a0fdba 100644 --- a/TabletDriverService/Logger.cpp +++ b/TabletDriverService/Logger.cpp @@ -8,7 +8,7 @@ Logger::Logger() { verbosity = LogLevelDebug; newMessage = false; directPrint = false; - debugEnabled = false; + isDebugOutputEnabled = false; pipeHandle = NULL; } @@ -298,6 +298,31 @@ bool Logger::CloseLogFile() { } +// +// Enable/disable debug output +// +void Logger::SetDebugOutput(bool enabled) +{ + lock.lock(); + isDebugOutputEnabled = enabled; + lock.unlock(); + +} + +// +// Check if the debug output is enabled +// +bool Logger::IsDebugOutputEnabled() +{ + bool enabled = false; + lock.lock(); + enabled = isDebugOutputEnabled; + lock.unlock(); + return enabled; +} + + + // // Start logger thread // diff --git a/TabletDriverService/Logger.h b/TabletDriverService/Logger.h index 57f4590..b1287fd 100644 --- a/TabletDriverService/Logger.h +++ b/TabletDriverService/Logger.h @@ -60,6 +60,7 @@ class Logger { bool newMessage; ofstream logFile; bool logToFile; + bool isDebugOutputEnabled; public: enum LogLevels { @@ -94,9 +95,9 @@ class Logger { }; bool isRunning; bool directPrint; - bool debugEnabled; string logFilename = ""; HANDLE pipeHandle; + mutex lock; void OutputMessage(LogItem *message); void ProcessMessages(); @@ -109,6 +110,10 @@ class Logger { void Stop(); bool OpenLogFile(string filename); bool CloseLogFile(); + + void SetDebugOutput(bool enabled); + bool IsDebugOutputEnabled(); + }; extern Logger logger; diff --git a/TabletDriverService/Main.cpp b/TabletDriverService/Main.cpp index 39c4b18..92d1077 100644 --- a/TabletDriverService/Main.cpp +++ b/TabletDriverService/Main.cpp @@ -75,7 +75,7 @@ int main(int argc, char**argv) { // Unknown tablet if(tablet == NULL) { LOG_ERROR("Tablet not found!\n"); - CleanupAndExit(1); + CleanupAndExit(0); } // Tablet init @@ -84,7 +84,7 @@ int main(int argc, char**argv) { LOG_ERROR("Possible fixes:\n"); LOG_ERROR("1) Uninstall other tablet drivers.\n"); LOG_ERROR("2) Stop other tablet driver services.\n"); - CleanupAndExit(1); + CleanupAndExit(0); } // Set screen mapper tablet @@ -140,7 +140,7 @@ int main(int argc, char**argv) { LOG_ERROR("1) Install VMulti driver\n"); LOG_ERROR("2) Kill PentabletService.exe (XP Pen driver)\n"); LOG_ERROR("3) Uninstall other tablet drivers and reinstall VMulti driver\n"); - CleanupAndExit(1); + CleanupAndExit(0); } // Output manager @@ -209,6 +209,10 @@ int main(int argc, char**argv) { // void CleanupAndExit(int code) { + // Stop logger + LOGGER_STOP(); + + // PipeHandler printf("Cleanup PipeHandler\n"); if(pipeHandler != NULL) { @@ -237,9 +241,10 @@ void CleanupAndExit(int code) { if(tablet != NULL) delete tablet; - // Logger - LOGGER_STOP(); - Sleep(500); + // Uninitialize COM + CoUninitialize(); + + //Sleep(500); //printf("Press enter to exit..."); //getchar(); diff --git a/TabletDriverService/OutputDummy.cpp b/TabletDriverService/OutputDummy.cpp index f6274b4..182bb8e 100644 --- a/TabletDriverService/OutputDummy.cpp +++ b/TabletDriverService/OutputDummy.cpp @@ -14,7 +14,7 @@ bool OutputDummy::Set(TabletState *tabletState) { double timeDelta = (tabletState->time - timeBegin).count() / 1000000.0; - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("T=%0.3f B=%d X=%0.2f Y=%0.2f P=%0.4f\n", timeDelta, tabletState->buttons, diff --git a/TabletDriverService/OutputSendInputAbsolute.cpp b/TabletDriverService/OutputSendInputAbsolute.cpp index c5eadf5..5383cc6 100644 --- a/TabletDriverService/OutputSendInputAbsolute.cpp +++ b/TabletDriverService/OutputSendInputAbsolute.cpp @@ -96,7 +96,7 @@ bool OutputSendInputAbsolute::Write() { SendInput(1, &input, sizeof(INPUT)); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("%0.0f,%0.0f | %0.0f,%0.0f | %0.0f,%0.0f | %ld, %ld -> %0.0f,%0.0f\n", monitorInfo.virtualWidth, monitorInfo.virtualHeight, monitorInfo.virtualX, monitorInfo.virtualY, diff --git a/TabletDriverService/OutputSendInputRelative.cpp b/TabletDriverService/OutputSendInputRelative.cpp index e5734e6..cd9293d 100644 --- a/TabletDriverService/OutputSendInputRelative.cpp +++ b/TabletDriverService/OutputSendInputRelative.cpp @@ -75,7 +75,7 @@ bool OutputSendInputRelative::Write() { SendInput(1, &input, sizeof(INPUT)); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("X: %d, Y: %d, Flags: %d\n", input.mi.dx, input.mi.dy, input.mi.dwFlags ); diff --git a/TabletDriverService/OutputVMultiAbsolute.cpp b/TabletDriverService/OutputVMultiAbsolute.cpp index 0d1b827..3648621 100644 --- a/TabletDriverService/OutputVMultiAbsolute.cpp +++ b/TabletDriverService/OutputVMultiAbsolute.cpp @@ -71,7 +71,7 @@ bool OutputVMultiAbsolute::Write() { if(vmulti->HasReportChanged()) { // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(&report, 9, "Report: "); } diff --git a/TabletDriverService/OutputVMultiDigitizer.cpp b/TabletDriverService/OutputVMultiDigitizer.cpp index 47fcb0d..3ce42dd 100644 --- a/TabletDriverService/OutputVMultiDigitizer.cpp +++ b/TabletDriverService/OutputVMultiDigitizer.cpp @@ -78,7 +78,7 @@ bool OutputVMultiDigitizer::Write() { if(vmulti->HasReportChanged()) { // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(&report, 10, "Report: "); } diff --git a/TabletDriverService/OutputVMultiDigitizerRelative.cpp b/TabletDriverService/OutputVMultiDigitizerRelative.cpp index b78292d..112edae 100644 --- a/TabletDriverService/OutputVMultiDigitizerRelative.cpp +++ b/TabletDriverService/OutputVMultiDigitizerRelative.cpp @@ -110,7 +110,7 @@ bool OutputVMultiDigitizerRelative::Write() if(vmulti->HasReportChanged()) { // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(&report, 10, "Report: "); } diff --git a/TabletDriverService/OutputVMultiRelative.cpp b/TabletDriverService/OutputVMultiRelative.cpp index 50d9175..c9b07eb 100644 --- a/TabletDriverService/OutputVMultiRelative.cpp +++ b/TabletDriverService/OutputVMultiRelative.cpp @@ -68,7 +68,7 @@ bool OutputVMultiRelative::Write() { // Write report to VMulti device if report has changed if(vmulti->HasReportChanged() || report.x != 0 || report.y != 0) { - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(&report, 10, "Report: "); } vmulti->WriteReport(); diff --git a/TabletDriverService/PipeHandler.cpp b/TabletDriverService/PipeHandler.cpp index fa9a0e5..15c3196 100644 --- a/TabletDriverService/PipeHandler.cpp +++ b/TabletDriverService/PipeHandler.cpp @@ -23,7 +23,6 @@ PipeHandler::PipeHandler(string pipeName) isRunning = false; isStateOutputEnabled = false; - } @@ -41,8 +40,14 @@ PipeHandler::~PipeHandler() // bool PipeHandler::Start() { - if(!isRunning) { + lock.lock(); + bool running = isRunning; + lock.unlock(); + + if(!running) { + lock.lock(); isRunning = true; + lock.unlock(); threadInput = new thread(&PipeHandler::RunInputThread, this); threadOutput = new thread(&PipeHandler::RunOutputThread, this); threadState = new thread(&PipeHandler::RunStateThread, this); @@ -56,13 +61,20 @@ bool PipeHandler::Start() // Stop // bool PipeHandler::Stop() { - if(isRunning) { - isRunning = false; + + lock.lock(); + bool running = isRunning; + lock.unlock(); + + if(running) { + lock.lock(); + isRunning = true; + lock.unlock(); // Input if(handleInput != NULL) { if(connectedInput) { - CloseHandle(handleInput); + SAFE_CLOSE_HANDLE(handleInput); } TerminateThread(threadInput, 0); } @@ -70,7 +82,7 @@ bool PipeHandler::Stop() { // Output if(handleOutput != NULL) { if(connectedOutput) { - CloseHandle(handleOutput); + SAFE_CLOSE_HANDLE(handleOutput); } TerminateThread(threadOutput, 0); } @@ -78,7 +90,7 @@ bool PipeHandler::Stop() { // State if(handleState != NULL) { if(connectedState) { - CloseHandle(handleState); + SAFE_CLOSE_HANDLE(handleState); } TerminateThread(threadState, 0); } @@ -104,7 +116,16 @@ void PipeHandler::RunInputThread() char bufferWrite[1024]; char bufferRead[1024]; - while(isRunning) { + while(true) { + + lock.lock(); + if(!isRunning) { + lock.unlock(); + break; + } + else { + lock.unlock(); + } connectedInput = false; @@ -132,8 +153,7 @@ void PipeHandler::RunInputThread() LOG_DEBUG("Input pipe connected: %d\n", result); if(!result) { - if(handleInput != NULL) - CloseHandle(handleInput); + SAFE_CLOSE_HANDLE(handleInput); continue; } @@ -147,7 +167,7 @@ void PipeHandler::RunInputThread() if(!result || bytesRead == 0) { if(GetLastError() == ERROR_BROKEN_PIPE) { LOG_DEBUG("Input pipe broken! %d\n", GetLastError()); - CloseHandle(handleInput); + SAFE_CLOSE_HANDLE(handleInput); break; } } @@ -185,7 +205,16 @@ void PipeHandler::RunOutputThread() char bufferRead[1024]; // Connect loop - while(isRunning) { + while(true) { + + lock.lock(); + if(!isRunning) { + lock.unlock(); + break; + } + else { + lock.unlock(); + } connectedOutput = false; @@ -211,8 +240,7 @@ void PipeHandler::RunOutputThread() LOG_DEBUG("Output pipe connected: %d\n", result); if(!result) { - if(handleOutput != NULL) - CloseHandle(handleOutput); + SAFE_CLOSE_HANDLE(handleOutput); logger.pipeHandle = NULL; continue; } @@ -225,7 +253,7 @@ void PipeHandler::RunOutputThread() // Read loop while(true) { if(logger.pipeHandle == NULL) { - CloseHandle(handleOutput); + SAFE_CLOSE_HANDLE(handleOutput); break; } Sleep(100); @@ -234,7 +262,7 @@ void PipeHandler::RunOutputThread() if(!result || bytesRead == 0) { if(GetLastError() == ERROR_BROKEN_PIPE) { LOG_DEBUG("Output pipe broken! %d\n", GetLastError()); - CloseHandle(handleOutput); + SAFE_CLOSE_HANDLE(handleOutput); break; } } @@ -258,8 +286,19 @@ void PipeHandler::RunStateThread() char bufferRead[1024]; TabletState lastState; + bool outputEnabled = false; + // Connect loop - while(isRunning) { + while(true) { + + lock.lock(); + if(!isRunning) { + lock.unlock(); + break; + } + else { + lock.unlock(); + } connectedState = false; @@ -284,8 +323,7 @@ void PipeHandler::RunStateThread() LOG_DEBUG("State pipe connected: %d\n", result); if(!result) { - if(handleState != NULL) - CloseHandle(handleState); + SAFE_CLOSE_HANDLE(handleState); logger.pipeHandle = NULL; Sleep(1000); continue; @@ -298,7 +336,11 @@ void PipeHandler::RunStateThread() // Write loop while(true) { - if(tablet != NULL && tabletHandler != NULL && isStateOutputEnabled) { + lock.lock(); + outputEnabled = isStateOutputEnabled; + lock.unlock(); + + if(tablet != NULL && tabletHandler != NULL && outputEnabled) { if(tabletHandler->outputStateWrite.position.Distance(lastState.position) > 0) { // Input diff --git a/TabletDriverService/PipeHandler.h b/TabletDriverService/PipeHandler.h index eec5c7a..ec127c5 100644 --- a/TabletDriverService/PipeHandler.h +++ b/TabletDriverService/PipeHandler.h @@ -39,6 +39,7 @@ class PipeHandler bool connectedOutput; bool connectedState; + mutex lock; bool isRunning; bool isStateOutputEnabled; diff --git a/TabletDriverService/Tablet.cpp b/TabletDriverService/Tablet.cpp index 2901d02..61e2758 100644 --- a/TabletDriverService/Tablet.cpp +++ b/TabletDriverService/Tablet.cpp @@ -485,7 +485,7 @@ bool Tablet::Read(void *buffer, int length) { else if(hidDevice != NULL) { status = hidDevice->Read(buffer, length); } - if(logger.debugEnabled && status) { + if(logger.IsDebugOutputEnabled() && status) { LOG_DEBUGBUFFER(buffer, length, "Read: "); } return status; @@ -554,7 +554,7 @@ int Tablet::ReadAuxReport() status = hidDeviceAux->Read(buffer, length); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(buffer, length, "Aux read: "); } diff --git a/TabletDriverService/TabletDriverService.vcxproj.user b/TabletDriverService/TabletDriverService.vcxproj.user index 25f7916..7027a21 100644 --- a/TabletDriverService/TabletDriverService.vcxproj.user +++ b/TabletDriverService/TabletDriverService.vcxproj.user @@ -1,7 +1,7 @@  - config\init.cfg TDTestPipe + config\serviceonly.cfg WindowsLocalDebugger diff --git a/TabletDriverService/TabletFilterAdvancedSmoothing.cpp b/TabletDriverService/TabletFilterAdvancedSmoothing.cpp index b131c8f..0987d44 100644 --- a/TabletDriverService/TabletFilterAdvancedSmoothing.cpp +++ b/TabletDriverService/TabletFilterAdvancedSmoothing.cpp @@ -126,7 +126,7 @@ void TabletFilterAdvancedSmoothing::Update() { outputState.pressure += deltaPressure * weight; // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("TX=%0.2f TY=%0.2f TP=%0.2f OX=%0.2f OY=%0.2f OP=%0.2f DX=%0.2f DY=%0.2f DP=%0.2f D=%0.2f W=%0.3f V=%0.2f mm/s\n", target.position.x, target.position.y, diff --git a/TabletDriverService/TabletFilterAntiSmoothing.cpp b/TabletDriverService/TabletFilterAntiSmoothing.cpp index 028a2c7..5a40fe0 100644 --- a/TabletDriverService/TabletFilterAntiSmoothing.cpp +++ b/TabletDriverService/TabletFilterAntiSmoothing.cpp @@ -98,7 +98,7 @@ void TabletFilterAntiSmoothing::Update() { // if(timeDelta > 10) { - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("INVALID! V=%0.2f mm/s, A=%0.0f mm/s^2, D=%0.2f ms\n", velocity, acceleration, @@ -124,7 +124,7 @@ void TabletFilterAntiSmoothing::Update() { // Skip invalid reports by setting the position as latest target // if(ignoreInvalidReports > 0) { - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("IGNORE! V=%0.2f mm/s, A=%0.0f mm/s^2, D=%0.2f ms\n", velocity, acceleration, @@ -217,7 +217,7 @@ void TabletFilterAntiSmoothing::Update() { } // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { double delta = outputPosition->Distance(latestTarget); double pixelDensity = (mapper->primaryScreenArea->width / mapper->primaryTabletArea->width); LOG_DEBUG( diff --git a/TabletDriverService/TabletFilterGravity.cpp b/TabletDriverService/TabletFilterGravity.cpp index 2c9ce71..f993fcf 100644 --- a/TabletDriverService/TabletFilterGravity.cpp +++ b/TabletDriverService/TabletFilterGravity.cpp @@ -99,7 +99,7 @@ void TabletFilterGravity::Update() { // Debug message - if(logger.debugEnabled && distance > 0.0) { + if(logger.IsDebugOutputEnabled() && distance > 0.0) { LOG_DEBUG("X=%0.2f Y=%0.2f DX=%0.2f DY=%0.2f VX=%0.2f VY=%0.2f\n", outputPosition.x, outputPosition.y, diff --git a/TabletDriverService/TabletFilterNoiseReduction.cpp b/TabletDriverService/TabletFilterNoiseReduction.cpp index 91ffe8d..d1ae589 100644 --- a/TabletDriverService/TabletFilterNoiseReduction.cpp +++ b/TabletDriverService/TabletFilterNoiseReduction.cpp @@ -99,7 +99,7 @@ void TabletFilterNoiseReduction::Update() { } // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("Threshold! D=%0.2f mm, R=%0.2f, V=%0.2f mm/s, V2=%0.2f mm/s\n", distance, distanceRatio, @@ -112,7 +112,7 @@ void TabletFilterNoiseReduction::Update() { } // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { double distance = outputPosition->Distance(latestTarget); double latency; if(velocity <= 0) { diff --git a/TabletDriverService/TabletFilterSmoothing.cpp b/TabletDriverService/TabletFilterSmoothing.cpp index bacfd40..ed27d63 100644 --- a/TabletDriverService/TabletFilterSmoothing.cpp +++ b/TabletDriverService/TabletFilterSmoothing.cpp @@ -124,7 +124,7 @@ void TabletFilterSmoothing::Update() { double velocity = sqrt(dx * dx + dy * dy) * (1000.0 / timerInterval); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("TXY=%0.2f,%0.2f TP=%0.2f OXY=%0.2f,%0.2f OP=%0.2f DXY=%0.2f,%0.2f DP=%0.2f D=%0.2f W=%0.3f V=%0.2f L=%0.2f\n", target.position.x, target.position.y, diff --git a/TabletDriverService/TabletFilterTester.cpp b/TabletDriverService/TabletFilterTester.cpp index de2d0c9..1950337 100644 --- a/TabletDriverService/TabletFilterTester.cpp +++ b/TabletDriverService/TabletFilterTester.cpp @@ -73,7 +73,7 @@ void TabletFilterTester::Run() { position.y = cmd.GetDouble(2, 0); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("IN : %0.3f ms, %0.2f, %0.2f\n", time, position.x, @@ -88,7 +88,7 @@ void TabletFilterTester::Run() { outputState.position.Set(position); // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("First report: %0.3f, %0.2f, %0.2f\n", time, position.x, position.y); } @@ -122,7 +122,7 @@ void TabletFilterTester::Run() { */ // Debug message - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("OUT: %0.3f ms, %0.2f, %0.2f (%0.3f mm)\n", time, position.x, diff --git a/TabletDriverService/TabletHandler.cpp b/TabletDriverService/TabletHandler.cpp index 43bd138..b1d4dff 100644 --- a/TabletDriverService/TabletHandler.cpp +++ b/TabletDriverService/TabletHandler.cpp @@ -98,7 +98,7 @@ void TabletHandler::ChangeTimerInterval(int newInterval) { NtQueryTimerResolution(&timerMinimumResolution, &timerMaximumResolution, &timerCurrentResolution); // Debug messages - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("System timer resolution:\n"); LOG_DEBUG(" Minimum: %0.3f ms\n", (double)timerMinimumResolution / 10000.0); LOG_DEBUG(" Maximum: %0.3f ms\n", (double)timerMaximumResolution / 10000.0); @@ -127,7 +127,7 @@ void TabletHandler::ChangeTimerInterval(int newInterval) { NtQueryTimerResolution(&timerMinimumResolution, &timerMaximumResolution, &timerCurrentResolution); // Debug messages - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("System timer resolution set to %0.3f ms\n", (double)timerResolution / 10000.0); LOG_DEBUG("System timer resolution is now %0.3f ms\n", (double)timerCurrentResolution / 10000.0); } @@ -223,7 +223,7 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) isDown = IsButtonDown(buttons, buttonIndex); isPressed = IsButtonPressed(buttons, lastButtons, buttonIndex); isReleased = IsButtonReleased(buttons, lastButtons, buttonIndex); - + // Pen button map hasBinding = false; if(isPen && tablet->settings.buttonMap[buttonIndex].size() > 0) { @@ -288,6 +288,8 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) mouseButton == InputEmulator::MouseScrollBoth || mouseButton == InputEmulator::MediaVolumeControl + || + mouseButton == InputEmulator::MediaBalanceControl ) { // Scroll button down? @@ -347,13 +349,21 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) // Vertical Scroll - if(delta.y != 0 && (mouseButton & InputEmulator::MouseScrollVertical) == InputEmulator::MouseScrollVertical) { + if(delta.y != 0 && ( + mouseButton == InputEmulator::MouseScrollVertical + || + mouseButton == InputEmulator::MouseScrollBoth + )) { inputEmulator.MouseScroll((int)delta.y, true); scrolled = true; } // Horizontal scroll - if(delta.x != 0 && (mouseButton & InputEmulator::MouseScrollHorizontal) == InputEmulator::MouseScrollHorizontal) { + if(delta.x != 0 && ( + mouseButton == InputEmulator::MouseScrollHorizontal + || + mouseButton == InputEmulator::MouseScrollBoth + )) { inputEmulator.MouseScroll((int)-delta.x, false); scrolled = true; } @@ -364,6 +374,13 @@ void TabletHandler::ProcessButtons(UINT32 *outButtons, bool isPen) scrolled = true; } + // Media balance control + if((delta.x != 0 || isPressed) && mouseButton == InputEmulator::MediaBalanceControl) { + double balance = 0.5 + ((scrollStartPosition.x - scrollPosition.x) * tablet->settings.scrollSensitivity) / 50.0f; + inputEmulator.VolumeBalance((float)balance); + scrolled = true; + } + // Stop cursor if(tablet->settings.scrollStopCursor) { tablet->state.position.Set(scrollStartPosition); @@ -444,7 +461,7 @@ void TabletHandler::RunTabletInputThread() { // Reading failed else { LOG_ERROR("Tablet Read Error!\n"); - CleanupAndExit(1); + CleanupAndExit(0); } @@ -473,7 +490,7 @@ void TabletHandler::RunTabletInputThread() { } // Debug messages - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { double delta = (tablet->state.time - timeBegin).count() / 1000000.0; LOG_DEBUG("InputState: T=%0.3f, B=%d, X=%0.3f, Y=%0.3f, P=%0.3f V=%0.2f Valid=%s\n", delta, @@ -607,10 +624,10 @@ void TabletHandler::RunAuxInputThread() tablet->auxState.isValid = false; // Process buttons - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUG("Aux buttons: 0x%04X\n", auxState.buttons); } - UINT32 tmpButtons = 0; + UINT32 tmpButtons = 0; ProcessAuxButtons(&tmpButtons); tablet->auxState.lastButtons = tablet->auxState.buttons; @@ -706,7 +723,7 @@ void TabletHandler::WriteOutputState(TabletState * outputState) } // Debug message - if(result && logger.debugEnabled) { + if(result && logger.IsDebugOutputEnabled()) { double delta = (chrono::high_resolution_clock::now() - timeBegin).count() / 1000000.0; LOG_DEBUG("OutputState: T=%0.3f, B=%d, X=%0.3f, Y=%0.3f, P=%0.3f V=%s\n", delta, diff --git a/TabletDriverService/USBDevice.cpp b/TabletDriverService/USBDevice.cpp index e4762e4..b95e447 100644 --- a/TabletDriverService/USBDevice.cpp +++ b/TabletDriverService/USBDevice.cpp @@ -89,8 +89,7 @@ bool USBDevice::OpenDevice(HANDLE *outDeviceHandle, WINUSB_INTERFACE_HANDLE *out WinUsb_Initialize(deviceHandle, &usbHandle); if(!usbHandle) { LOG_ERROR("ERROR! Unable to initialize WinUSB!\n"); - if(deviceHandle != INVALID_HANDLE_VALUE) - CloseHandle(deviceHandle); + SAFE_CLOSE_HANDLE(deviceHandle); return false; } @@ -136,8 +135,7 @@ bool USBDevice::OpenDevice(HANDLE *outDeviceHandle, WINUSB_INTERFACE_HANDLE *out WinUsb_Free(usbHandle); // Free device file - if(deviceHandle && deviceHandle != INVALID_HANDLE_VALUE) - CloseHandle(deviceHandle); + SAFE_CLOSE_HANDLE(deviceHandle); } } } @@ -266,8 +264,7 @@ void USBDevice::CloseDevice() { } if(_deviceHandle != NULL && _deviceHandle != INVALID_HANDLE_VALUE) { try { - CloseHandle(_deviceHandle); - _deviceHandle = NULL; + SAFE_CLOSE_HANDLE(_deviceHandle); } catch(exception &e) { LOG_ERROR("WinUSB CloseHandler ERROR! %s\n", e.what()); } diff --git a/TabletDriverService/VMulti.cpp b/TabletDriverService/VMulti.cpp index 0205df8..886fee5 100644 --- a/TabletDriverService/VMulti.cpp +++ b/TabletDriverService/VMulti.cpp @@ -72,7 +72,7 @@ int VMulti::WriteReport() { memcpy(lastReportBuffer, reportBuffer, 65); // Debug - if(logger.debugEnabled) { + if(logger.IsDebugOutputEnabled()) { LOG_DEBUGBUFFER(reportBuffer, 12, "Write: "); } diff --git a/TabletDriverService/stdafx.h b/TabletDriverService/stdafx.h index 3cf5596..758a55c 100644 --- a/TabletDriverService/stdafx.h +++ b/TabletDriverService/stdafx.h @@ -21,6 +21,9 @@ // IntelliSense "fix"... //#define memcpy memcpy +#define SAFE_CLOSE_HANDLE(handle) if(handle != NULL && handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); handle = NULL; } + + // Global variables... extern VMulti *vmulti; extern CommandHandler *commandHandler; From 35077593ce08f275eafe6219575af681f8211c05 Mon Sep 17 00:00:00 2001 From: hawku Date: Fri, 4 Jan 2019 22:39:02 +0200 Subject: [PATCH 78/83] Replace commas with dots when parsing numbers --- TabletDriverGUI/Utils.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TabletDriverGUI/Utils.cs b/TabletDriverGUI/Utils.cs index d8c8862..1d03b79 100644 --- a/TabletDriverGUI/Utils.cs +++ b/TabletDriverGUI/Utils.cs @@ -37,6 +37,11 @@ public static bool ParseNumber(string str, out double val) { CheckCultureInfo(); val = 0; + + // Replace commas with dots + str = str.Replace(',', '.'); + + // Parse if (double.TryParse(str, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, cultureInfo.NumberFormat, out double tmp)) { val = tmp; From 9f7daa5d3faae95369aecd93b3be1f3849402003 Mon Sep 17 00:00:00 2001 From: hawku Date: Sun, 6 Jan 2019 13:32:41 +0200 Subject: [PATCH 79/83] Rewrote the named pipe code --- TabletDriverGUI/MainWindow.Console.cs | 3 +- TabletDriverGUI/MainWindow.Driver.cs | 24 +- TabletDriverGUI/MainWindow.Settings.cs | 11 + TabletDriverGUI/MainWindow.xaml | 3 +- TabletDriverGUI/NamedPipeClient.cs | 241 ++++++++++ TabletDriverGUI/TabletDriver.cs | 439 ++++++------------ TabletDriverGUI/TabletDriverGUI.csproj | 1 + TabletDriverService/CommandHandler.Other.cpp | 32 +- TabletDriverService/CommandHandler.cpp | 39 +- TabletDriverService/CommandHandler.h | 7 + TabletDriverService/Logger.cpp | 18 +- TabletDriverService/Logger.h | 6 +- TabletDriverService/Main.cpp | 77 ++- TabletDriverService/NamedPipeInput.cpp | 26 ++ TabletDriverService/NamedPipeInput.h | 12 + TabletDriverService/NamedPipeServer.cpp | 236 ++++++++++ TabletDriverService/NamedPipeServer.h | 46 ++ TabletDriverService/NamedPipeState.cpp | 87 ++++ TabletDriverService/NamedPipeState.h | 33 ++ TabletDriverService/PipeHandler.cpp | 380 --------------- TabletDriverService/PipeHandler.h | 56 --- TabletDriverService/Runnable.cpp | 18 + TabletDriverService/Runnable.h | 14 + TabletDriverService/Tablet.cpp | 2 +- .../TabletDriverService.vcxproj | 10 +- .../TabletDriverService.vcxproj.filters | 30 +- TabletDriverService/TabletHandler.cpp | 15 +- TabletDriverService/TabletHandler.h | 4 +- TabletDriverService/TabletMeasurement.cpp | 8 +- TabletDriverService/TabletMeasurement.h | 4 +- TabletDriverService/stdafx.h | 8 +- TabletDriverService/tools/RunServiceOnly.bat | 2 +- 32 files changed, 1078 insertions(+), 814 deletions(-) create mode 100644 TabletDriverGUI/NamedPipeClient.cs create mode 100644 TabletDriverService/NamedPipeInput.cpp create mode 100644 TabletDriverService/NamedPipeInput.h create mode 100644 TabletDriverService/NamedPipeServer.cpp create mode 100644 TabletDriverService/NamedPipeServer.h create mode 100644 TabletDriverService/NamedPipeState.cpp create mode 100644 TabletDriverService/NamedPipeState.h delete mode 100644 TabletDriverService/PipeHandler.cpp delete mode 100644 TabletDriverService/PipeHandler.h create mode 100644 TabletDriverService/Runnable.cpp create mode 100644 TabletDriverService/Runnable.h diff --git a/TabletDriverGUI/MainWindow.Console.cs b/TabletDriverGUI/MainWindow.Console.cs index a837946..b9de986 100644 --- a/TabletDriverGUI/MainWindow.Console.cs +++ b/TabletDriverGUI/MainWindow.Console.cs @@ -110,8 +110,7 @@ private void ConsoleSendCommand(string line) textConsoleInput.ScrollToEnd(); try { - driver.SendPipeCommand(line); - //driver.SendCommand(line); + driver.SendCommand(line); } catch (Exception e) { diff --git a/TabletDriverGUI/MainWindow.Driver.cs b/TabletDriverGUI/MainWindow.Driver.cs index 561844d..f9940dd 100644 --- a/TabletDriverGUI/MainWindow.Driver.cs +++ b/TabletDriverGUI/MainWindow.Driver.cs @@ -425,10 +425,27 @@ private void OnDriverStatusReceived(object sender, TabletDriver.DriverEventArgs private void ProcessStatusMessage(string variableName, string parameters) { + // + // Startup commands request + // + if(variableName == "startup_request") + { + SendStartupCommands(); + } + + + // + // Settings request + // + else if (variableName == "settings_request") + { + SendSettingsToDriver(); + } + // // Tablet Name // - if (variableName == "tablet") + else if (variableName == "tablet") { string tabletName = parameters; string title = "TabletDriverGUI - " + tabletName; @@ -591,6 +608,11 @@ private void ProcessStatusMessage(string variableName, string parameters) // Driver Started // private void OnDriverStarted(object sender, EventArgs e) + { + + } + + private void SendStartupCommands() { // Debugging commands if (config.DebuggingEnabled) diff --git a/TabletDriverGUI/MainWindow.Settings.cs b/TabletDriverGUI/MainWindow.Settings.cs index 2e2ae4d..4714655 100644 --- a/TabletDriverGUI/MainWindow.Settings.cs +++ b/TabletDriverGUI/MainWindow.Settings.cs @@ -1296,6 +1296,17 @@ private void MainMenuClick(object sender, RoutedEventArgs e) } + // + // Exit GUI only + // + else if (sender == mainMenuExitGUI) + { + driver.DoNotKill = true; + Close(); + } + + + // // Exit // diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml index 2ad5621..0d1573e 100644 --- a/TabletDriverGUI/MainWindow.xaml +++ b/TabletDriverGUI/MainWindow.xaml @@ -77,7 +77,8 @@ - + + diff --git a/TabletDriverGUI/NamedPipeClient.cs b/TabletDriverGUI/NamedPipeClient.cs new file mode 100644 index 0000000..c028682 --- /dev/null +++ b/TabletDriverGUI/NamedPipeClient.cs @@ -0,0 +1,241 @@ +using System; +using System.IO.Pipes; +using System.Text; +using System.Threading.Tasks; + +namespace TabletDriverGUI +{ + class NamedPipeClient + { + readonly string pipeName; + NamedPipeClientStream pipeStream; + + bool isRunning; + readonly object lockObject = new object(); + + public bool IsRunning + { + get + { + lock (lockObject) { return isRunning; } + } + set + { + lock (lockObject) { isRunning = value; } + } + } + + byte[] bufferRead; + + + public delegate void NamedPipeEventHandler(object sender, NamedPipeEventArgs e); + public event NamedPipeEventHandler MessageReceived; + public event EventHandler Connected; + public event EventHandler Disconnected; + public class NamedPipeEventArgs : EventArgs + { + public Message Message; + public NamedPipeEventArgs(Message message) + { + Message = message; + } + } + + public class Message + { + public byte[] Data; + public int Length; + public Message() + { + Length = 0; + } + } + + public class TaskResult + { + public bool IsSuccess { get; set; } + public string ErrorMessage { get; set; } + } + + + // + // Constructor + // + public NamedPipeClient(string pipeName) + { + this.pipeName = pipeName; + bufferRead = new byte[1024]; + } + + // + // Start + // + public bool Start() + { + if (!IsRunning) + { + IsRunning = true; + Console.WriteLine("new NamedPipeClientStream " + pipeName); + pipeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); + Console.WriteLine("Connecting to " + pipeName + "..."); + pipeStream.Connect(5000); + if (pipeStream.IsConnected) + { + Console.WriteLine("Connected to " + pipeName + "!"); + OnConnected(); + BeginRead(new Message()); + } + else + { + Console.WriteLine("Couldn't connect to " + pipeName + "!"); + return false; + } + return true; + } + return false; + } + + + // + // Stop + // + public bool Stop() + { + IsRunning = false; + + try + { + pipeStream.Close(); + pipeStream.Dispose(); + } + catch (Exception ex) + { + Console.WriteLine("Pipe stop error: " + ex.Message); + throw; + } + + OnDisconnected(); + + return true; + } + + + // + // Begin reading from the pipe + // + private void BeginRead(Message message) + { + try + { + //Console.WriteLine("BeginRead " + pipeName); + pipeStream.BeginRead(bufferRead, 0, bufferRead.Length, EndReadCallBack, message); + } + catch (Exception ex) + { + Console.WriteLine("BeginRead error: " + ex.Message); + } + } + + // + // End pipe read + // + private void EndReadCallBack(IAsyncResult result) + { + int bytesRead = pipeStream.EndRead(result); + //Console.WriteLine("EndRead length " + pipeName + ": " + bytesRead); + + if (bytesRead > 0) + { + var message = (Message)result.AsyncState; + + // Set message values + message.Length = bytesRead; + message.Data = bufferRead; + + // Process message + OnMessageReceived(message); + + // Start a new read + BeginRead(new Message()); + } + + // Client disconnected + else + { + if (IsRunning) + { + OnDisconnected(); + Stop(); + } + } + + } + + // + // End pipe write + // + private TaskResult EndWriteCallBack(IAsyncResult asyncResult) + { + pipeStream.EndWrite(asyncResult); + pipeStream.Flush(); + + return new TaskResult { IsSuccess = true }; + } + + + // + // Write message + // + public Task WriteMessage(string message) + { + var taskCompletionSource = new TaskCompletionSource(); + + if (pipeStream != null && pipeStream.IsConnected) + { + var buffer = Encoding.UTF8.GetBytes(message); + + //Console.WriteLine("Writing message to " + pipeName + ": " + message); + + pipeStream.BeginWrite(buffer, 0, buffer.Length, asyncResult => + { + try + { + taskCompletionSource.SetResult(EndWriteCallBack(asyncResult)); + } + catch (Exception ex) + { + taskCompletionSource.SetException(ex); + } + + }, null); + } + + return taskCompletionSource.Task; + } + + // + // Pipe connected + // + void OnConnected() + { + Console.WriteLine(pipeName + " connected!"); + Connected?.Invoke(this, new EventArgs()); + } + + // + // Pipe disconnected + // + void OnDisconnected() + { + Disconnected?.Invoke(this, new EventArgs()); + } + + // + // Pipe message received + // + void OnMessageReceived(Message message) + { + MessageReceived?.Invoke(this, new NamedPipeEventArgs(message)); + } + } +} diff --git a/TabletDriverGUI/TabletDriver.cs b/TabletDriverGUI/TabletDriver.cs index 5963936..18efa37 100644 --- a/TabletDriverGUI/TabletDriver.cs +++ b/TabletDriverGUI/TabletDriver.cs @@ -4,8 +4,6 @@ using System.Diagnostics; using System.IO; using System.Timers; -using System.IO.Pipes; -using System.Windows; using System.Text; using System.Runtime.InteropServices; @@ -48,14 +46,12 @@ public DriverEventArgs(DriverEventType type, string message, string parameters) private Dictionary commands; public Dictionary Commands { get { return commands; } } - // Pipe - System.Threading.Thread pipeInputThread = null; - System.Threading.Thread pipeOutputThread = null; - System.Threading.Thread pipeStateThread = null; - - NamedPipeClientStream pipeStreamInput; - NamedPipeClientStream pipeStreamOutput; - NamedPipeClientStream pipeStreamState; + // Pipe stuff + NamedPipeClient pipeInput; + NamedPipeClient pipeOutput; + NamedPipeClient pipeState; + byte[] stateBytes; + StringBuilder messageBuilder; [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] @@ -84,6 +80,9 @@ public struct TabletState private Timer timerWatchdog; private bool running; private readonly object locker = new object(); + + public bool DoNotKill; + public bool IsRunning { get @@ -108,7 +107,8 @@ public bool IsRunning public TabletDriver(string servicePath) { this.servicePath = servicePath; - this.processService = null; + processService = null; + DoNotKill = false; timerWatchdog = new Timer(2000); timerWatchdog.Elapsed += TimerWatchdog_Elapsed; @@ -118,9 +118,31 @@ public TabletDriver(string servicePath) commands = new Dictionary(); + messageBuilder = new StringBuilder(); + + pipeInput = new NamedPipeClient("TabletDriverOutput"); + pipeOutput = new NamedPipeClient("TabletDriverInput"); + pipeState = new NamedPipeClient("TabletDriverState"); + + stateBytes = new byte[Marshal.SizeOf(typeof(TabletState))]; + pipeInput.MessageReceived += PipeInput_MessageReceived; + pipeState.MessageReceived += PipeState_MessageReceived; + + pipeInput.Connected += (snd, e) => { pipeInput.WriteMessage("\n"); }; + pipeOutput.Connected += (snd, e) => + { + pipeOutput.WriteMessage("\n"); + }; + pipeState.Connected += (snd, e) => { pipeState.WriteMessage("\n"); }; + + + // Invoke driver started event + Started?.Invoke(this, new EventArgs()); + } + // // Driver watchdog // @@ -154,25 +176,19 @@ private void TimerWatchdog_Elapsed(object sender, ElapsedEventArgs e) // // Send command to the driver service // - public void SendCommand(string line) - { - if (IsRunning) - { - processService.StandardInput.WriteLine(line); - } - } - - // - // Send command to the driver service through a named pipe - // - public void SendPipeCommand(string line) + public void SendCommand(string command) { if (IsRunning) { - byte[] buffer = Encoding.UTF8.GetBytes(line); - pipeStreamOutput.Write(buffer, 0, buffer.Length); - pipeStreamOutput.Flush(); + if (pipeOutput.IsRunning) + { + pipeOutput.WriteMessage(command); + } + else + { + processService.StandardInput.WriteLine(command); + } } } @@ -379,243 +395,129 @@ private void ProcessService_OutputDataReceived(object sender, DataReceivedEventA } else { - string line = e.Data; - - // Status line? - if (line.Contains("[STATUS]")) - { - - // Parse status variable and value - Match match = Regex.Match(line, "^.+\\[STATUS\\] ([^ ]+) (.*?)$"); - if (!match.Success) return; - - string variableName = match.Groups[1].ToString().ToLower(); - string parameters = match.Groups[2].ToString(); - - // - // Commands status message - // - if (variableName == "commands") - { - string[] commandNames = parameters.Split(' '); - string lowerCaseName; - foreach (string name in commandNames) - { - lowerCaseName = name.Trim().ToLower(); - if (lowerCaseName.Length > 0) - { - if (!commands.ContainsKey(lowerCaseName)) - { - commands.Add(lowerCaseName, name); - } - } - } - } - - StatusReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Status, variableName, parameters)); - - } - - - //ConsoleAddLine(e.Data); - MessageReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Message, e.Data, "")); + //ProcessDriverMessage(e.Data); } } // - // Pipe input thread + // Received input pipe message // - private void RunPipeInputThread() + private void PipeInput_MessageReceived(object sender, NamedPipeClient.NamedPipeEventArgs e) { - StringBuilder stringBuilder = new StringBuilder(); - byte[] buffer = new byte[1024]; - - - while (IsRunning) - { - pipeStreamInput = new NamedPipeClientStream(".", "TabletDriverOutput", PipeDirection.InOut, PipeOptions.Asynchronous); - - Console.WriteLine("Input pipe connecting..."); - try { pipeStreamInput.Connect(); } - catch (Exception ex) - { - Console.WriteLine("Input pipe connect error! " + ex.Message); - break; - } - Console.WriteLine("Input pipe connected!"); - - // - // Input thread read loop - // - while (IsRunning && pipeStreamInput.IsConnected) - { - //Console.WriteLine("Input pipe read!"); - int bytesRead = 0; - try - { - bytesRead = pipeStreamInput.Read(buffer, 0, buffer.Length); - } - catch (Exception) - { - Console.WriteLine("Can't read input pipe!"); - break; - } - //Console.WriteLine("Input pipe read length: " + bytesRead); - if (bytesRead == 0) continue; - for (int i = 0; i < bytesRead; i++) - { - char c = (char)buffer[i]; - if (c == '\n' || c == '\r' || c == 0) - { - if (stringBuilder.Length > 0) - { - ConsoleAddLine(stringBuilder.ToString()); - } - stringBuilder.Clear(); - } - else - { - stringBuilder.Append(c); - } - } - - } + string stringMessage = Encoding.UTF8.GetString(e.Message.Data, 0, e.Message.Length); + //ConsoleAddLine("Pipe: " + stringMessage); + ProcessDriverMessage(stringMessage); - Console.WriteLine("Input pipe disconnected!"); - ConsoleAddLine("Input pipe disconnected!"); + } - // Close input stream - try - { - pipeStreamInput.Close(); - pipeStreamInput.Dispose(); - pipeStreamInput = null; - } - catch (Exception) { } + // + // Received state pipe message + // + private void PipeState_MessageReceived(object sender, NamedPipeClient.NamedPipeEventArgs e) + { + GCHandle gcHandle; + TabletState readState; + // Convert bytes to TabletState + if (e.Message.Length == stateBytes.Length) + { + gcHandle = GCHandle.Alloc(e.Message.Data, GCHandleType.Pinned); + readState = (TabletState)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(TabletState)); + gcHandle.Free(); + tabletState = readState; } - } // - // Pipe input thread + // Process driver message // - private void RunPipeOutputThread() + private void ProcessDriverMessage(string messageData) { - StringBuilder stringBuilder = new StringBuilder(); - char[] buffer = new char[1024]; + //ConsoleAddLine("Message data: '" + messageData + "'"); + // Add message data to stringbuilder + messageBuilder.Append(messageData); - while (IsRunning) - { - pipeStreamOutput = new NamedPipeClientStream(".", "TabletDriverInput", PipeDirection.InOut, PipeOptions.Asynchronous); - Console.WriteLine("Output pipe connecting..."); - try { pipeStreamOutput.Connect(); } - catch (Exception ex) - { - Console.WriteLine("Output pipe connect error! " + ex.Message); - break; - } - Console.WriteLine("Output pipe connected!"); - - // Wait for pipe to disconnect - while (IsRunning && pipeStreamOutput.IsConnected) - { - System.Threading.Thread.Sleep(100); - } - - Console.WriteLine("Output pipe disconnected!"); - - // Close output stream - try + // Find a line + string line = ""; + int index; + int startIndex = 0; + for (index = 0; index < messageBuilder.Length; index++) + { + char c = messageBuilder[index]; + if (c == '\n') { - pipeStreamOutput.Close(); - pipeStreamOutput.Dispose(); - pipeStreamOutput = null; + //ConsoleAddLine("New line at " + index); + if (index > 0 && index > startIndex + 1) + { + line = messageBuilder.ToString(startIndex, index - startIndex + 1).Trim(); + ProcessDriverMessageLine(line); + startIndex = index; + } } - catch (Exception) { } + } + // Remove lines from stringbuilder + if (startIndex < messageBuilder.Length) + { + messageBuilder.Remove(0, startIndex + 1); + } + else + { + messageBuilder.Clear(); } - } + } // - // Pipe state thread + // Process driver message line // - private void RunPipeStateThread() + private void ProcessDriverMessageLine(string line) { - int size = 0; - byte[] bytes; - GCHandle gcHandle; - TabletState readState; - size = Marshal.SizeOf(typeof(TabletState)); - bytes = new byte[Marshal.SizeOf(typeof(TabletState))]; - + //ConsoleAddLine("Message line: '" + line + "'"); - while (IsRunning) + // Status line? + if (line.Contains("[STATUS]")) { - pipeStreamState = new NamedPipeClientStream(".", "TabletDriverState", PipeDirection.InOut, PipeOptions.Asynchronous); - Console.WriteLine("State pipe connecting..."); - try + // Parse status variable and value + Match match = Regex.Match(line, "^.+\\[STATUS\\] ([^ ]+) (.*?)$"); + if (match.Success) { - pipeStreamState.Connect(); - } - catch (Exception ex) - { - Console.WriteLine("State pipe connection error! " + ex.Message); - break; - } - Console.WriteLine("State pipe connected!"); - + string variableName = match.Groups[1].ToString().ToLower(); + string parameters = match.Groups[2].ToString(); - // - // State pipe read loop - // - while (IsRunning && pipeStreamState.IsConnected) - { - //Console.WriteLine("Reading state pipe!"); - try + // + // Commands status message + // + if (variableName == "commands") { - // Read - if (pipeStreamState.Read(bytes, 0, bytes.Length) == size) + string[] commandNames = parameters.Split(' '); + string lowerCaseName; + foreach (string name in commandNames) { - - // Convert bytes to TabletState - gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - readState = (TabletState)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(TabletState)); - gcHandle.Free(); - tabletState = readState; - + lowerCaseName = name.Trim().ToLower(); + if (lowerCaseName.Length > 0) + { + if (!commands.ContainsKey(lowerCaseName)) + { + commands.Add(lowerCaseName, name); + } + } } } - catch (Exception ex) - { - Console.WriteLine("Can't read state pipe! " + ex.Message); - size = 0; - break; - } - + StatusReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Status, variableName, parameters)); } - - Console.WriteLine("State pipe disconnected!"); - - // Close state stream - try - { - pipeStreamState.Close(); - pipeStreamState.Dispose(); - pipeStreamState = null; - } - catch (Exception) { } - } + + ConsoleAddLine(line); + MessageReceived?.Invoke(this, new DriverEventArgs(DriverEventType.Message, line, "")); } @@ -674,20 +576,11 @@ public void Start(string processPath, string arguments) IsRunning = true; timerWatchdog.Start(); - // Pipe input thread - pipeInputThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeInputThread)); - pipeInputThread.Start(); - - // Pipe output thread - pipeOutputThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeOutputThread)); - pipeOutputThread.Start(); - - // Pipe state thread - pipeStateThread = new System.Threading.Thread(new System.Threading.ThreadStart(RunPipeStateThread)); - pipeStateThread.Start(); + // Named pipes + pipeInput.Start(); + pipeOutput.Start(); + pipeState.Start(); - - Started?.Invoke(this, new EventArgs()); } // Start failed @@ -714,81 +607,22 @@ public void Stop() timerWatchdog.Stop(); IsRunning = false; - - // Close pipe input stream - Console.WriteLine("Closing input pipe stream"); - try - { - if (pipeStreamInput != null) - { - pipeStreamInput.Close(); - pipeStreamInput.Dispose(); - } - } - catch (Exception e) { Console.WriteLine("Pipe input stream error! " + e.Message); } - - - // Close pipe output stream - Console.WriteLine("Closing output pipe stream"); - try - { - if (pipeStreamOutput != null) - { - pipeStreamOutput.Close(); - pipeStreamOutput.Dispose(); - } - } - catch (Exception e) { Console.WriteLine("Pipe output stream error! " + e.Message); } - - - // Close pipe state stream - Console.WriteLine("Closing state pipe stream"); - try - { - if (pipeStreamState != null) - { - pipeStreamState.Close(); - pipeStreamState.Dispose(); - } - } - catch (Exception e) { Console.WriteLine("Pipe state stream error! " + e.Message); } - - // Close input pipe thread - Console.WriteLine("Closing input pipe thread"); - try - { - pipeInputThread.Abort(); - pipeInputThread.Join(1000); - } - catch (Exception e) { Console.WriteLine("Pipe input thread error! " + e.Message); } - - - // Close output pipe thread - Console.WriteLine("Closing output pipe thread"); - try - { - pipeOutputThread.Abort(); - pipeOutputThread.Join(1000); - } - catch (Exception e) { Console.WriteLine("Pipe output thread error! " + e.Message); } - - // Close pipe state thread - Console.WriteLine("Closing state pipe thread"); - try - { - pipeStateThread.Abort(); - pipeStateThread.Join(1000); - } - catch (Exception e) { Console.WriteLine("Pipe state thread error! " + e.Message); } + // Stop named pipe clients + pipeInput.Stop(); + pipeOutput.Stop(); + pipeState.Stop(); // Kill service process Console.WriteLine("Killing TabletDriverService"); try { - processService.CancelOutputRead(); - processService.Kill(); - processService.Dispose(); + if (!DoNotKill) + { + processService.CancelOutputRead(); + processService.Kill(); + processService.Dispose(); + } } catch (Exception e) { @@ -797,7 +631,6 @@ public void Stop() Stopped?.Invoke(this, new EventArgs()); - System.Threading.Thread.Sleep(10); } diff --git a/TabletDriverGUI/TabletDriverGUI.csproj b/TabletDriverGUI/TabletDriverGUI.csproj index c28e1b5..43089b5 100644 --- a/TabletDriverGUI/TabletDriverGUI.csproj +++ b/TabletDriverGUI/TabletDriverGUI.csproj @@ -79,6 +79,7 @@ + WindowTabletViewSettings.xaml diff --git a/TabletDriverService/CommandHandler.Other.cpp b/TabletDriverService/CommandHandler.Other.cpp index f08ca92..424ff5d 100644 --- a/TabletDriverService/CommandHandler.Other.cpp +++ b/TabletDriverService/CommandHandler.Other.cpp @@ -329,10 +329,12 @@ void CommandHandler::CreateOtherCommands() { // AddCommand(new Command("StateOutput", [&](CommandLine *cmd) { if(!ExecuteCommand("TabletValid")) return false; - pipeHandler->lock.lock(); - pipeHandler->isStateOutputEnabled = cmd->GetBoolean(0, pipeHandler->isStateOutputEnabled); - pipeHandler->lock.unlock(); - LOG_INFO("State output = %s\n", pipeHandler->isStateOutputEnabled ? "Enabled" : "Disabled"); + bool isOutputEnabled = false; + pipeState->lock.lock(); + pipeState->isStateOutputEnabled = cmd->GetBoolean(0, pipeState->isStateOutputEnabled); + isOutputEnabled = pipeState->isStateOutputEnabled; + pipeState->lock.unlock(); + LOG_INFO("State output = %s\n", pipeState->isStateOutputEnabled ? "Enabled" : "Disabled"); return true; })); @@ -535,6 +537,24 @@ void CommandHandler::CreateOtherCommands() { })); + // + // Command: RequestSettings + // + AddCommand(new Command("RequestSettings", [&](CommandLine *cmd) { + LOG_STATUS("SETTINGS_REQUEST 1\n"); + return true; + })); + + + // + // Command: RequestStartup + // + AddCommand(new Command("RequestStartup", [&](CommandLine *cmd) { + LOG_STATUS("STARTUP_REQUEST 1\n"); + return true; + })); + + // // Command: Status // @@ -563,7 +583,7 @@ void CommandHandler::CreateOtherCommands() { LOG_STATUS("MAX_PRESSURE %d\n", tablet->settings.maxPressure); LOG_STATUS("AUX_BUTTONS %d\n", tablet->settings.auxButtonCount); if(tabletHandler != NULL) { - LOG_STATUS("STARTED %d\n", tabletHandler->isRunning); + LOG_STATUS("STARTED %d\n", tabletHandler->IsRunning()); } return true; @@ -629,6 +649,8 @@ void CommandHandler::CreateOtherCommands() { string commandsString = ""; + LOG_STATUS("COMMANDS_CLEAR 1\n"); + for(pair cmdPair : commands) { string commandName = cmdPair.second->name; string lowerCaseName = commandName; diff --git a/TabletDriverService/CommandHandler.cpp b/TabletDriverService/CommandHandler.cpp index c069971..01cc9e6 100644 --- a/TabletDriverService/CommandHandler.cpp +++ b/TabletDriverService/CommandHandler.cpp @@ -99,6 +99,13 @@ bool CommandHandler::IsValidCommand(string command) { bool CommandHandler::ExecuteCommand(string command) { return ExecuteCommand(command, NULL); } +bool CommandHandler::ExecuteCommandLock(string command) { + lock.lock(); + bool result = ExecuteCommand(command); + lock.unlock(); + return result; +} + // // Execute a command using command line @@ -106,6 +113,13 @@ bool CommandHandler::ExecuteCommand(string command) { bool CommandHandler::ExecuteCommand(CommandLine *cmd) { return ExecuteCommand(cmd->command, cmd); } +bool CommandHandler::ExecuteCommandLock(CommandLine *cmd) { + lock.lock(); + bool result = ExecuteCommand(cmd); + lock.unlock(); + return result; +} + // // Execute a command using command and parameter string @@ -116,6 +130,13 @@ bool CommandHandler::ExecuteCommand(string command, string parameters) { delete cmd; return result; } +bool CommandHandler::ExecuteCommandLock(string command, string parameters) { + lock.lock(); + bool result = ExecuteCommand(command, parameters); + lock.unlock(); + return result; +} + // // Execute a command @@ -130,6 +151,13 @@ bool CommandHandler::ExecuteCommand(string command, CommandLine * cmd) { } return false; } +bool CommandHandler::ExecuteCommandLock(string command, CommandLine * cmd) { + lock.lock(); + bool result = ExecuteCommand(command, cmd); + lock.unlock(); + return result; +} + // // Execute commands from a file @@ -146,7 +174,6 @@ bool CommandHandler::ExecuteFile(string filename) { return false; } - LOG_INFO("\\ Reading '%s'\n", filename.c_str()); // Loop through lines @@ -177,7 +204,15 @@ bool CommandHandler::ExecuteFile(string filename) { } LOG_INFO(">> %s\n", cmd->line.c_str()); - ExecuteCommand(cmd); + string command = cmd->command; + transform(command.begin(), command.end(), command.begin(), ::tolower); + if(aliases.count(command) > 0) { + command = aliases[command]; + } + if(commands.count(command) > 0) { + commands[command]->Execute(cmd); + } + //ExecuteCommand(cmd); delete cmd; } diff --git a/TabletDriverService/CommandHandler.h b/TabletDriverService/CommandHandler.h index 17353ce..ec50097 100644 --- a/TabletDriverService/CommandHandler.h +++ b/TabletDriverService/CommandHandler.h @@ -2,6 +2,7 @@ #include "Command.h" #include "TabletFilterTester.h" +#include #include #include @@ -13,6 +14,8 @@ class CommandHandler { map aliasNames; map help; + mutex lock; + CommandHandler(); ~CommandHandler(); @@ -28,9 +31,13 @@ class CommandHandler { bool IsValidCommand(string command); bool ExecuteCommand(string command); + bool ExecuteCommandLock(string command); bool ExecuteCommand(CommandLine *cmd); + bool ExecuteCommandLock(CommandLine * cmd); bool ExecuteCommand(string command, string parameters); + bool ExecuteCommandLock(string command, string parameters); bool ExecuteCommand(string command, CommandLine *cmd); + bool ExecuteCommandLock(string command, CommandLine * cmd); bool ExecuteFile(string filename); diff --git a/TabletDriverService/Logger.cpp b/TabletDriverService/Logger.cpp index 7a0fdba..d874359 100644 --- a/TabletDriverService/Logger.cpp +++ b/TabletDriverService/Logger.cpp @@ -9,7 +9,7 @@ Logger::Logger() { newMessage = false; directPrint = false; isDebugOutputEnabled = false; - pipeHandle = NULL; + namedPipe = NULL; } @@ -60,17 +60,15 @@ void Logger::OutputMessage(LogItem *message) { cout.write(buffer, index); cout.flush(); } catch(exception) { - exit(1); } // Write to output pipe try { - if(pipeHandle != NULL) { + if(namedPipe != NULL) { DWORD bytesWritten = 0; - WriteFile(pipeHandle, buffer, index, &bytesWritten, NULL); + namedPipe->Write(buffer, index); } } catch(exception) { - logger.pipeHandle = NULL; } // Write to log file @@ -264,7 +262,7 @@ void Logger::run() { } // Shutdown the thread - if(!isRunning && !newMessage) break; + if(!IsRunning() && !newMessage) break; // Sleep 10ms Sleep(10); @@ -327,8 +325,8 @@ bool Logger::IsDebugOutputEnabled() // Start logger thread // void Logger::Start() { - if(!isRunning) { - isRunning = true; + if(!IsRunning()) { + SetRunningState(true); threadLog = thread([this] { this->run(); }); } } @@ -337,8 +335,8 @@ void Logger::Start() { // Stop logger thread // void Logger::Stop() { - if(isRunning) { - isRunning = false; + if(IsRunning()) { + SetRunningState(false); newMessage = true; threadLog.join(); if(logFile && logFile.is_open()) { diff --git a/TabletDriverService/Logger.h b/TabletDriverService/Logger.h index b1287fd..4f5d41e 100644 --- a/TabletDriverService/Logger.h +++ b/TabletDriverService/Logger.h @@ -17,6 +17,7 @@ #include #include +#include "Runnable.h" #ifndef LOG_MODULE #define LOG_MODULE "Logger" @@ -52,7 +53,7 @@ using namespace std; -class Logger { +class Logger : public Runnable { private: thread threadLog; void run(); @@ -93,10 +94,9 @@ class Logger { "INFO", "DEBUG" }; - bool isRunning; bool directPrint; string logFilename = ""; - HANDLE pipeHandle; + NamedPipeServer *namedPipe; mutex lock; void OutputMessage(LogItem *message); diff --git a/TabletDriverService/Main.cpp b/TabletDriverService/Main.cpp index 92d1077..d8c3b04 100644 --- a/TabletDriverService/Main.cpp +++ b/TabletDriverService/Main.cpp @@ -6,6 +6,7 @@ #include "Tablet.h" #include "CommandLine.h" #include "DataFormatter.h" +#include "NamedPipeInput.h" #define LOG_MODULE "" #include "Logger.h" @@ -24,7 +25,11 @@ VMulti *vmulti; CommandHandler *commandHandler; OutputManager *outputManager; ScreenMapper *mapper; -PipeHandler *pipeHandler; + +// Named pipes +NamedPipeInput *pipeInput; +NamedPipeServer *pipeOutput; +NamedPipeState *pipeState; void InitConsole(); bool ProcessCommand(CommandLine *cmd); @@ -42,7 +47,7 @@ int main(int argc, char**argv) { vmulti = NULL; tablet = NULL; tabletHandler = NULL; - + // Init console InitConsole(); @@ -67,7 +72,7 @@ int main(int argc, char**argv) { // commandHandler->AddCommand(new Command("Start", [&](CommandLine *cmd) { - if(tabletHandler->isRunning) { + if(tabletHandler->IsRunning()) { LOG_INFO("Driver is already started!\n"); return true; } @@ -115,14 +120,23 @@ int main(int argc, char**argv) { LOGGER_START(); - // Pipe handler + // Named pipes if(argc > 2) { - pipeHandler = new PipeHandler(argv[2]); + pipeInput = new NamedPipeInput(string(argv[2]) + string("Input")); + pipeOutput = new NamedPipeServer(string(argv[2]) + string("Output")); + pipeState = new NamedPipeState(string(argv[2]) + string("State")); } else { - pipeHandler = new PipeHandler("TabletDriver"); + pipeInput = new NamedPipeInput("TabletDriverInput"); + pipeOutput = new NamedPipeServer("TabletDriverOutput"); + pipeState = new NamedPipeState("TabletDriverState"); } - pipeHandler->Start(); + pipeInput->Start(); + pipeOutput->Start(); + pipeState->Start(); + + // Logger output pipe + logger.namedPipe = pipeOutput; // VMulti XP-Pen vmulti = new VMulti(VMulti::TypeXPPen); @@ -156,6 +170,8 @@ int main(int argc, char**argv) { LOG_ERROR("Can't open '%s'\n", filename.c_str()); } + commandHandler->ExecuteCommandLock("RequestStartup"); + // // Main loop @@ -163,13 +179,16 @@ int main(int argc, char**argv) { while(true) { // Broken pipe - if(!cin) break; + if(!cin) { + Sleep(100); + } // Read line from the console try { getline(cin, line); } catch(exception) { - break; + Sleep(100); + //break; } // Process valid lines @@ -178,13 +197,8 @@ int main(int argc, char**argv) { // Line to command line cmd = new CommandLine(line); - // Process echo command directly - if(cmd->is("Echo")) { - commandHandler->ExecuteCommand(cmd); - } - // Hide console (for the service only mode) - else if(cmd->is("Hide")) { + if(cmd->is("Hide")) { ::ShowWindow(::GetConsoleWindow(), SW_HIDE); } @@ -213,12 +227,20 @@ void CleanupAndExit(int code) { LOGGER_STOP(); - // PipeHandler - printf("Cleanup PipeHandler\n"); - if(pipeHandler != NULL) { - delete pipeHandler; + // Named pipes + printf("Cleanup Input Pipe\n"); + if(pipeInput != NULL) { + delete pipeInput; + } + printf("Cleanup Output Pipe\n"); + if(pipeOutput != NULL) { + delete pipeOutput; } - + printf("Cleanup State Pipe\n"); + if(pipeState != NULL) { + delete pipeState; + } + // TabletHandler printf("Cleanup TabletHandler\n"); if(tabletHandler != NULL) { @@ -235,12 +257,12 @@ void CleanupAndExit(int code) { printf("Cleanup VMulti\n"); if(vmulti != NULL) delete vmulti; - + // Tablet printf("Cleanup Tablet\n"); if(tablet != NULL) delete tablet; - + // Uninitialize COM CoUninitialize(); @@ -259,7 +281,14 @@ bool ProcessCommand(CommandLine *cmd) { bool logResult = false; - LOG_INFO(">> %s\n", cmd->line.c_str()); + + // Hide echo input + if(cmd->is("Echo")) { + } + else + { + LOG_INFO(">> %s\n", cmd->line.c_str()); + } // // Execute command handler command @@ -269,7 +298,7 @@ bool ProcessCommand(CommandLine *cmd) { cmd->command.pop_back(); } if(commandHandler->IsValidCommand(cmd->command)) { - bool result = commandHandler->ExecuteCommand(cmd->command, cmd); + bool result = commandHandler->ExecuteCommandLock(cmd->command, cmd); if(logResult) { LOG_INFO("Result: %s\n", result ? "True" : "False"); } diff --git a/TabletDriverService/NamedPipeInput.cpp b/TabletDriverService/NamedPipeInput.cpp new file mode 100644 index 0000000..83b63be --- /dev/null +++ b/TabletDriverService/NamedPipeInput.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "NamedPipeInput.h" + +#define LOG_MODULE "PipeInput" +#include "Logger.h" + +NamedPipeInput::NamedPipeInput(string pipeName) : NamedPipeServer(pipeName) +{ + +} + + +NamedPipeInput::~NamedPipeInput() +{ +} + +int NamedPipeInput::ProcessData(int clientId, char *buffer, int length, char * bufferOutput) +{ + buffer[length] = 0; + //LOG_DEBUG("PipeInput data: %s\n", buffer); + CommandLine *cmd = new CommandLine(buffer); + ProcessCommand(cmd); + delete cmd; + + return 0; +} diff --git a/TabletDriverService/NamedPipeInput.h b/TabletDriverService/NamedPipeInput.h new file mode 100644 index 0000000..4926bc4 --- /dev/null +++ b/TabletDriverService/NamedPipeInput.h @@ -0,0 +1,12 @@ +#pragma once +#include "NamedPipeServer.h" +class NamedPipeInput : public NamedPipeServer +{ +public: + NamedPipeInput(string pipeName); + ~NamedPipeInput(); + + int ProcessData(int clientId, char *bufferInput, int length, char *bufferOutput) override; + +}; + diff --git a/TabletDriverService/NamedPipeServer.cpp b/TabletDriverService/NamedPipeServer.cpp new file mode 100644 index 0000000..4f1ca23 --- /dev/null +++ b/TabletDriverService/NamedPipeServer.cpp @@ -0,0 +1,236 @@ +#include "stdafx.h" +#include "NamedPipeServer.h" + +#define LOG_MODULE "NamedPipeServer" +#include "Logger.h" + + +// +// Constructor +// +NamedPipeServer::NamedPipeServer(string pipeName) +{ + this->pipePath = "\\\\.\\PIPE\\" + pipeName; + this->pipeName = pipeName; + clientCount = 0; + + for(int i = 0; i < 256; i++) { + clients[i].id = i; + clients[i].pipeName = pipeName; + } + +} + + +// +// Destructor +// +NamedPipeServer::~NamedPipeServer() +{ + Stop(); +} + +// +// Start pipe server +// +bool NamedPipeServer::Start() +{ + if(!IsRunning()) { + SetRunningState(true); + + LOG_DEBUG("Starting %s main thread...\n", pipeName.c_str()); + threadMain = new thread(&NamedPipeServer::RunMainThread, this); + LOG_DEBUG("%s main thread started!\n", pipeName.c_str()); + + return true; + } + return false; +} + + +// +// Stop pipe server +// +bool NamedPipeServer::Stop() +{ + if(IsRunning()) { + SetRunningState(false); + } + return false; +} + + +// +// Process data +// +int NamedPipeServer::ProcessData(int clientId, char *bufferInput, int length, char *bufferOutput) { + int bytesOutput = 0; + + //bufferInput[length] = 0; + //LOG_DEBUG("Client #%d input: %s\n", clientId, bufferInput); + //bytesOutput = sprintf_s(bufferOutput, BUFFER_SIZE, "Reply: %s", bufferInput); + + return bytesOutput; +} + + +// +// Write a message to all clients +// +bool NamedPipeServer::Write(void * buffer, int length) { + + DWORD bytesWritten = 0; + + if(!IsRunning()) return false; + + for(int i = 0; i < clientCount; i++) { + if(clients[i].isConnected) { + Client *client = &clients[i]; + //printf("Write %d bytes to client #%d!\n", length, i); + WriteFile(client->handlePipe, buffer, length, &bytesWritten, NULL); + FlushFileBuffers(client->handlePipe); + //printf("Written %d bytes to client #%d!\n", bytesWritten, i); + } + } + return true; + +} + + +// +// Main thread +// +void NamedPipeServer::RunMainThread() +{ + bool connected; + int clientId = 0; + bool running = true; + Client *client; + + while(IsRunning()) { + + LOG_DEBUG("%s: Creating pipe...\n", pipeName.c_str()); + + // Find free client id + client = &clients[clientId]; + + // Create pipe + client->handlePipe = CreateNamedPipeA(pipePath.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + BUFFER_SIZE, + BUFFER_SIZE, + 100, + NULL + ); + // Pipe invalid? + if(client->handlePipe == INVALID_HANDLE_VALUE) + { + LOG_ERROR("%s: CreateNamedPipeA failed, GLE=%d\n", pipeName.c_str(), GetLastError()); + Sleep(100); + break; + } + + LOG_DEBUG("Pipe %s created!\n", pipeName.c_str()); + + + // Connect + LOG_DEBUG("%s: Connecting to pipe...\n", pipeName.c_str()); + connected = ConnectNamedPipe(client->handlePipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + // Connected? + if(connected) { + LOG_DEBUG("%s: Client connected!\n", pipeName.c_str()); + thread *threadPipe = new thread(&NamedPipeServer::RunClientThread, this, client); + client->threadPipe = threadPipe; + clientCount++; + if(clientCount >= 256) { + clientCount = 256; + } + clientId++; + if(clientId >= 256) { + clientId = 0; + } + } + + // No connection + else { + LOG_DEBUG("%s: Could not connect to a pipe!\n", pipeName.c_str()); + SAFE_CLOSE_HANDLE(client->handlePipe); + } + + } + +} + + +// +// Client thread +// +void NamedPipeServer::RunClientThread(Client *client) +{ + char bufferRead[BUFFER_SIZE]; + char bufferWrite[BUFFER_SIZE]; + DWORD bytesRead = 0; + DWORD bytesWritten = 0; + int replyBytes; + bool result; + string pipeName = client->pipeName; + HANDLE handlePipe = client->handlePipe; + int clientId = client->id; + client->isConnected = true; + + while(true) { + //LOG_DEBUG("Read %s!\n", pipeName.c_str()); + + // Try reading + result = ReadFile(handlePipe, bufferRead, BUFFER_SIZE, &bytesRead, NULL); + if(!result || bytesRead == 0) { + if(GetLastError() == ERROR_BROKEN_PIPE) + { + LOG_DEBUG("%s: Client #%d disconnected.\n", pipeName.c_str(), clientId); + break; + } + else if(GetLastError() != 0) + { + LOG_DEBUG("%s: Client #%d ReadFile failed, GLE=%d\n", pipeName.c_str(), clientId, GetLastError()); + break; + } + Sleep(10); + } + else { + replyBytes = ProcessData(clientId, bufferRead, bytesRead, bufferWrite); + + if(replyBytes > 0) { + result = WriteFile(handlePipe, bufferWrite, replyBytes, &bytesWritten, NULL); + if(!result || replyBytes != bytesWritten) + { + LOG_DEBUG("%s: Client #%d WriteFile failed, GLE=%d\n", pipeName.c_str(), clientId, GetLastError()); + break; + } + } + } + + } + + DisconnectNamedPipe(handlePipe); + SAFE_CLOSE_HANDLE(handlePipe); + if(client != nullptr && client != NULL) { + client->isConnected = false; + } + LOG_DEBUG("%s: Client #%d thread exit!\n", pipeName.c_str(), clientId); + +} + + +// +// Client constructor +// +NamedPipeServer::Client::Client() +{ + id = 0; + handlePipe = NULL; + threadPipe = NULL; + isConnected = false; +} diff --git a/TabletDriverService/NamedPipeServer.h b/TabletDriverService/NamedPipeServer.h new file mode 100644 index 0000000..6d7057d --- /dev/null +++ b/TabletDriverService/NamedPipeServer.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +#include "Runnable.h" + +#define BUFFER_SIZE 1024 + +class NamedPipeServer : public Runnable +{ +public: + string pipePath; + string pipeName; + thread *threadMain; + mutex lock; + + class Client { + public: + int id; + HANDLE handlePipe; + thread* threadPipe; + string pipeName; + bool isConnected; + Client(); + }; + + Client clients[256]; + int clientCount; + + NamedPipeServer(string pipeName); + ~NamedPipeServer(); + + virtual bool Start(); + virtual bool Stop(); + virtual int ProcessData(int clientId, char *bufferInput, int length, char *bufferOutput); + bool Write(void * buffer, int length); + + void RunMainThread(); + void RunClientThread(Client *client); + + + +}; + diff --git a/TabletDriverService/NamedPipeState.cpp b/TabletDriverService/NamedPipeState.cpp new file mode 100644 index 0000000..2545e64 --- /dev/null +++ b/TabletDriverService/NamedPipeState.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "NamedPipeState.h" + + +NamedPipeState::NamedPipeState(string pipeName) : NamedPipeServer(pipeName) +{ + isStateOutputEnabled = false; +} + +NamedPipeState::~NamedPipeState() +{ +} + +// +// Start +// +bool NamedPipeState::Start() +{ + if(!IsRunning()) { + bool result = NamedPipeServer::Start(); + threadStateWriter = new thread(&NamedPipeState::RunStateWriterThread, this); + return result; + } + return false; +} + + +// +// Stop +// +bool NamedPipeState::Stop() +{ + return NamedPipeServer::Stop(); +} + +// +// State writer thread +// +void NamedPipeState::RunStateWriterThread() +{ + TabletState lastState; + bool outputEnabled = false; + bool running = true; + + while(IsRunning()) { + lock.lock(); + outputEnabled = isStateOutputEnabled; + lock.unlock(); + + if(tablet != NULL && tabletHandler != NULL && outputEnabled) { + if(tabletHandler->outputStateWrite.position.Distance(lastState.position) > 0) { + + // Input + stateMessage.inputButtons = tablet->state.inputButtons; + stateMessage.inputX = tablet->state.inputPosition.x; + stateMessage.inputY = tablet->state.inputPosition.y; + stateMessage.inputPressure = tablet->state.inputPressure; + stateMessage.inputVelocity = tablet->state.inputVelocity; + + // Output + stateMessage.outputButtons = tabletHandler->outputStateWrite.buttons; + stateMessage.outputX = tabletHandler->outputStateWrite.position.x; + stateMessage.outputY = tabletHandler->outputStateWrite.position.y; + stateMessage.outputPressure = tabletHandler->outputStateWrite.pressure; + + // Index + stateMessage.index++; + + // Write to pipe + Write(&stateMessage, sizeof(stateMessage)); + + // Update last state + memcpy(&lastState, &tabletHandler->outputStateWrite, sizeof(TabletState)); + } + else { + Sleep(2); + } + } + else { + Sleep(100); + } + + + } + + +} diff --git a/TabletDriverService/NamedPipeState.h b/TabletDriverService/NamedPipeState.h new file mode 100644 index 0000000..76645ac --- /dev/null +++ b/TabletDriverService/NamedPipeState.h @@ -0,0 +1,33 @@ +#pragma once +#include "NamedPipeServer.h" +class NamedPipeState : public NamedPipeServer +{ +public: +#pragma pack(1) + struct { + int index; + + int inputButtons; + double inputX; + double inputY; + double inputPressure; + double inputVelocity; + + int outputButtons; + double outputX; + double outputY; + double outputPressure; + } stateMessage; + + thread *threadStateWriter; + bool isStateOutputEnabled; + + NamedPipeState(string pipeName); + ~NamedPipeState(); + + bool Start() override; + bool Stop() override; + void RunStateWriterThread(); + +}; + diff --git a/TabletDriverService/PipeHandler.cpp b/TabletDriverService/PipeHandler.cpp deleted file mode 100644 index 15c3196..0000000 --- a/TabletDriverService/PipeHandler.cpp +++ /dev/null @@ -1,380 +0,0 @@ -#include "stdafx.h" -#include "PipeHandler.h" - -#define LOG_MODULE "Pipe" -#include "Logger.h" - - -// -// Constructor -// -PipeHandler::PipeHandler(string pipeName) -{ - pipeNameInput = "\\\\.\\PIPE\\" + pipeName + "Input"; - pipeNameOutput = "\\\\.\\PIPE\\" + pipeName + "Output"; - pipeNameState = "\\\\.\\PIPE\\" + pipeName + "State"; - - handleInput = NULL; - handleOutput = NULL; - handleState = NULL; - connectedInput = false; - connectedOutput = false; - connectedState = false; - - isRunning = false; - isStateOutputEnabled = false; -} - - -// -// Destructor -// -PipeHandler::~PipeHandler() -{ - Stop(); -} - - -// -// Start -// -bool PipeHandler::Start() -{ - lock.lock(); - bool running = isRunning; - lock.unlock(); - - if(!running) { - lock.lock(); - isRunning = true; - lock.unlock(); - threadInput = new thread(&PipeHandler::RunInputThread, this); - threadOutput = new thread(&PipeHandler::RunOutputThread, this); - threadState = new thread(&PipeHandler::RunStateThread, this); - return true; - } - return false; -} - - -// -// Stop -// -bool PipeHandler::Stop() { - - lock.lock(); - bool running = isRunning; - lock.unlock(); - - if(running) { - lock.lock(); - isRunning = true; - lock.unlock(); - - // Input - if(handleInput != NULL) { - if(connectedInput) { - SAFE_CLOSE_HANDLE(handleInput); - } - TerminateThread(threadInput, 0); - } - - // Output - if(handleOutput != NULL) { - if(connectedOutput) { - SAFE_CLOSE_HANDLE(handleOutput); - } - TerminateThread(threadOutput, 0); - } - - // State - if(handleState != NULL) { - if(connectedState) { - SAFE_CLOSE_HANDLE(handleState); - } - TerminateThread(threadState, 0); - } - - // Logger handle - logger.pipeHandle = NULL; - return true; - } - return false; -} - - -// -// Input thread -// -void PipeHandler::RunInputThread() -{ - BOOL result; - DWORD bytesWritten = 0; - DWORD bytesRead = 0; - DWORD bytesAvailable = 0; - DWORD bytesLeft = 0; - char bufferWrite[1024]; - char bufferRead[1024]; - - while(true) { - - lock.lock(); - if(!isRunning) { - lock.unlock(); - break; - } - else { - lock.unlock(); - } - - connectedInput = false; - - // Input connection - handleInput = CreateNamedPipeA(pipeNameInput.c_str(), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - sizeof(bufferWrite), - sizeof(bufferRead), - 500, // Timeout - NULL - ); - - - // Invalid handle? - if(handleInput == INVALID_HANDLE_VALUE) { - LOG_ERROR("Couldn't create input pipe!\n"); - break; - } - - // Connect - LOG_DEBUG("Connecting to input pipe!\n"); - result = ConnectNamedPipe(handleInput, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - LOG_DEBUG("Input pipe connected: %d\n", result); - - if(!result) { - SAFE_CLOSE_HANDLE(handleInput); - continue; - } - - connectedInput = true; - - // Read loop - while(true) { - PeekNamedPipe(handleInput, &bufferRead, sizeof(bufferRead), &bytesRead, &bytesAvailable, &bytesLeft); - if(bytesAvailable > 0) { - result = ReadFile(handleInput, &bufferRead, sizeof(bufferRead), &bytesRead, NULL); - if(!result || bytesRead == 0) { - if(GetLastError() == ERROR_BROKEN_PIPE) { - LOG_DEBUG("Input pipe broken! %d\n", GetLastError()); - SAFE_CLOSE_HANDLE(handleInput); - break; - } - } - - if(bytesRead > 0) { - bufferRead[bytesRead] = 0; - //LOG_DEBUG("Pipe read: %s\n", bufferRead); - CommandLine *cmd = new CommandLine(bufferRead); - ProcessCommand(cmd); - delete cmd; - } - } - else { - Sleep(100); - } - } - - } - - - - -} - - -// -// Output thread -// -void PipeHandler::RunOutputThread() -{ - BOOL result; - DWORD bytesWritten = 0; - DWORD bytesRead = 0; - char bufferWrite[1024]; - char bufferRead[1024]; - - // Connect loop - while(true) { - - lock.lock(); - if(!isRunning) { - lock.unlock(); - break; - } - else { - lock.unlock(); - } - - connectedOutput = false; - - // Output connection - handleOutput = CreateNamedPipeA(pipeNameOutput.c_str(), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - sizeof(bufferWrite), - sizeof(bufferRead), - 500, // Timeout - NULL - ); - - if(handleOutput == INVALID_HANDLE_VALUE) { - LOG_ERROR("Couldn't create output pipe!\n"); - Sleep(1000); - continue; - } - - LOG_DEBUG("Connecting to output pipe!\n"); - result = ConnectNamedPipe(handleOutput, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - LOG_DEBUG("Output pipe connected: %d\n", result); - - if(!result) { - SAFE_CLOSE_HANDLE(handleOutput); - logger.pipeHandle = NULL; - continue; - } - - // Logger pipe handle - logger.pipeHandle = handleOutput; - - connectedOutput = true; - - // Read loop - while(true) { - if(logger.pipeHandle == NULL) { - SAFE_CLOSE_HANDLE(handleOutput); - break; - } - Sleep(100); - /* - result = ReadFile(handleOutput, &bufferRead, sizeof(bufferRead), &bytesRead, NULL); - if(!result || bytesRead == 0) { - if(GetLastError() == ERROR_BROKEN_PIPE) { - LOG_DEBUG("Output pipe broken! %d\n", GetLastError()); - SAFE_CLOSE_HANDLE(handleOutput); - break; - } - } - */ - } - - } - -} - - -// -// Tablet state thread -// -void PipeHandler::RunStateThread() -{ - BOOL result; - DWORD bytesWritten = 0; - DWORD bytesRead = 0; - char bufferWrite[1024]; - char bufferRead[1024]; - TabletState lastState; - - bool outputEnabled = false; - - // Connect loop - while(true) { - - lock.lock(); - if(!isRunning) { - lock.unlock(); - break; - } - else { - lock.unlock(); - } - - connectedState = false; - - // Output connection - handleState = CreateNamedPipeA(pipeNameState.c_str(), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - sizeof(bufferWrite), - sizeof(bufferRead), - 500, // Timeout - NULL - ); - - if(handleState == INVALID_HANDLE_VALUE) { - LOG_ERROR("Couldn't create state pipe!\n"); - break; - } - - LOG_DEBUG("Connecting to state pipe!\n"); - result = ConnectNamedPipe(handleState, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - LOG_DEBUG("State pipe connected: %d\n", result); - - if(!result) { - SAFE_CLOSE_HANDLE(handleState); - logger.pipeHandle = NULL; - Sleep(1000); - continue; - } - - connectedState = true; - - stateMessage.index = 0; - - // Write loop - while(true) { - - lock.lock(); - outputEnabled = isStateOutputEnabled; - lock.unlock(); - - if(tablet != NULL && tabletHandler != NULL && outputEnabled) { - if(tabletHandler->outputStateWrite.position.Distance(lastState.position) > 0) { - - // Input - stateMessage.inputButtons = tablet->state.inputButtons; - stateMessage.inputX = tablet->state.inputPosition.x; - stateMessage.inputY = tablet->state.inputPosition.y; - stateMessage.inputPressure = tablet->state.inputPressure; - stateMessage.inputVelocity = tablet->state.inputVelocity; - - // Output - stateMessage.outputButtons = tabletHandler->outputStateWrite.buttons; - stateMessage.outputX = tabletHandler->outputStateWrite.position.x; - stateMessage.outputY = tabletHandler->outputStateWrite.position.y; - stateMessage.outputPressure = tabletHandler->outputStateWrite.pressure; - - // Index - stateMessage.index++; - - // Write to pipe - WriteFile(handleState, &stateMessage, sizeof(stateMessage), &bytesWritten, NULL); - - // Update last state - memcpy(&lastState, &tabletHandler->outputStateWrite, sizeof(TabletState)); - } - else { - Sleep(2); - } - } - else { - Sleep(100); - } - - } - - } - -} \ No newline at end of file diff --git a/TabletDriverService/PipeHandler.h b/TabletDriverService/PipeHandler.h deleted file mode 100644 index ec127c5..0000000 --- a/TabletDriverService/PipeHandler.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include - -class PipeHandler -{ -public: - -#pragma pack(1) - struct { - int index; - - int inputButtons; - double inputX; - double inputY; - double inputPressure; - double inputVelocity; - - int outputButtons; - double outputX; - double outputY; - double outputPressure; - } stateMessage; - - string pipeNameInput; - string pipeNameOutput; - string pipeNameState; - - thread *threadInput; - thread *threadOutput; - thread *threadState; - - HANDLE handleInput; - HANDLE handleOutput; - HANDLE handleState; - - bool connectedInput; - bool connectedOutput; - bool connectedState; - - mutex lock; - bool isRunning; - bool isStateOutputEnabled; - - PipeHandler(string pipeName); - ~PipeHandler(); - - bool Start(); - bool Stop(); - void RunInputThread(); - void RunOutputThread(); - void RunStateThread(); - -}; - diff --git a/TabletDriverService/Runnable.cpp b/TabletDriverService/Runnable.cpp new file mode 100644 index 0000000..ed1b08a --- /dev/null +++ b/TabletDriverService/Runnable.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "Runnable.h" + +bool Runnable::IsRunning() +{ + bool running = false; + isRunningLock.lock(); + running = _isRunning; + isRunningLock.unlock(); + return running; +} + +void Runnable::SetRunningState(bool running) +{ + isRunningLock.lock(); + _isRunning = running; + isRunningLock.unlock(); +} diff --git a/TabletDriverService/Runnable.h b/TabletDriverService/Runnable.h new file mode 100644 index 0000000..a11c293 --- /dev/null +++ b/TabletDriverService/Runnable.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class Runnable +{ +protected: + bool _isRunning = false; + mutex isRunningLock; +public: + bool IsRunning(); + void SetRunningState(bool running); +}; + diff --git a/TabletDriverService/Tablet.cpp b/TabletDriverService/Tablet.cpp index 61e2758..9af8fab 100644 --- a/TabletDriverService/Tablet.cpp +++ b/TabletDriverService/Tablet.cpp @@ -462,7 +462,7 @@ int Tablet::ReadState() { // Tablet measurement update - if(measurement.isRunning) { + if(measurement.IsRunning()) { state.buttons = reportData.buttons & 0x0F; measurement.Update(state); return Tablet::ReportInvalid; diff --git a/TabletDriverService/TabletDriverService.vcxproj b/TabletDriverService/TabletDriverService.vcxproj index bd0951f..783188c 100644 --- a/TabletDriverService/TabletDriverService.vcxproj +++ b/TabletDriverService/TabletDriverService.vcxproj @@ -181,6 +181,9 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG + + + @@ -191,9 +194,9 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG - + @@ -227,6 +230,9 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG + + + @@ -237,9 +243,9 @@ xcopy /C /Y "$(ProjectDir)tools\RunServiceOnly.bat" "$(SolutionDir)TabletDriverG - + diff --git a/TabletDriverService/TabletDriverService.vcxproj.filters b/TabletDriverService/TabletDriverService.vcxproj.filters index f84cfd6..5a55500 100644 --- a/TabletDriverService/TabletDriverService.vcxproj.filters +++ b/TabletDriverService/TabletDriverService.vcxproj.filters @@ -120,9 +120,6 @@ Header Files - - Header Files - Header Files @@ -132,6 +129,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -254,9 +263,6 @@ Source Files - - Source Files - Source Files @@ -266,6 +272,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/TabletDriverService/TabletHandler.cpp b/TabletDriverService/TabletHandler.cpp index b1d4dff..edab5f3 100644 --- a/TabletDriverService/TabletHandler.cpp +++ b/TabletDriverService/TabletHandler.cpp @@ -12,7 +12,7 @@ TabletHandler::TabletHandler() { tablet = NULL; tabletInputThread = NULL; auxInputThread = NULL; - isRunning = false; + SetRunningState(false); timerInterval = 20; isTimerTickRunning = false; } @@ -23,7 +23,7 @@ TabletHandler::TabletHandler() { // TabletHandler::~TabletHandler() { StopTimer(); - isRunning = false; + SetRunningState(false); if(tablet != NULL) { tablet->isOpen = false; } @@ -36,7 +36,7 @@ TabletHandler::~TabletHandler() { bool TabletHandler::Start() { if(tablet == NULL) return false; ChangeTimerInterval((int)round(timerInterval)); - isRunning = true; + SetRunningState(true); tabletInputThread = new thread(&TabletHandler::RunTabletInputThread, this); auxInputThread = new thread(&TabletHandler::RunAuxInputThread, this); return true; @@ -47,7 +47,7 @@ bool TabletHandler::Start() { // bool TabletHandler::Stop() { if(tablet == NULL) return false; - isRunning = false; + SetRunningState(false); return true; } @@ -83,6 +83,7 @@ bool TabletHandler::StopTimer() { return false; } + // // Change timer interval // @@ -421,7 +422,7 @@ void TabletHandler::RunTabletInputThread() { // // Tablet input main loop // - while(isRunning) { + while(IsRunning()) { // // Read tablet position @@ -579,7 +580,7 @@ void TabletHandler::RunTabletInputThread() { WriteOutputState(&outputStateWrite); } - isRunning = false; + SetRunningState(false); } @@ -602,7 +603,7 @@ void TabletHandler::RunAuxInputThread() // // Auxiliary input main loop // - while(isRunning) { + while(IsRunning()) { // Read reportStatus = tablet->ReadAuxReport(); diff --git a/TabletDriverService/TabletHandler.h b/TabletDriverService/TabletHandler.h index 51a4a2b..65a686b 100644 --- a/TabletDriverService/TabletHandler.h +++ b/TabletDriverService/TabletHandler.h @@ -3,8 +3,9 @@ #include #include "Tablet.h" #include "InputEmulator.h" +#include "Runnable.h" -class TabletHandler { +class TabletHandler : public Runnable { private: Vector2D lastScrollPosition; Vector2D scrollStartPosition; @@ -28,7 +29,6 @@ class TabletHandler { TabletState outputStateWrite; InputEmulator inputEmulator; - bool isRunning; bool isTimerTickRunning; TabletHandler(); diff --git a/TabletDriverService/TabletMeasurement.cpp b/TabletDriverService/TabletMeasurement.cpp index 4406eaf..89ab974 100644 --- a/TabletDriverService/TabletMeasurement.cpp +++ b/TabletDriverService/TabletMeasurement.cpp @@ -50,14 +50,14 @@ void TabletMeasurement::Start(int reportCount) { lastPointTime = 0; - isRunning = true; + SetRunningState(true); } // // Stop measurement // void TabletMeasurement::Stop() { - isRunning = false; + SetRunningState(false); } @@ -69,7 +69,7 @@ void TabletMeasurement::Update(TabletState state) { DWORD time; - if(isRunning) { + if(IsRunning()) { if(reportCounter > 0 || reportCounter <= -1) { // X Limits @@ -98,7 +98,7 @@ void TabletMeasurement::Update(TabletState state) { reportCounter--; } else { - isRunning = false; + SetRunningState(false); } } } \ No newline at end of file diff --git a/TabletDriverService/TabletMeasurement.h b/TabletDriverService/TabletMeasurement.h index 46bafac..65141a2 100644 --- a/TabletDriverService/TabletMeasurement.h +++ b/TabletDriverService/TabletMeasurement.h @@ -2,8 +2,9 @@ #include "Vector2D.h" #include "TabletState.h" +#include "Runnable.h" -class TabletMeasurement { +class TabletMeasurement : public Runnable { public: Vector2D minimum; Vector2D maximum; @@ -12,7 +13,6 @@ class TabletMeasurement { int totalReports; int reportCounter; - bool isRunning; TabletState lastState; DWORD lastPointTime; diff --git a/TabletDriverService/stdafx.h b/TabletDriverService/stdafx.h index 758a55c..4103d92 100644 --- a/TabletDriverService/stdafx.h +++ b/TabletDriverService/stdafx.h @@ -16,7 +16,9 @@ #include "ScreenMapper.h" #include "OutputManager.h" #include "CommandHandler.h" -#include "PipeHandler.h" +#include "NamedPipeServer.h" +#include "NamedPipeInput.h" +#include "NamedPipeState.h" // IntelliSense "fix"... //#define memcpy memcpy @@ -31,7 +33,9 @@ extern Tablet *tablet; extern TabletHandler *tabletHandler; extern OutputManager *outputManager; extern ScreenMapper *mapper; -extern PipeHandler *pipeHandler; +extern NamedPipeInput *pipeInput; +extern NamedPipeServer *pipeOutput; +extern NamedPipeState *pipeState; extern void CleanupAndExit(int code); extern bool ProcessCommand(CommandLine *cmd); diff --git a/TabletDriverService/tools/RunServiceOnly.bat b/TabletDriverService/tools/RunServiceOnly.bat index 2344495..996ab13 100644 --- a/TabletDriverService/tools/RunServiceOnly.bat +++ b/TabletDriverService/tools/RunServiceOnly.bat @@ -1,2 +1,2 @@ cd .. -"bin\TabletDriverService.exe" "config\serviceonly.cfg" \ No newline at end of file +"bin\TabletDriverService.exe" "config\serviceonly.cfg" "ServiceOnly" \ No newline at end of file From 0d8cc3ef7d25aedb471be832ead11c62daae7b20 Mon Sep 17 00:00:00 2001 From: hawku Date: Sun, 13 Jan 2019 21:08:55 +0200 Subject: [PATCH 80/83] Fixed multiple thread safety and stability issues --- TabletDriverGUI/Configuration.cs | 4 +- TabletDriverGUI/MainWindow.Areas.cs | 17 +- TabletDriverGUI/MainWindow.Settings.cs | 20 +- TabletDriverGUI/MainWindow.xaml | 530 ++++++++++-------- TabletDriverGUI/MainWindow.xaml.cs | 8 + TabletDriverGUI/NamedPipeClient.cs | 30 +- TabletDriverGUI/TabletDriver.cs | 6 +- TabletDriverGUI/WindowTabletView.xaml.cs | 40 +- TabletDriverGUI/WindowTabletViewSettings.xaml | 3 +- .../WindowTabletViewSettings.xaml.cs | 6 +- TabletDriverService/BufferQueue.cpp | 102 ++++ TabletDriverService/BufferQueue.h | 27 + .../CommandHandler.Auxiliary.cpp | 24 +- TabletDriverService/CommandHandler.Device.cpp | 102 ++-- .../CommandHandler.Filters.cpp | 100 ++-- TabletDriverService/CommandHandler.Other.cpp | 168 +++--- TabletDriverService/CommandHandler.Tablet.cpp | 218 +++---- TabletDriverService/CommandHandler.cpp | 62 +- TabletDriverService/CommandHandler.h | 1 + TabletDriverService/CommandLine.cpp | 92 +-- TabletDriverService/DataFormatter.cpp | 10 +- TabletDriverService/HIDDevice.cpp | 77 ++- TabletDriverService/InputEmulator.cpp | 182 +++--- TabletDriverService/InputEmulator.h | 6 +- TabletDriverService/Logger.cpp | 227 ++++---- TabletDriverService/Logger.h | 24 +- TabletDriverService/Main.cpp | 115 ++-- TabletDriverService/NamedPipeInput.cpp | 31 +- TabletDriverService/NamedPipeInput.h | 2 +- TabletDriverService/NamedPipeServer.cpp | 521 ++++++++++++++--- TabletDriverService/NamedPipeServer.h | 34 +- TabletDriverService/NamedPipeState.cpp | 31 +- TabletDriverService/NamedPipeState.h | 5 +- TabletDriverService/Runnable.cpp | 8 +- TabletDriverService/Runnable.h | 4 +- TabletDriverService/Tablet.cpp | 209 ++++--- TabletDriverService/Tablet.h | 13 +- .../TabletDriverService.vcxproj | 4 +- .../TabletDriverService.vcxproj.filters | 6 + TabletDriverService/TabletFilterSmoothing.cpp | 1 + TabletDriverService/TabletHandler.cpp | 334 +++++++---- TabletDriverService/TabletHandler.h | 5 + TabletDriverService/TabletSettings.h | 1 + TabletDriverService/TabletState.cpp | 1 + TabletDriverService/TabletState.h | 2 + TabletDriverService/stdafx.h | 14 +- 46 files changed, 2198 insertions(+), 1229 deletions(-) create mode 100644 TabletDriverService/BufferQueue.cpp create mode 100644 TabletDriverService/BufferQueue.h diff --git a/TabletDriverGUI/Configuration.cs b/TabletDriverGUI/Configuration.cs index ff41d47..25bd26b 100644 --- a/TabletDriverGUI/Configuration.cs +++ b/TabletDriverGUI/Configuration.cs @@ -122,6 +122,7 @@ public class TabletViewSettings public Point OffsetText; public Point OffsetPressure; public bool FadeInOut; + public bool Borderless; public TabletViewSettings() { BackgroundColor = "#FFFFFF"; @@ -133,11 +134,12 @@ public TabletViewSettings() InputTrailLength = 30; OutputTrailLength = 30; DrawLength = 0; - Font = "Arial"; + Font = "Segoe UI"; FontSize = 25; OffsetPressure = new Point(0, 0); OffsetText = new Point(0, 0); FadeInOut = false; + Borderless = false; } }; public TabletViewSettings TabletView; diff --git a/TabletDriverGUI/MainWindow.Areas.cs b/TabletDriverGUI/MainWindow.Areas.cs index b0d35ae..0b5b8d6 100644 --- a/TabletDriverGUI/MainWindow.Areas.cs +++ b/TabletDriverGUI/MainWindow.Areas.cs @@ -908,18 +908,19 @@ void UpdateAreaInformation() // Screen area labelScreenAreaInfo.Content = areaText + - "" + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedScreenArea.Height, "0.000") + ":1" + - " | " + Utils.GetNumberString(config.SelectedScreenArea.Width * config.SelectedScreenArea.Height, "0") + " pixels"; + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedScreenArea.Height, "0.000") + ":1 | " + + Utils.GetNumberString(config.SelectedScreenArea.Width * config.SelectedScreenArea.Height, "0") + " pixels"; // Tablet area labelTabletAreaInfo.Content = areaText + - "" + Utils.GetNumberString(config.SelectedTabletArea.Width / config.SelectedTabletArea.Height, "0.000") + ":1" + - " | " + Utils.GetNumberString(config.SelectedTabletArea.Width * config.SelectedTabletArea.Height, "0") + " mm²" + - " " + Utils.GetNumberString( + Utils.GetNumberString(config.SelectedTabletArea.Width / config.SelectedTabletArea.Height, "0.000") + ":1 | " + + Utils.GetNumberString(config.SelectedTabletArea.Width * config.SelectedTabletArea.Height, "0") + " mm² " + + Utils.GetNumberString( config.SelectedTabletArea.Width * config.SelectedTabletArea.Height / (config.TabletFullArea.Width * config.TabletFullArea.Height) * 100.0 - , "0") + "%" + - " | " + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedTabletArea.Width, "0.0") + " x " + + , "0") + "% of " + + Utils.GetNumberString(config.TabletFullArea.Width) + "x" + Utils.GetNumberString(config.TabletFullArea.Height) + " mm | " + + Utils.GetNumberString(config.SelectedScreenArea.Width / config.SelectedTabletArea.Width, "0.0") + "x" + Utils.GetNumberString(config.SelectedScreenArea.Height / config.SelectedTabletArea.Height, "0.0") + " px/mm"; } @@ -995,7 +996,7 @@ private void CanvasArea_MouseUp(object sender, MouseButtonEventArgs e) canvasTabletArea.ReleaseMouseCapture(); // Focus - if(sender == canvasScreenMap) + if (sender == canvasScreenMap) canvasScreenMap.Focus(); else if (sender == canvasTabletArea) canvasTabletArea.Focus(); diff --git a/TabletDriverGUI/MainWindow.Settings.cs b/TabletDriverGUI/MainWindow.Settings.cs index 4714655..5ad54af 100644 --- a/TabletDriverGUI/MainWindow.Settings.cs +++ b/TabletDriverGUI/MainWindow.Settings.cs @@ -1401,17 +1401,6 @@ private void MainMenuClick(object sender, RoutedEventArgs e) } - // - // Fit window to content - // - else if (sender == mainMenuFitToContent) - { - SizeToContent = SizeToContent.WidthAndHeight; - UpdateLayout(); - SizeToContent = SizeToContent.Manual; - } - - // // Update desktop image // @@ -1433,6 +1422,15 @@ private void MainMenuClick(object sender, RoutedEventArgs e) } + // + // Fit window to content + // + else if (sender == mainMenuFitToContent) + { + SizeToContent = SizeToContent.WidthAndHeight; + UpdateLayout(); + SizeToContent = SizeToContent.Manual; + } } diff --git a/TabletDriverGUI/MainWindow.xaml b/TabletDriverGUI/MainWindow.xaml index 0d1573e..f20350b 100644 --- a/TabletDriverGUI/MainWindow.xaml +++ b/TabletDriverGUI/MainWindow.xaml @@ -5,46 +5,92 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Keyboard.PreviewKeyDown="Window_PreviewKeyDown" - Title="TabletDriverGUI" Height="703" Width="680" - > + Title="TabletDriverGUI" Height="750" Width="770"> + - - - + + + + + - + + + + + - + + + + + + + + + + - + - -