From 51c5c44725fb32a31039481ce5b25a15b72b09e6 Mon Sep 17 00:00:00 2001 From: Boris Fritscher Date: Sun, 16 Jul 2017 17:54:57 +0200 Subject: [PATCH 1/5] Show mouse actions as keys with a symbol Support Left, Middle, Right XButton1, XButton2 and ScrollWheel. To support MouseWheel VolumeUp and VolumeDown where used as substitute as to not have to refactor the whole application. --- src/Carnac.Logic/Carnac.Logic.csproj | 4 + src/Carnac.Logic/KeyProvider.cs | 7 +- src/Carnac.Logic/Models/Message.cs | 3 +- .../MouseMonitor/InterceptMouse.cs | 112 ++++++ src/Carnac.Logic/packages.config | 1 + src/Carnac/UI/KeyShowView.xaml | 339 +++++++++++++++++- src/Carnac/packages.config | 1 + 7 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 src/Carnac.Logic/MouseMonitor/InterceptMouse.cs diff --git a/src/Carnac.Logic/Carnac.Logic.csproj b/src/Carnac.Logic/Carnac.Logic.csproj index 3fc10979..f66d0c33 100644 --- a/src/Carnac.Logic/Carnac.Logic.csproj +++ b/src/Carnac.Logic/Carnac.Logic.csproj @@ -38,6 +38,9 @@ false + + ..\packages\MouseKeyHook.5.4.0\lib\net40\Gma.System.MouseKeyHook.dll + @@ -92,6 +95,7 @@ + diff --git a/src/Carnac.Logic/KeyProvider.cs b/src/Carnac.Logic/KeyProvider.cs index d5272adb..1bab4742 100644 --- a/src/Carnac.Logic/KeyProvider.cs +++ b/src/Carnac.Logic/KeyProvider.cs @@ -10,6 +10,8 @@ using Carnac.Logic.Models; using Microsoft.Win32; using System.Windows.Media; +using Carnac.Logic.MouseMonitor; + namespace Carnac.Logic { @@ -66,7 +68,10 @@ public IObservable GetKeyStream() winKeyPressed = false; }, observer.OnError); - var keyStreamSubsription = interceptKeysSource.GetKeyStream() + var keyStreamSubsription = Observable.Merge( + new IObservable[2] { + interceptKeysSource.GetKeyStream(), + InterceptMouse.Current.GetKeyStream() }) .Select(DetectWindowsKey) .Where(k => !IsModifierKeyPress(k) && k.KeyDirection == KeyDirection.Down) .Select(ToCarnacKeyPress) diff --git a/src/Carnac.Logic/Models/Message.cs b/src/Carnac.Logic/Models/Message.cs index 415492b0..d612c4b6 100644 --- a/src/Carnac.Logic/Models/Message.cs +++ b/src/Carnac.Logic/Models/Message.cs @@ -29,7 +29,8 @@ public Message(KeyPress key) { processName = key.Process.ProcessName; processIcon = key.Process.ProcessIcon; - canBeMerged = !key.HasModifierPressed; + // allow to aggregate all key combination as to not spam if ctrl + mousewheel is used. + canBeMerged = true; // !key.HasModifierPressed; keys = new ReadOnlyCollection(new[] { key }); textCollection = new ReadOnlyCollection(CreateTextSequence(key).ToArray()); diff --git a/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs new file mode 100644 index 00000000..bfe8de02 --- /dev/null +++ b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs @@ -0,0 +1,112 @@ +using Carnac.Logic.KeyMonitor; +using System; +using System.Diagnostics; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Windows.Forms; +using Gma.System.MouseKeyHook; + +namespace Carnac.Logic.MouseMonitor +{ + class InterceptMouse : IInterceptKeys + { + + public static readonly InterceptMouse Current = new InterceptMouse(); + readonly IObservable keyStream; + private IObserver observer; + private readonly KeysConverter kc = new KeysConverter(); + + InterceptMouse() + { + keyStream = Observable.Create(observer => + { + this.observer = observer; + var m_GlobalHook = Hook.GlobalEvents(); + + m_GlobalHook.MouseClick += OnMouseClick; + m_GlobalHook.MouseDoubleClick += OnMouseDoubleClick; + m_GlobalHook.MouseWheel += HookManager_MouseWheel; + m_GlobalHook.MouseMove += HookManager_MouseMove; + Debug.Write("Subscribed to mouse"); + + return Disposable.Create(() => + { + m_GlobalHook.MouseClick -= OnMouseClick; + m_GlobalHook.MouseDoubleClick -= OnMouseDoubleClick; + m_GlobalHook.MouseWheel -= HookManager_MouseWheel; + m_GlobalHook.MouseMove -= HookManager_MouseMove; + m_GlobalHook.Dispose(); + Debug.Write("Unsubscribed from mouse"); + }); + }) + .Publish().RefCount(); + + } + + private Keys MouseButtonsToKeys(MouseButtons button) + { + switch(button) + { + case MouseButtons.Left: + return Keys.LButton; + case MouseButtons.Middle: + return Keys.MButton; + case MouseButtons.Right: + return Keys.RButton; + case MouseButtons.XButton1: + return Keys.XButton1; + case MouseButtons.XButton2: + return Keys.XButton2; + default: + return Keys.None; + } + } + + private void OnMouseClick(object sender, MouseEventArgs e) + { + Debug.WriteLine(string.Format("MouseClick \t\t {0} {1}\n", e.Button, Control.ModifierKeys)); + + observer.OnNext(new InterceptKeyEventArgs( + MouseButtonsToKeys(e.Button), + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + private void OnMouseDoubleClick(object sender, MouseEventArgs e) + { + Debug.WriteLine(string.Format("MouseDoubleClick \t\t {0}\n", e.Button)); + observer.OnNext(new InterceptKeyEventArgs( + MouseButtonsToKeys(e.Button), + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + private void HookManager_MouseWheel(object sender, MouseEventArgs e) + { + Debug.WriteLine(string.Format("Wheel={0:000}", e.Delta)); + // for now using VolumeDown and Up as proxy could be refactored + observer.OnNext(new InterceptKeyEventArgs( + e.Delta > 0 ? Keys.VolumeUp : Keys.VolumeDown, + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + private void HookManager_MouseMove(object sender, MouseEventArgs e) + { + Debug.WriteLine(string.Format("x={0:0000}; y={1:0000}", e.X, e.Y)); + } + + public IObservable GetKeyStream() + { + return keyStream; + } + + } +} + diff --git a/src/Carnac.Logic/packages.config b/src/Carnac.Logic/packages.config index acb9c233..511e3dcb 100644 --- a/src/Carnac.Logic/packages.config +++ b/src/Carnac.Logic/packages.config @@ -1,6 +1,7 @@  + diff --git a/src/Carnac/UI/KeyShowView.xaml b/src/Carnac/UI/KeyShowView.xaml index 914ce248..8fd3d558 100644 --- a/src/Carnac/UI/KeyShowView.xaml +++ b/src/Carnac/UI/KeyShowView.xaml @@ -16,7 +16,6 @@ - @@ -55,6 +54,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -182,7 +454,70 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Carnac/packages.config b/src/Carnac/packages.config index f61f5d46..dcd43146 100644 --- a/src/Carnac/packages.config +++ b/src/Carnac/packages.config @@ -6,6 +6,7 @@ + From 53d744f9d038b2884d3d522a601debeaa03299fb Mon Sep 17 00:00:00 2001 From: Boris Fritscher Date: Sun, 16 Jul 2017 21:31:38 +0200 Subject: [PATCH 2/5] Visual circles arround mouse cursor and preferences menu --- src/Carnac.Logic/Models/PopupSettings.cs | 31 +++++++ .../MouseMonitor/InterceptMouse.cs | 17 +--- src/Carnac/Carnac.csproj | 3 + src/Carnac/UI/KeyShowView.xaml | 34 +++++++- src/Carnac/UI/KeyShowView.xaml.cs | 65 ++++++++++++++- src/Carnac/UI/KeyShowViewModel.cs | 8 ++ src/Carnac/UI/PreferencesView.xaml | 82 ++++++++++++++++++- src/Carnac/UI/PreferencesViewModel.cs | 27 +++++- 8 files changed, 247 insertions(+), 20 deletions(-) diff --git a/src/Carnac.Logic/Models/PopupSettings.cs b/src/Carnac.Logic/Models/PopupSettings.cs index 8331f73f..19a8552d 100644 --- a/src/Carnac.Logic/Models/PopupSettings.cs +++ b/src/Carnac.Logic/Models/PopupSettings.cs @@ -85,5 +85,36 @@ public string SortDescription public bool DetectShortcutsOnly { get; set; } public bool ShowApplicationIcon { get; set; } public bool SettingsConfigured { get; set; } + + [DefaultValue(true)] + public bool ShowMouseClicks { get; set; } + + [DefaultValue("OrangeRed")] + public string LeftClickColor { get; set; } + + [DefaultValue("RoyalBlue")] + public string RightClickColor { get; set; } + + [DefaultValue(1)] + public double ClickStartScale { get; set; } + + [DefaultValue(4)] + public double ClickStopScale { get; set; } + + [DefaultValue(3700)] + public int ClickFadeDelay { get; set; } + + [DefaultValue(1)] + public double ClickStartBorder { get; set; } + + [DefaultValue(0.8)] + public double ClickStartOpacity { get; set; } + + [DefaultValue(2)] + public double ClickStopBorder { get; set; } + + [DefaultValue(0)] + public double ClickStopOpacity { get; set; } + public string ClickColor { get; set; } } } diff --git a/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs index bfe8de02..e816aa16 100644 --- a/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs +++ b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs @@ -8,25 +8,24 @@ namespace Carnac.Logic.MouseMonitor { - class InterceptMouse : IInterceptKeys + public class InterceptMouse : IInterceptKeys { public static readonly InterceptMouse Current = new InterceptMouse(); + public readonly IKeyboardMouseEvents m_GlobalHook = Hook.GlobalEvents(); readonly IObservable keyStream; private IObserver observer; private readonly KeysConverter kc = new KeysConverter(); + InterceptMouse() { keyStream = Observable.Create(observer => { this.observer = observer; - var m_GlobalHook = Hook.GlobalEvents(); - m_GlobalHook.MouseClick += OnMouseClick; m_GlobalHook.MouseDoubleClick += OnMouseDoubleClick; m_GlobalHook.MouseWheel += HookManager_MouseWheel; - m_GlobalHook.MouseMove += HookManager_MouseMove; Debug.Write("Subscribed to mouse"); return Disposable.Create(() => @@ -34,7 +33,6 @@ class InterceptMouse : IInterceptKeys m_GlobalHook.MouseClick -= OnMouseClick; m_GlobalHook.MouseDoubleClick -= OnMouseDoubleClick; m_GlobalHook.MouseWheel -= HookManager_MouseWheel; - m_GlobalHook.MouseMove -= HookManager_MouseMove; m_GlobalHook.Dispose(); Debug.Write("Unsubscribed from mouse"); }); @@ -64,8 +62,6 @@ private Keys MouseButtonsToKeys(MouseButtons button) private void OnMouseClick(object sender, MouseEventArgs e) { - Debug.WriteLine(string.Format("MouseClick \t\t {0} {1}\n", e.Button, Control.ModifierKeys)); - observer.OnNext(new InterceptKeyEventArgs( MouseButtonsToKeys(e.Button), KeyDirection.Down, @@ -76,7 +72,6 @@ private void OnMouseClick(object sender, MouseEventArgs e) private void OnMouseDoubleClick(object sender, MouseEventArgs e) { - Debug.WriteLine(string.Format("MouseDoubleClick \t\t {0}\n", e.Button)); observer.OnNext(new InterceptKeyEventArgs( MouseButtonsToKeys(e.Button), KeyDirection.Down, @@ -87,7 +82,6 @@ private void OnMouseDoubleClick(object sender, MouseEventArgs e) private void HookManager_MouseWheel(object sender, MouseEventArgs e) { - Debug.WriteLine(string.Format("Wheel={0:000}", e.Delta)); // for now using VolumeDown and Up as proxy could be refactored observer.OnNext(new InterceptKeyEventArgs( e.Delta > 0 ? Keys.VolumeUp : Keys.VolumeDown, @@ -97,11 +91,6 @@ private void HookManager_MouseWheel(object sender, MouseEventArgs e) Control.ModifierKeys == Keys.Shift)); } - private void HookManager_MouseMove(object sender, MouseEventArgs e) - { - Debug.WriteLine(string.Format("x={0:0000}; y={1:0000}", e.X, e.Y)); - } - public IObservable GetKeyStream() { return keyStream; diff --git a/src/Carnac/Carnac.csproj b/src/Carnac/Carnac.csproj index 04636d02..6cbc23f8 100644 --- a/src/Carnac/Carnac.csproj +++ b/src/Carnac/Carnac.csproj @@ -87,6 +87,9 @@ ..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.PatchApi.dll + + ..\packages\MouseKeyHook.5.4.0\lib\net40\Gma.System.MouseKeyHook.dll + ..\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll diff --git a/src/Carnac/UI/KeyShowView.xaml b/src/Carnac/UI/KeyShowView.xaml index 8fd3d558..03e05521 100644 --- a/src/Carnac/UI/KeyShowView.xaml +++ b/src/Carnac/UI/KeyShowView.xaml @@ -326,10 +326,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + \ No newline at end of file diff --git a/src/Carnac/UI/KeyShowView.xaml.cs b/src/Carnac/UI/KeyShowView.xaml.cs index 149362be..b674b179 100644 --- a/src/Carnac/UI/KeyShowView.xaml.cs +++ b/src/Carnac/UI/KeyShowView.xaml.cs @@ -3,16 +3,21 @@ using System.Timers; using System.Windows; using System.Windows.Interop; +using System.Windows.Media.Animation; using Carnac.Logic; +using Carnac.Logic.MouseMonitor; namespace Carnac.UI { public partial class KeyShowView { + private Storyboard sb; + public KeyShowView(KeyShowViewModel keyShowViewModel) { DataContext = keyShowViewModel; InitializeComponent(); + keyShowViewModel.Settings.PropertyChanged += Settings_PropertyChanged; } protected override void OnSourceInitialized(EventArgs e) @@ -38,6 +43,10 @@ protected override void OnSourceInitialized(EventArgs e) Left = vm.Settings.Left; vm.Settings.LeftChanged += SettingsLeftChanged; WindowState = WindowState.Maximized; + if (vm.Settings.ShowMouseClicks) + { + SetupMouseEvents(); + } } [DllImport("user32.dll", SetLastError = true)] @@ -83,7 +92,7 @@ public static readonly int private void WindowLoaded(object sender, RoutedEventArgs e) { - + sb = this.FindResource("clickHighlighterStoryboard") as Storyboard; } void SettingsLeftChanged(object sender, EventArgs e) @@ -93,5 +102,59 @@ void SettingsLeftChanged(object sender, EventArgs e) Left = vm.Settings.Left; WindowState = WindowState.Maximized; } + + void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + switch (e.PropertyName) + { + case "ClickFadeDelay": + Duration d = TimeSpan.FromMilliseconds(vm.Settings.ClickFadeDelay); + foreach(DoubleAnimation da in sb.Children) + { + da.Duration = d; + } + break; + case "ShowMouseClicks": + if (vm.Settings.ShowMouseClicks) + { + SetupMouseEvents(); + } + else + { + DestroyMouseEvents(); + } + break; + } + } + + void SetupMouseEvents() + { + InterceptMouse.Current.m_GlobalHook.MouseDown += OnMouseDown; + InterceptMouse.Current.m_GlobalHook.MouseMove += OnMouseMove; + } + + void DestroyMouseEvents() + { + InterceptMouse.Current.m_GlobalHook.MouseDown -= OnMouseDown; + InterceptMouse.Current.m_GlobalHook.MouseMove -= OnMouseMove; + } + + private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + vm.Settings.ClickColor = vm.Settings.LeftClickColor; + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + vm.Settings.ClickColor = vm.Settings.RightClickColor; + } + sb.Begin(); + } + + private void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + vm.CursorPosition = PointFromScreen(new Point(e.X, e.Y)); + } } } diff --git a/src/Carnac/UI/KeyShowViewModel.cs b/src/Carnac/UI/KeyShowViewModel.cs index 590608cf..98c8a412 100644 --- a/src/Carnac/UI/KeyShowViewModel.cs +++ b/src/Carnac/UI/KeyShowViewModel.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Windows; using Carnac.Logic; using Carnac.Logic.Models; @@ -15,5 +16,12 @@ public KeyShowViewModel(PopupSettings popupSettings) public ObservableCollection Messages { get; private set; } public PopupSettings Settings { get; set; } + + public Point CursorPosition { get; set; } + + public Thickness CursorMargins + { + get { return new Thickness(CursorPosition.X - 10, CursorPosition.Y - 10 , 0, 0); } + } } } diff --git a/src/Carnac/UI/PreferencesView.xaml b/src/Carnac/UI/PreferencesView.xaml index bcbcecac..47f9af43 100644 --- a/src/Carnac/UI/PreferencesView.xaml +++ b/src/Carnac/UI/PreferencesView.xaml @@ -91,7 +91,7 @@ - + @@ -151,6 +151,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +