From d881fb0de14912fde7b605a2200cb9701a36bc98 Mon Sep 17 00:00:00 2001 From: TransposonY <ccfyzh@gmail.com> Date: Thu, 12 Jan 2017 14:13:13 +0800 Subject: [PATCH] HotKey support --- .../Applications/ApplicationManager.cs | 20 +- GestureSign.Common/Gestures/Gesture.cs | 10 + GestureSign.Common/Plugins/PluginManager.cs | 25 +- .../Dialogs/GestureDefinition.cs | 47 +++- .../Dialogs/GestureDefinition.xaml | 110 +++++--- .../Languages/ControlPanel/en.xml | 2 + .../Languages/ControlPanel/zh.xml | 2 + GestureSign.Daemon/GestureSign.Daemon.csproj | 5 + GestureSign.Daemon/Program.cs | 4 +- .../Triggers/GestureNameEventArgs.cs | 15 + GestureSign.Daemon/Triggers/HotKeyManager.cs | 79 ++++++ GestureSign.Daemon/Triggers/Trigger.cs | 20 ++ GestureSign.Daemon/Triggers/TriggerManager.cs | 85 ++++++ ManagedWinapi/Hotkey.cs | 264 ++++++++++++++++++ ManagedWinapi/ManagedWinapi.csproj | 6 +- .../Windows/EventDispatchingNativeWindow.cs | 94 +++++++ ManagedWinapi/{ => Windows}/PInvokeTypes.cs | 0 ManagedWinapi/{ => Windows}/SystemWindow.cs | 0 18 files changed, 710 insertions(+), 78 deletions(-) create mode 100644 GestureSign.Daemon/Triggers/GestureNameEventArgs.cs create mode 100644 GestureSign.Daemon/Triggers/HotKeyManager.cs create mode 100644 GestureSign.Daemon/Triggers/Trigger.cs create mode 100644 GestureSign.Daemon/Triggers/TriggerManager.cs create mode 100644 ManagedWinapi/Hotkey.cs create mode 100644 ManagedWinapi/Windows/EventDispatchingNativeWindow.cs rename ManagedWinapi/{ => Windows}/PInvokeTypes.cs (100%) rename ManagedWinapi/{ => Windows}/SystemWindow.cs (100%) diff --git a/GestureSign.Common/Applications/ApplicationManager.cs b/GestureSign.Common/Applications/ApplicationManager.cs index e7ea07d..8e10bb2 100644 --- a/GestureSign.Common/Applications/ApplicationManager.cs +++ b/GestureSign.Common/Applications/ApplicationManager.cs @@ -12,7 +12,6 @@ using GestureSign.Common.Input; using GestureSign.Common.InterProcessCommunication; using ManagedWinapi.Windows; -using Action = GestureSign.Applications.Action; namespace GestureSign.Common.Applications { @@ -119,12 +118,14 @@ protected void TouchCapture_CaptureStarted(object sender, PointsCapturedEventArg else limitNumberFlag |= e.Points.Count < userApplication.LimitNumberOfFingers; } - - IgnoredApplication ignoredApplication = app as IgnoredApplication; - if (ignoredApplication != null && ignoredApplication.IsEnabled) + else { - e.Cancel = true; - return; + IgnoredApplication ignoredApplication = app as IgnoredApplication; + if (ignoredApplication != null && ignoredApplication.IsEnabled) + { + e.Cancel = true; + return; + } } } e.Cancel = limitNumberFlag ?? e.Points.Count == 1; @@ -363,6 +364,13 @@ public IApplication[] FindMatchApplications<TApplication>(MatchUsing matchUsing, excludedApplication != a.Name).ToArray(); } + public SystemWindow GetForegroundApplications() + { + CaptureWindow = SystemWindow.ForegroundWindow; + RecognizedApplication = GetApplicationFromWindow(CaptureWindow); + return CaptureWindow; + } + #endregion #region Private Methods diff --git a/GestureSign.Common/Gestures/Gesture.cs b/GestureSign.Common/Gestures/Gesture.cs index f78b260..052068f 100644 --- a/GestureSign.Common/Gestures/Gesture.cs +++ b/GestureSign.Common/Gestures/Gesture.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Serialization; +using ManagedWinapi; namespace GestureSign.Common.Gestures { @@ -27,6 +28,15 @@ public Gesture(string name, PointPattern[] pointPatterns) [DataMember] public PointPattern[] PointPatterns { get; set; } + [DataMember] + public Hotkey Hotkey { get; set; } + + public bool Equals(Gesture other) + { + if (other == null) return false; + return Name != null && Name.Equals(other.Name) && Hotkey != null && Hotkey.Equals(other.Hotkey); + } + #endregion } } diff --git a/GestureSign.Common/Plugins/PluginManager.cs b/GestureSign.Common/Plugins/PluginManager.cs index 374558e..73ce881 100644 --- a/GestureSign.Common/Plugins/PluginManager.cs +++ b/GestureSign.Common/Plugins/PluginManager.cs @@ -46,19 +46,27 @@ protected PluginManager() protected void TouchCapture_GestureRecognized(object sender, RecognitionEventArgs e) { var touchCapture = (ITouchCapture)sender; + ExecuteAction(touchCapture.Mode, e.GestureName, e.ContactIdentifiers, e.FirstCapturedPoints, e.Points); + } + + #endregion + + #region Public Methods + + public void ExecuteAction(CaptureMode mode, string gestureName, List<int> contactIdentifiers, List<Point> firstCapturedPoints, List<List<Point>> points) + { // Exit if we're teaching - if (touchCapture.Mode == CaptureMode.Training) + if (mode == CaptureMode.Training) return; - // Get action to be executed - IEnumerable<IAction> executableActions = ApplicationManager.Instance.GetRecognizedDefinedAction(e.GestureName); + IEnumerable<IAction> executableActions = ApplicationManager.Instance.GetRecognizedDefinedAction(gestureName); foreach (IAction executableAction in executableActions) { // Exit if there is no action configured if (executableAction == null || !executableAction.IsEnabled || - (touchCapture.Mode == CaptureMode.UserDisabled && + (mode == CaptureMode.UserDisabled && !"GestureSign.CorePlugins.ToggleDisableGestures".Equals(executableAction.PluginClass)) || - !Compute(executableAction.Condition, e.Points, e.ContactIdentifiers)) + !Compute(executableAction.Condition, points, contactIdentifiers)) continue; // Locate the plugin associated with this action @@ -71,13 +79,10 @@ protected void TouchCapture_GestureRecognized(object sender, RecognitionEventArg // Load action settings into plugin pluginInfo.Plugin.Deserialize(executableAction.ActionSettings); // Execute plugin process - pluginInfo.Plugin.Gestured(new PointInfo(e.FirstCapturedPoints, e.Points)); + pluginInfo.Plugin.Gestured(new PointInfo(firstCapturedPoints, points)); } - } - - #endregion - #region Public Methods + } public bool LoadPlugins(IHostControl host) { diff --git a/GestureSign.ControlPanel/Dialogs/GestureDefinition.cs b/GestureSign.ControlPanel/Dialogs/GestureDefinition.cs index 0ff89c2..a727df4 100644 --- a/GestureSign.ControlPanel/Dialogs/GestureDefinition.cs +++ b/GestureSign.ControlPanel/Dialogs/GestureDefinition.cs @@ -8,7 +8,9 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; using System.Windows.Media; +using ManagedWinapi; namespace GestureSign.ControlPanel.Dialogs { @@ -26,11 +28,12 @@ public GestureDefinition() public GestureDefinition(IGesture gesture) : this() { + _oldGesture = (Gesture)gesture; _currentPointPatterns = gesture.PointPatterns; - GestureManager.Instance.GestureName = _oldGestureName = gesture.Name; + GestureManager.Instance.GestureName = gesture.Name; } - private string _oldGestureName; + private Gesture _oldGesture; private string _similarGestureName; private PointPattern[] _currentPointPatterns; private Color _color; @@ -70,7 +73,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) if (_currentPointPatterns != null) imgGestureThumbnail.Source = GestureImage.CreateImage(_currentPointPatterns, new Size(65, 65), _color); - if (_oldGestureName == null) + if (_oldGesture == null) { NamedPipe.SendMessageAsync("StartTeaching", "GestureSignDaemon"); Title = LocalizationProvider.Instance.GetTextValue("GestureDefinition.Title"); @@ -78,8 +81,10 @@ private void Window_Loaded(object sender, RoutedEventArgs e) else { Title = LocalizationProvider.Instance.GetTextValue("GestureDefinition.Rename"); - txtGestureName.Text = _oldGestureName; //this.txtGestureName.Text - + txtGestureName.Text = _oldGesture.Name; //this.txtGestureName.Text + var hotkey = ((Gesture)_oldGesture).Hotkey; + if (hotkey != null) + HotKeyTextBox.HotKey = new HotKey(KeyInterop.KeyFromVirtualKey(hotkey.KeyCode), (ModifierKeys)hotkey.ModifierKeys); DrawGestureTextBlock.Visibility = ResetButton.Visibility = StackUpGestureButton.Visibility = Visibility.Collapsed; txtGestureName.Focus(); @@ -91,7 +96,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) private async void Window_Closing(object sender, CancelEventArgs e) { //Non-renaming mode - if (_oldGestureName == null) + if (_oldGesture == null) await NamedPipe.SendMessageAsync("StopTraining", "GestureSignDaemon"); } @@ -114,7 +119,7 @@ private void cmdCancel_Click(object sender, RoutedEventArgs e) private void txtGestureName_TextChanged(object sender, TextChangedEventArgs e) { string newGestureName = txtGestureName.Text.Trim(); - if (_oldGestureName == newGestureName || SimilarGestureName == newGestureName) return; + if (_oldGesture?.Name == newGestureName || SimilarGestureName == newGestureName) return; txtGestureName.GetBindingExpression(TextBox.TextProperty).UpdateSource(); } @@ -178,16 +183,30 @@ private bool SaveGesture() if (_currentPointPatterns == null || _currentPointPatterns.Length == 0) return false; - //Rename mode - if (_oldGestureName != null) + var newGesture = new Gesture() + { + Name = newGestureName, + PointPatterns = _currentPointPatterns, + Hotkey = HotKeyTextBox.HotKey != null ? + new Hotkey() + { + KeyCode = KeyInterop.VirtualKeyFromKey(HotKeyTextBox.HotKey.Key), + ModifierKeys = (int)HotKeyTextBox.HotKey.ModifierKeys + } : null + }; + + if (_oldGesture != null) { - if (_oldGestureName.Equals(newGestureName)) return true; - if (GestureManager.Instance.GestureExists(newGestureName)) + //Edit gesture + if (_oldGesture.Equals(newGesture)) return true; + if (!_oldGesture.Name.Equals(newGestureName) && GestureManager.Instance.GestureExists(newGestureName)) { txtGestureName.GetBindingExpression(TextBox.TextProperty).UpdateSource(); return false; } - GestureManager.Instance.RenameGesture(_oldGestureName, newGestureName); + GestureManager.Instance.RenameGesture(_oldGesture.Name, newGestureName); + GestureManager.Instance.DeleteGesture(newGestureName); + GestureManager.Instance.AddGesture(newGesture); } else { @@ -199,7 +218,7 @@ private bool SaveGesture() return false; } // Add new gesture to gesture manager - GestureManager.Instance.AddGesture(new Gesture(newGestureName, _currentPointPatterns)); + GestureManager.Instance.AddGesture(newGesture); } else { @@ -214,7 +233,7 @@ private bool SaveGesture() GestureManager.Instance.DeleteGesture(newGestureName); // Add new gesture to gesture manager - GestureManager.Instance.AddGesture(new Gesture(newGestureName, _currentPointPatterns)); + GestureManager.Instance.AddGesture(newGesture); } } GestureManager.Instance.GestureName = newGestureName; diff --git a/GestureSign.ControlPanel/Dialogs/GestureDefinition.xaml b/GestureSign.ControlPanel/Dialogs/GestureDefinition.xaml index 0c5eb48..e8602a7 100644 --- a/GestureSign.ControlPanel/Dialogs/GestureDefinition.xaml +++ b/GestureSign.ControlPanel/Dialogs/GestureDefinition.xaml @@ -21,51 +21,71 @@ <controls:MetroWindow.Resources> </controls:MetroWindow.Resources> <StackPanel> - <TextBlock x:Name="DrawGestureTextBlock" - FontSize="14" - FontWeight="Bold" - Margin="12,10,12,0" - Text="{localization:LocalisedText GestureDefinition.DrawGesture}" - HorizontalAlignment="Center" /> - <Image x:Name="imgGestureThumbnail" - Height="65" - Margin="0,10,0,0" - StretchDirection="DownOnly" /> - <Button x:Name="ResetButton" - Content="{localization:LocalisedText GestureDefinition.Reset}" - Style="{DynamicResource SquareButtonStyle}" - Width="100" - HorizontalAlignment="Left" - BorderThickness="1" - BorderBrush="{StaticResource HighlightBrush}" - Margin="20,5,0,0" - Click="ResetButton_Click" - controls:ControlsHelper.ContentCharacterCasing="Normal" - IsEnabled="False" - Focusable="False" /> - <Button x:Name="StackUpGestureButton" - Content="{localization:LocalisedText GestureDefinition.StackUpGesture}" - Style="{DynamicResource SquareButtonStyle}" - Width="100" - HorizontalAlignment="Right" - BorderThickness="1" - BorderBrush="{StaticResource HighlightBrush}" - Margin="0,-28,20,0" - Click="StackUpGestureButton_Click" - controls:ControlsHelper.ContentCharacterCasing="Normal" - IsEnabled="False" - Focusable="False" /> - <TextBlock x:Name="ExistingTextBlock" - TextWrapping="Wrap" - FontSize="14" - Margin="12,0" - Text="{localization:LocalisedText GestureDefinition.ExistingGesture}" - Visibility="Collapsed" /> - <Image x:Name="ExistingGestureImage" - Height="65" - StretchDirection="DownOnly" - Margin="5" - Visibility="Collapsed" /> + <TabControl> + <TabItem controls:ControlsHelper.HeaderFontSize="18" + Header="{localization:LocalisedText Gesture.Header}"> + <StackPanel> + <TextBlock x:Name="DrawGestureTextBlock" + FontSize="14" + FontWeight="Bold" + Margin="12,10,12,0" + Text="{localization:LocalisedText GestureDefinition.DrawGesture}" + HorizontalAlignment="Center" /> + <Image x:Name="imgGestureThumbnail" + Height="65" + Margin="0,10,0,0" + StretchDirection="DownOnly" /> + <Button x:Name="ResetButton" + Content="{localization:LocalisedText GestureDefinition.Reset}" + Style="{DynamicResource SquareButtonStyle}" + Width="100" + HorizontalAlignment="Left" + BorderThickness="1" + BorderBrush="{StaticResource HighlightBrush}" + Margin="20,5,0,0" + Click="ResetButton_Click" + controls:ControlsHelper.ContentCharacterCasing="Normal" + IsEnabled="False" + Focusable="False" /> + <Button x:Name="StackUpGestureButton" + Content="{localization:LocalisedText GestureDefinition.StackUpGesture}" + Style="{DynamicResource SquareButtonStyle}" + Width="100" + HorizontalAlignment="Right" + BorderThickness="1" + BorderBrush="{StaticResource HighlightBrush}" + Margin="0,-28,20,0" + Click="StackUpGestureButton_Click" + controls:ControlsHelper.ContentCharacterCasing="Normal" + IsEnabled="False" + Focusable="False" /> + <TextBlock x:Name="ExistingTextBlock" + TextWrapping="Wrap" + FontSize="14" + Margin="12,0" + Text="{localization:LocalisedText GestureDefinition.ExistingGesture}" + Visibility="Collapsed" /> + <Image x:Name="ExistingGestureImage" + Height="65" + StretchDirection="DownOnly" + Margin="5" + Visibility="Collapsed" /> + </StackPanel> + </TabItem> + <TabItem Header="{localization:LocalisedText GestureDefinition.HotKey}" + controls:ControlsHelper.HeaderFontSize="18"> + <Canvas Height="50"> + <controls:HotKeyBox x:Name="HotKeyTextBox" + Width="313" + AreModifierKeysRequired="True" + Watermark="{localization:LocalisedText GestureDefinition.HotKeyWatermark}" + Canvas.Left="18" + Canvas.Top="15" + FontSize="14"> + </controls:HotKeyBox> + </Canvas> + </TabItem> + </TabControl> <TextBox x:Name="txtGestureName" controls:TextBoxHelper.Watermark="{localization:LocalisedText GestureDefinition.GestureNameWatermark}" TextWrapping="Wrap" diff --git a/GestureSign.ControlPanel/Languages/ControlPanel/en.xml b/GestureSign.ControlPanel/Languages/ControlPanel/en.xml index cd9536a..3a6eb83 100644 --- a/GestureSign.ControlPanel/Languages/ControlPanel/en.xml +++ b/GestureSign.ControlPanel/Languages/ControlPanel/en.xml @@ -161,6 +161,8 @@ Or click the button below to select the running applications.</GetMatchStringTip <StackUpGesture>Stack Up</StackUpGesture> <DrawGesture>Please draw a gesture</DrawGesture> <Reset>Reset</Reset> + <HotKey>HotKey</HotKey> + <HotKeyWatermark>Enter hot key</HotKeyWatermark> <Messages> <GestureExists>The Gesture Name "{0}" already exists, please provide a different Gesture Name</GestureExists> </Messages> diff --git a/GestureSign.ControlPanel/Languages/ControlPanel/zh.xml b/GestureSign.ControlPanel/Languages/ControlPanel/zh.xml index e75c3a6..583dd87 100644 --- a/GestureSign.ControlPanel/Languages/ControlPanel/zh.xml +++ b/GestureSign.ControlPanel/Languages/ControlPanel/zh.xml @@ -167,6 +167,8 @@ http://transposony.coding.me/GestureSign/ <StackUpGesture>叠加手势</StackUpGesture> <DrawGesture>请画出手势</DrawGesture> <Reset>重置</Reset> + <HotKey>快捷键</HotKey> + <HotKeyWatermark>可为该手势指定一个快捷键</HotKeyWatermark> <Messages> <GestureExists>输入的手势名称{0}已存在,请重新输入一个手势名称</GestureExists> </Messages> diff --git a/GestureSign.Daemon/GestureSign.Daemon.csproj b/GestureSign.Daemon/GestureSign.Daemon.csproj index e48bda3..6ee8a48 100644 --- a/GestureSign.Daemon/GestureSign.Daemon.csproj +++ b/GestureSign.Daemon/GestureSign.Daemon.csproj @@ -99,6 +99,10 @@ <SubType>Form</SubType> </Compile> <Compile Include="TrayManager.cs" /> + <Compile Include="Triggers\GestureNameEventArgs.cs" /> + <Compile Include="Triggers\HotKeyManager.cs" /> + <Compile Include="Triggers\Trigger.cs" /> + <Compile Include="Triggers\TriggerManager.cs" /> <EmbeddedResource Include="Properties\Resources.resx"> <Generator>ResXFileCodeGenerator</Generator> <SubType>Designer</SubType> @@ -162,6 +166,7 @@ <None Include="Resources\add.ico" /> <None Include="Resources\stop.ico" /> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <PropertyGroup> <PostBuildEvent> diff --git a/GestureSign.Daemon/Program.cs b/GestureSign.Daemon/Program.cs index 2d40f96..c8e4d32 100644 --- a/GestureSign.Daemon/Program.cs +++ b/GestureSign.Daemon/Program.cs @@ -13,6 +13,7 @@ using GestureSign.Common.Plugins; using GestureSign.Daemon.Input; using GestureSign.Daemon.Surface; +using GestureSign.Daemon.Triggers; namespace GestureSign.Daemon { @@ -46,6 +47,8 @@ static void Main() TouchCapture.Instance.Load(); _surfaceForm = new SurfaceForm(); + SynchronizationContext uiContext = SynchronizationContext.Current; + TriggerManager.Instance.Load(uiContext); if (!StartTouchInputProvider()) return; @@ -63,7 +66,6 @@ static void Main() PluginManager.Instance.Load(hostControl); TrayManager.Instance.Load(); - SynchronizationContext uiContext = SynchronizationContext.Current; NamedPipe.Instance.RunNamedPipeServer("GestureSignDaemon", new MessageProcessor(uiContext)); //if (TouchCapture.Instance.MessageWindow.NumberOfTouchscreens == 0) diff --git a/GestureSign.Daemon/Triggers/GestureNameEventArgs.cs b/GestureSign.Daemon/Triggers/GestureNameEventArgs.cs new file mode 100644 index 0000000..c28d9bc --- /dev/null +++ b/GestureSign.Daemon/Triggers/GestureNameEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace GestureSign.Daemon.Triggers +{ + public class GestureNameEventArgs : EventArgs + { + public GestureNameEventArgs(List<string> gestureName) + { + GestureName = gestureName; + } + + public List<string> GestureName { get; } + } +} diff --git a/GestureSign.Daemon/Triggers/HotKeyManager.cs b/GestureSign.Daemon/Triggers/HotKeyManager.cs new file mode 100644 index 0000000..cef5a83 --- /dev/null +++ b/GestureSign.Daemon/Triggers/HotKeyManager.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using GestureSign.Common.Gestures; +using ManagedWinapi; + +namespace GestureSign.Daemon.Triggers +{ + class HotKeyManager : Trigger + { + private Dictionary<Hotkey, List<string>> _hotKeyMap = new Dictionary<Hotkey, List<string>>(); + + public override bool LoadConfiguration(IGesture[] gestures) + { + UnloadHotKeys(); + return LoadHotKeys(gestures); + } + + private bool LoadHotKeys(IGesture[] gestures) + { + _hotKeyMap = new Dictionary<Hotkey, List<string>>(); + + if (gestures == null || gestures.Length == 0) return false; + + foreach (var g in gestures) + { + var h = ((Gesture)g).Hotkey; + + if (h != null && h.ModifierKeys != 0 && h.KeyCode != 0) + { + var hotKey = new Hotkey() { KeyCode = h.KeyCode, ModifierKeys = h.ModifierKeys }; + if (_hotKeyMap.ContainsKey(hotKey)) + { + var gestureNameList = _hotKeyMap[hotKey] ?? new List<string>(); + if (!gestureNameList.Contains(g.Name)) + gestureNameList.Add(g.Name); + } + else + { + InitializeHotKey(hotKey); + _hotKeyMap.Add(hotKey, new List<string>(new[] { g.Name })); + } + } + } + return true; + } + + private void InitializeHotKey(Hotkey hotkey) + { + try + { + hotkey.HotkeyPressed += Hotkey_HotkeyPressed; + hotkey.Register(); + } + catch (HotkeyAlreadyInUseException) + { + hotkey.Unregister(); + } + } + + private void UnloadHotKeys() + { + foreach (var hotKeyPair in _hotKeyMap) + { + hotKeyPair.Key.HotkeyPressed -= Hotkey_HotkeyPressed; + hotKeyPair.Key.Dispose(); + } + _hotKeyMap = null; + } + + private void Hotkey_HotkeyPressed(object sender, EventArgs e) + { + Hotkey hotkey = (Hotkey)sender; + if (_hotKeyMap.ContainsKey(hotkey)) + { + OnTriggerFired(new GestureNameEventArgs(_hotKeyMap[hotkey])); + } + } + } +} diff --git a/GestureSign.Daemon/Triggers/Trigger.cs b/GestureSign.Daemon/Triggers/Trigger.cs new file mode 100644 index 0000000..cc5f98b --- /dev/null +++ b/GestureSign.Daemon/Triggers/Trigger.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GestureSign.Common.Gestures; + +namespace GestureSign.Daemon.Triggers +{ + public abstract class Trigger + { + public event EventHandler<GestureNameEventArgs> TriggerFired; + public abstract bool LoadConfiguration(IGesture[] gestures); + + protected virtual void OnTriggerFired(GestureNameEventArgs e) + { + TriggerFired?.Invoke(this, e); + } + } +} diff --git a/GestureSign.Daemon/Triggers/TriggerManager.cs b/GestureSign.Daemon/Triggers/TriggerManager.cs new file mode 100644 index 0000000..fcae873 --- /dev/null +++ b/GestureSign.Daemon/Triggers/TriggerManager.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Threading; +using GestureSign.Common.Applications; +using GestureSign.Common.Gestures; +using GestureSign.Common.Plugins; +using GestureSign.Daemon.Input; + +namespace GestureSign.Daemon.Triggers +{ + class TriggerManager + { + #region Private Variables + + private List<Trigger> _triggerList = new List<Trigger>(); + private SynchronizationContext _synchronizationContext; + + #endregion + + #region Constructors + + static TriggerManager() + { + Instance = new TriggerManager(); + } + + #endregion + + #region Public Instance Properties + + public static TriggerManager Instance { get; } + + #endregion + + #region Public Methods + + public void Load(SynchronizationContext synchronizationContext) + { + _synchronizationContext = synchronizationContext; + AddTrigger(new HotKeyManager()); + GestureManager.OnLoadGesturesCompleted += GestureManager_OnLoadGesturesCompleted; + } + + #endregion + + + #region Private Methods + + private void GestureManager_OnLoadGesturesCompleted(object sender, EventArgs e) + { + _synchronizationContext.Send(state => { LoadConfig(((GestureManager)sender).Gestures); }, null); + } + + private void LoadConfig(IGesture[] gestures) + { + foreach (var trigger in _triggerList) + { + trigger.LoadConfiguration(gestures); + } + } + + private void AddTrigger(Trigger newTrigger) + { + newTrigger.TriggerFired += Trigger_TriggerFired; + _triggerList.Add(newTrigger); + } + + private void Trigger_TriggerFired(object sender, GestureNameEventArgs e) + { + var window = ApplicationManager.Instance.GetForegroundApplications(); + var point = new List<Point>(new[] { window.Rectangle.Location }); + foreach (var name in e.GestureName) + { + PluginManager.Instance.ExecuteAction(TouchCapture.Instance.Mode, + name, + new List<int>(new[] { 1 }), + point, + new List<List<Point>>(new[] { point })); + } + } + + #endregion + } +} diff --git a/ManagedWinapi/Hotkey.cs b/ManagedWinapi/Hotkey.cs new file mode 100644 index 0000000..121b1d2 --- /dev/null +++ b/ManagedWinapi/Hotkey.cs @@ -0,0 +1,264 @@ +/* + * ManagedWinapi - A collection of .NET components that wrap PInvoke calls to + * access native API by managed code. http://mwinapi.sourceforge.net/ + * Copyright (C) 2006 Michael Schierl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. if not, visit + * http://www.gnu.org/licenses/lgpl.html or write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using ManagedWinapi.Windows; + +namespace ManagedWinapi +{ + + /// <summary> + /// Specifies a component that creates a global keyboard hotkey. + /// </summary> + [DefaultEvent("HotkeyPressed")] + public class Hotkey : IDisposable//: Component + { + + /// <summary> + /// Occurs when the hotkey is pressed. + /// </summary> + public event EventHandler HotkeyPressed; + + private static Object _myStaticLock = new Object(); + private static int _hotkeyCounter = 0xA000; + + private readonly int _hotkeyIndex; + private bool _isDisposed = false, _isEnabled = false, _isRegistered = false; + private int _keyCode; + //private bool _ctrl, _alt, _shift, _windows; + private int _modifierKeys; + private readonly IntPtr hWnd; + private readonly EventDispatchingNativeWindow nativeWindow; + + ///// <summary> + ///// Initializes a new instance of this class with the specified container. + ///// </summary> + ///// <param name="container">The container to add it to.</param> + //public Hotkey(IContainer container) : this() + //{ + // container.Add(this); + //} + + /// <summary> + /// Initializes a new instance of this class. + /// </summary> + public Hotkey() + { + nativeWindow = EventDispatchingNativeWindow.Instance; + nativeWindow.EventHandler += nw_EventHandler; + lock (_myStaticLock) + { + _hotkeyIndex = ++_hotkeyCounter; + } + hWnd = nativeWindow.Handle; + } + + /// <summary> + /// Enables the hotkey. When the hotkey is enabled, pressing it causes a + /// <c>HotkeyPressed</c> event instead of being handled by the active + /// application. + /// </summary> + private bool Enabled + { + get + { + return _isEnabled; + } + set + { + _isEnabled = value; + updateHotkey(false); + } + } + + /// <summary> + /// The key code of the hotkey. + /// </summary> + public int KeyCode + { + get + { + return _keyCode; + } + + set + { + _keyCode = value; + updateHotkey(true); + } + } + + public int ModifierKeys + { + get { return _modifierKeys; } + set + { + _modifierKeys = value; + updateHotkey(true); + } + } + + ///// <summary> + ///// Whether the shortcut includes the Control modifier. + ///// </summary> + //public bool Ctrl { + // get { return _ctrl; } + // set {_ctrl = value; updateHotkey(true);} + //} + + ///// <summary> + ///// Whether this shortcut includes the Alt modifier. + ///// </summary> + //public bool Alt { + // get { return _alt; } + // set {_alt = value; updateHotkey(true);} + //} + + ///// <summary> + ///// Whether this shortcut includes the shift modifier. + ///// </summary> + //public bool Shift { + // get { return _shift; } + // set {_shift = value; updateHotkey(true);} + //} + + ///// <summary> + ///// Whether this shortcut includes the Windows key modifier. The windows key + ///// is an addition by Microsoft to the keyboard layout. It is located between + ///// Control and Alt and depicts a Windows flag. + ///// </summary> + //public bool WindowsKey { + // get { return _windows; } + // set {_windows = value; updateHotkey(true);} + //} + + public void Register() + { + Enabled = true; + } + + public void Unregister() + { + Enabled = false; + } + + public bool Equals(Hotkey other) + { + if (object.ReferenceEquals(null, other)) + return false; + if (object.ReferenceEquals(this, other)) + return true; + return Equals(other.KeyCode, this.KeyCode) && Equals(other.ModifierKeys, this.ModifierKeys); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(null, obj)) + return false; + if (object.ReferenceEquals(this, obj)) + return true; + return obj.GetType() == typeof(Hotkey) && this.Equals((Hotkey)obj); + } + + public override int GetHashCode() + { + return this.KeyCode.GetHashCode() * 397 ^ this.ModifierKeys.GetHashCode(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void nw_EventHandler(ref Message m, ref bool handled) + { + if (handled) return; + if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == _hotkeyIndex) + { + if (HotkeyPressed != null) + HotkeyPressed(this, EventArgs.Empty); + handled = true; + } + } + + /// <summary> + /// Releases all resources used by the System.ComponentModel.Component. + /// </summary> + /// <param name="disposing">Whether to dispose managed resources.</param> + protected void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + // Release managed resources + } + _isDisposed = true; + updateHotkey(false); + nativeWindow.EventHandler -= nw_EventHandler; + } + } + + private void updateHotkey(bool reregister) + { + bool shouldBeRegistered = _isEnabled && !_isDisposed; + if (_isRegistered && (!shouldBeRegistered || reregister)) + { + // unregister hotkey + UnregisterHotKey(hWnd, _hotkeyIndex); + _isRegistered = false; + } + if (!_isRegistered && shouldBeRegistered) + { + // register hotkey + bool success = RegisterHotKey(hWnd, _hotkeyIndex, _modifierKeys, _keyCode); + if (!success) throw new HotkeyAlreadyInUseException(); + _isRegistered = true; + } + } + + ~Hotkey() + { + Dispose(false); + } + + #region PInvoke Declarations + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); + [DllImport("user32.dll", SetLastError = true)] + private static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + private static readonly int WM_HOTKEY = 0x0312; + + #endregion + } + + /// <summary> + /// The exception is thrown when a hotkey should be registered that + /// has already been registered by another application. + /// </summary> + public class HotkeyAlreadyInUseException : Exception { } +} diff --git a/ManagedWinapi/ManagedWinapi.csproj b/ManagedWinapi/ManagedWinapi.csproj index 1db933e..d8d9ee9 100644 --- a/ManagedWinapi/ManagedWinapi.csproj +++ b/ManagedWinapi/ManagedWinapi.csproj @@ -64,11 +64,13 @@ </ItemGroup> <ItemGroup> <Compile Include="ApiHelper.cs" /> + <Compile Include="Hotkey.cs" /> <Compile Include="KeyboardKey.cs" /> - <Compile Include="PInvokeTypes.cs" /> + <Compile Include="Windows\EventDispatchingNativeWindow.cs" /> + <Compile Include="Windows\PInvokeTypes.cs" /> <Compile Include="SendKeysEscaper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="SystemWindow.cs" /> + <Compile Include="Windows\SystemWindow.cs" /> <Service Include="{94E38DFF-614B-4cbd-B67C-F211BB35CE8B}" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> diff --git a/ManagedWinapi/Windows/EventDispatchingNativeWindow.cs b/ManagedWinapi/Windows/EventDispatchingNativeWindow.cs new file mode 100644 index 0000000..528cf6e --- /dev/null +++ b/ManagedWinapi/Windows/EventDispatchingNativeWindow.cs @@ -0,0 +1,94 @@ +/* + * ManagedWinapi - A collection of .NET components that wrap PInvoke calls to + * access native API by managed code. http://mwinapi.sourceforge.net/ + * Copyright (C) 2006 Michael Schierl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. if not, visit + * http://www.gnu.org/licenses/lgpl.html or write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; + +namespace ManagedWinapi.Windows +{ + + /// <summary> + /// Called by an EventDispatchingNativeWindow when a window message is received + /// </summary> + /// <param name="m">The message to handle.</param> + /// <param name="handled">Whether the event has already been handled. If this value is true, the handler + /// should return immediately. It may set the value to true to indicate that no others + /// should handle it. If the event is not handled by any handler, it is passed to the + /// default WindowProc.</param> + public delegate void WndProcEventHandler(ref Message m, ref bool handled); + + /// <summary> + /// A Win32 native window that delegates window messages to handlers. So several + /// components can use the same native window to save "USER resources". This class + /// is useful when writing your own components. + /// </summary> + public class EventDispatchingNativeWindow : NativeWindow + { + + private static Object myLock = new Object(); + [ThreadStatic] + private static EventDispatchingNativeWindow _instance; + + /// <summary> + /// A global instance which can be used by components that do not need + /// their own window. + /// </summary> + public static EventDispatchingNativeWindow Instance + { + get + { + lock (myLock) + { + if (_instance == null) + _instance = new EventDispatchingNativeWindow(); + return _instance; + } + } + } + + /// <summary> + /// Attach your event handlers here. + /// </summary> + public event WndProcEventHandler EventHandler; + + /// <summary> + /// Create your own event dispatching window. + /// </summary> + public EventDispatchingNativeWindow() + { + CreateHandle(new CreateParams()); + } + + /// <summary> + /// Parse messages passed to this window and send them to the event handlers. + /// </summary> + /// <param name="m">A System.Windows.Forms.Message that is associated with the + /// current Windows message.</param> + protected override void WndProc(ref Message m) + { + bool handled = false; + if (EventHandler != null) + EventHandler(ref m, ref handled); + if (!handled) + base.WndProc(ref m); + } + } +} diff --git a/ManagedWinapi/PInvokeTypes.cs b/ManagedWinapi/Windows/PInvokeTypes.cs similarity index 100% rename from ManagedWinapi/PInvokeTypes.cs rename to ManagedWinapi/Windows/PInvokeTypes.cs diff --git a/ManagedWinapi/SystemWindow.cs b/ManagedWinapi/Windows/SystemWindow.cs similarity index 100% rename from ManagedWinapi/SystemWindow.cs rename to ManagedWinapi/Windows/SystemWindow.cs