mirror of https://github.com/quasar/Quasar.git
Merge pull request #305 from MaxXor/pr/304
Added Mouse Scrolling and Keyboard Input to Remote Desktop
This commit is contained in:
commit
c3f9b67938
|
@ -71,6 +71,7 @@
|
|||
<Compile Include="Core\Encryption\AES.cs" />
|
||||
<Compile Include="Core\Encryption\SHA256.cs" />
|
||||
<Compile Include="Core\Extensions\RegistryKeyExtensions.cs" />
|
||||
<Compile Include="Core\Packets\ServerPackets\DoKeyboardEvent.cs" />
|
||||
<Compile Include="Core\Utilities\FileSplit.cs" />
|
||||
<Compile Include="Core\Utilities\NativeMethods.cs" />
|
||||
<Compile Include="Core\Utilities\UnsafeStreamCodec.cs" />
|
||||
|
|
|
@ -81,16 +81,27 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command
|
|||
{
|
||||
case MouseAction.LeftDown:
|
||||
case MouseAction.LeftUp:
|
||||
NativeMethodsHelper.DoMouseEventLeft(p, command.IsMouseDown);
|
||||
NativeMethodsHelper.DoMouseLeftClick(p, command.IsMouseDown);
|
||||
break;
|
||||
case MouseAction.RightDown:
|
||||
case MouseAction.RightUp:
|
||||
NativeMethodsHelper.DoMouseEventRight(p, command.IsMouseDown);
|
||||
NativeMethodsHelper.DoMouseRightClick(p, command.IsMouseDown);
|
||||
break;
|
||||
case MouseAction.MoveCursor:
|
||||
NativeMethodsHelper.DoMouseMoveCursor(p);
|
||||
NativeMethodsHelper.DoMouseMove(p);
|
||||
break;
|
||||
}
|
||||
case MouseAction.ScrollDown:
|
||||
NativeMethodsHelper.DoMouseScroll(p, true);
|
||||
break;
|
||||
case MouseAction.ScrollUp:
|
||||
NativeMethodsHelper.DoMouseScroll(p, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleDoKeyboardEvent(Packets.ServerPackets.DoKeyboardEvent command, Client client)
|
||||
{
|
||||
NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown);
|
||||
}
|
||||
|
||||
public static void HandleGetMonitors(Packets.ServerPackets.GetMonitors command, Client client)
|
||||
|
|
|
@ -5,24 +5,37 @@ namespace xClient.Core.Helper
|
|||
{
|
||||
public static class NativeMethodsHelper
|
||||
{
|
||||
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
|
||||
private const int MOUSEEVENTF_LEFTUP = 0x04;
|
||||
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
|
||||
private const int MOUSEEVENTF_RIGHTUP = 0x10;
|
||||
private const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
|
||||
private const uint MOUSEEVENTF_LEFTUP = 0x0004;
|
||||
private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
|
||||
private const uint MOUSEEVENTF_RIGHTUP = 0x0010;
|
||||
private const uint MOUSEEVENTF_WHEEL = 0x0800;
|
||||
private const uint KEYEVENTF_KEYDOWN = 0x0000;
|
||||
private const uint KEYEVENTF_KEYUP = 0x0002;
|
||||
|
||||
public static void DoMouseEventLeft(Point p, bool isMouseDown)
|
||||
public static void DoMouseLeftClick(Point p, bool isMouseDown)
|
||||
{
|
||||
NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP, p.X, p.Y, 0, 0);
|
||||
}
|
||||
|
||||
public static void DoMouseEventRight(Point p, bool isMouseDown)
|
||||
public static void DoMouseRightClick(Point p, bool isMouseDown)
|
||||
{
|
||||
NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP, p.X, p.Y, 0, 0);
|
||||
}
|
||||
|
||||
public static void DoMouseMoveCursor(Point p)
|
||||
public static void DoMouseMove(Point p)
|
||||
{
|
||||
NativeMethods.SetCursorPos(p.X, p.Y);
|
||||
}
|
||||
|
||||
public static void DoMouseScroll(Point p, bool scrollDown)
|
||||
{
|
||||
NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0);
|
||||
}
|
||||
|
||||
public static void DoKeyPress(byte key, bool keyDown)
|
||||
{
|
||||
NativeMethods.keybd_event(key, 0, keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,10 @@ public static void HandlePacket(Client client, IPacket packet)
|
|||
{
|
||||
CommandHandler.HandleDoMouseEvent((ServerPackets.DoMouseEvent)packet, client);
|
||||
}
|
||||
else if (type == typeof(ServerPackets.DoKeyboardEvent))
|
||||
{
|
||||
CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent)packet, client);
|
||||
}
|
||||
else if (type == typeof(ServerPackets.GetSystemInfo))
|
||||
{
|
||||
CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo)packet, client);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using ProtoBuf;
|
||||
using xClient.Core.Networking;
|
||||
|
||||
namespace xClient.Core.Packets.ServerPackets
|
||||
{
|
||||
[ProtoContract]
|
||||
public class DoKeyboardEvent : IPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public byte Key { get; set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public bool KeyDown { get; set; }
|
||||
|
||||
public DoKeyboardEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public DoKeyboardEvent(byte key, bool keyDown)
|
||||
{
|
||||
this.Key = key;
|
||||
this.KeyDown = keyDown;
|
||||
}
|
||||
|
||||
public void Execute(Client client)
|
||||
{
|
||||
client.Send(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,10 @@ public static class NativeMethods
|
|||
public static extern bool SetCursorPos(int x, int y);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
||||
public static extern void mouse_event(uint dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a bit-block transfer of the color data corresponding to a
|
||||
|
|
|
@ -7,6 +7,8 @@ public enum MouseAction
|
|||
RightDown,
|
||||
RightUp,
|
||||
MoveCursor,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ private static void InitializeClient()
|
|||
typeof (Core.Packets.ServerPackets.GetDirectory),
|
||||
typeof (Core.Packets.ServerPackets.DoDownloadFile),
|
||||
typeof (Core.Packets.ServerPackets.DoMouseEvent),
|
||||
typeof (Core.Packets.ServerPackets.DoKeyboardEvent),
|
||||
typeof (Core.Packets.ServerPackets.GetSystemInfo),
|
||||
typeof (Core.Packets.ServerPackets.DoVisitWebsite),
|
||||
typeof (Core.Packets.ServerPackets.DoShowMessageBox),
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the class to start with.
|
||||
/// </summary>
|
||||
public static class Hook
|
||||
{
|
||||
/// <summary>
|
||||
/// Here you find all application wide events. Both mouse and keyboard.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returned instance is used for event subscriptions.
|
||||
/// You can refetch it (you will get the same instance anyway).
|
||||
/// </returns>
|
||||
public static IKeyboardMouseEvents AppEvents()
|
||||
{
|
||||
return new AppEventFacade();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Here you find all application wide events. Both mouse and keyboard.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returned instance is used for event subscriptions.
|
||||
/// You can refetch it (you will get the same instance anyway).
|
||||
/// </returns>
|
||||
public static IKeyboardMouseEvents GlobalEvents()
|
||||
{
|
||||
return new GlobalEventFacade();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.HotKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// The event arguments passed when a HotKeySet's OnHotKeysDownHold event is triggered.
|
||||
/// </summary>
|
||||
public sealed class HotKeyArgs : EventArgs
|
||||
{
|
||||
private readonly DateTime m_TimeOfExecution;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the HotKeyArgs.
|
||||
/// <param name="triggeredAt">Time when the event was triggered</param>
|
||||
/// </summary>
|
||||
public HotKeyArgs(DateTime triggeredAt)
|
||||
{
|
||||
m_TimeOfExecution = triggeredAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time when the event was triggered
|
||||
/// </summary>
|
||||
public DateTime Time
|
||||
{
|
||||
get { return m_TimeOfExecution; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.HotKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// An immutable set of Hot Keys that provides an event for when the set is activated.
|
||||
/// </summary>
|
||||
public class HotKeySet
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate representing the signature for the OnHotKeysDownHold event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
public delegate void HotKeyHandler(object sender, HotKeyArgs e);
|
||||
|
||||
private readonly IEnumerable<Keys> m_hotkeys; //hotkeys provided by the user.
|
||||
private readonly Dictionary<Keys, bool> m_hotkeystate; //Keeps track of the status of the set of Keys
|
||||
/*
|
||||
* Example of m_remapping:
|
||||
* a single key from the set of Keys requested is chosen to be the reference key (aka primary key)
|
||||
*
|
||||
* m_remapping[ Keys.LShiftKey ] = Keys.LShiftKey
|
||||
* m_remapping[ Keys.RShiftKey ] = Keys.LShiftKey
|
||||
*
|
||||
* This allows the m_hotkeystate to use a single key (primary key) from the set that will act on behalf of all the keys in the set,
|
||||
* which in turn reduces to this:
|
||||
*
|
||||
* Keys k = Keys.RShiftKey
|
||||
* Keys primaryKey = PrimaryKeyOf( k ) = Keys.LShiftKey
|
||||
* m_hotkeystate[ primaryKey ] = true/false
|
||||
*/
|
||||
private readonly Dictionary<Keys, Keys> m_remapping; //Used for mapping multiple keys to a single key
|
||||
private bool m_enabled = true; //enabled by default
|
||||
//These provide the actual status of whether a set is truly activated or not.
|
||||
private int m_hotkeydowncount; //number of hot keys down
|
||||
private int m_remappingCount;
|
||||
//the number of remappings, i.e., a set of mappings, not the individual count in m_remapping
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the HotKeySet class. Once created, the keys cannot be changed.
|
||||
/// </summary>
|
||||
/// <param name="hotkeys">Set of Hot Keys</param>
|
||||
public HotKeySet(IEnumerable<Keys> hotkeys)
|
||||
{
|
||||
m_hotkeystate = new Dictionary<Keys, bool>();
|
||||
m_remapping = new Dictionary<Keys, Keys>();
|
||||
m_hotkeys = hotkeys;
|
||||
InitializeKeys();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the ability to name the set
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables the ability to describe what the set is used for or supposed to do
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of hotkeys that this class handles.
|
||||
/// </summary>
|
||||
public IEnumerable<Keys> HotKeys
|
||||
{
|
||||
get { return m_hotkeys; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the set of Keys is activated
|
||||
/// </summary>
|
||||
public bool HotKeysActivated
|
||||
{
|
||||
//The number of sets of remapped keys is used to offset the amount originally specified by the user.
|
||||
get { return m_hotkeydowncount == (m_hotkeystate.Count - m_remappingCount); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the enabled state of the HotKey set.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return m_enabled; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
InitializeKeys(); //must get the actual current state of each key to update
|
||||
|
||||
m_enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called as the user holds down the keys in the set. It is NOT triggered the first time the keys are set.
|
||||
/// <see cref="OnHotKeysDownOnce" />
|
||||
/// </summary>
|
||||
public event HotKeyHandler OnHotKeysDownHold;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the hot key set is no longer active. This is essentially a KeyPress event, indicating that a full
|
||||
/// key cycle has occurred, only for HotKeys because a single key removed from the set constitutes an incomplete set.
|
||||
/// </summary>
|
||||
public event HotKeyHandler OnHotKeysUp;
|
||||
|
||||
/// <summary>
|
||||
/// Called the first time the down keys are set. It does not get called throughout the duration the user holds it but
|
||||
/// only the
|
||||
/// first time it's activated.
|
||||
/// </summary>
|
||||
public event HotKeyHandler OnHotKeysDownOnce;
|
||||
|
||||
/// <summary>
|
||||
/// General invocation handler
|
||||
/// </summary>
|
||||
/// <param name="hotKeyDelegate"></param>
|
||||
private void InvokeHotKeyHandler(HotKeyHandler hotKeyDelegate)
|
||||
{
|
||||
if (hotKeyDelegate != null)
|
||||
hotKeyDelegate(this, new HotKeyArgs(DateTime.Now));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the keys into the dictionary tracking the keys and gets the real-time status of the Keys
|
||||
/// from the OS
|
||||
/// </summary>
|
||||
private void InitializeKeys()
|
||||
{
|
||||
foreach (Keys k in HotKeys)
|
||||
{
|
||||
if (m_hotkeystate.ContainsKey(k))
|
||||
m_hotkeystate.Add(k, false);
|
||||
|
||||
//assign using the current state of the keyboard
|
||||
m_hotkeystate[k] = KeyboardState.GetCurrent().IsDown(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a previously set exclusive or based on the primary key.
|
||||
/// </summary>
|
||||
/// <param name="anyKeyInTheExclusiveOrSet">Any key used in the Registration method used to create an exclusive or set</param>
|
||||
/// <returns>
|
||||
/// True if successful. False doesn't indicate a failure to unregister, it indicates that the Key is not
|
||||
/// registered as an Exclusive Or key or it's not the Primary Key.
|
||||
/// </returns>
|
||||
public bool UnregisterExclusiveOrKey(Keys anyKeyInTheExclusiveOrSet)
|
||||
{
|
||||
Keys primaryKey = GetExclusiveOrPrimaryKey(anyKeyInTheExclusiveOrSet);
|
||||
|
||||
if (primaryKey == Keys.None || !m_remapping.ContainsValue(primaryKey))
|
||||
return false;
|
||||
|
||||
List<Keys> keystoremove = new List<Keys>();
|
||||
|
||||
foreach (KeyValuePair<Keys, Keys> pair in m_remapping)
|
||||
{
|
||||
if (pair.Value == primaryKey)
|
||||
keystoremove.Add(pair.Key);
|
||||
}
|
||||
|
||||
foreach (Keys k in keystoremove)
|
||||
m_remapping.Remove(k);
|
||||
|
||||
--m_remappingCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a group of Keys that are already part of the HotKeySet in order to provide better flexibility among keys.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// HotKeySet hks = new HotKeySet( new [] { Keys.T, Keys.LShiftKey, Keys.RShiftKey } );
|
||||
/// RegisterExclusiveOrKey( new [] { Keys.LShiftKey, Keys.RShiftKey } );
|
||||
/// </code>
|
||||
/// allows either Keys.LShiftKey or Keys.RShiftKey to be combined with Keys.T.
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="orKeySet"></param>
|
||||
/// <returns>Primary key used for mapping or Keys.None on error</returns>
|
||||
public Keys RegisterExclusiveOrKey(IEnumerable<Keys> orKeySet)
|
||||
{
|
||||
//Verification first, so as to not leave the m_remapping with a partial set.
|
||||
foreach (Keys k in orKeySet)
|
||||
{
|
||||
if (!m_hotkeystate.ContainsKey(k))
|
||||
return Keys.None;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
Keys primaryKey = Keys.None;
|
||||
|
||||
//Commit after verification
|
||||
foreach (Keys k in orKeySet)
|
||||
{
|
||||
if (i == 0)
|
||||
primaryKey = k;
|
||||
|
||||
m_remapping[k] = primaryKey;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
//Must increase to keep a true count of how many keys are necessary for the activation to be true
|
||||
++m_remappingCount;
|
||||
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the primary key
|
||||
/// </summary>
|
||||
/// <param name="k"></param>
|
||||
/// <returns>The primary key if it exists, otherwise Keys.None</returns>
|
||||
private Keys GetExclusiveOrPrimaryKey(Keys k)
|
||||
{
|
||||
return (m_remapping.ContainsKey(k) ? m_remapping[k] : Keys.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves obtaining the key used for state checking.
|
||||
/// </summary>
|
||||
/// <param name="k"></param>
|
||||
/// <returns>The primary key if it exists, otherwise the key entered</returns>
|
||||
private Keys GetPrimaryKey(Keys k)
|
||||
{
|
||||
//If the key is remapped then get the primary keys
|
||||
return (m_remapping.ContainsKey(k) ? m_remapping[k] : k);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="kex"></param>
|
||||
internal void OnKey(KeyEventArgsExt kex)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
//Gets the primary key if mapped to a single key or gets the key itself
|
||||
Keys primaryKey = GetPrimaryKey(kex.KeyCode);
|
||||
|
||||
if (kex.IsKeyDown)
|
||||
OnKeyDown(primaryKey);
|
||||
else //reset
|
||||
OnKeyUp(primaryKey);
|
||||
}
|
||||
|
||||
private void OnKeyDown(Keys k)
|
||||
{
|
||||
//If the keys are activated still then keep invoking the event
|
||||
if (HotKeysActivated)
|
||||
InvokeHotKeyHandler(OnHotKeysDownHold); //Call the duration event
|
||||
|
||||
//indicates the key's state is current false but the key is now down
|
||||
else if (m_hotkeystate.ContainsKey(k) && !m_hotkeystate[k])
|
||||
{
|
||||
m_hotkeystate[k] = true; //key's state is down
|
||||
++m_hotkeydowncount; //increase the number of keys down in this set
|
||||
|
||||
if (HotKeysActivated) //because of the increase, check whether the set is activated
|
||||
InvokeHotKeyHandler(OnHotKeysDownOnce); //Call the initial event
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyUp(Keys k)
|
||||
{
|
||||
if (m_hotkeystate.ContainsKey(k) && m_hotkeystate[k]) //indicates the key's state was down but now it's up
|
||||
{
|
||||
bool wasActive = HotKeysActivated;
|
||||
|
||||
m_hotkeystate[k] = false; //key's state is up
|
||||
--m_hotkeydowncount; //this set is no longer ready
|
||||
|
||||
if (wasActive)
|
||||
InvokeHotKeyHandler(OnHotKeysUp); //call the KeyUp event because the set is no longer active
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.HotKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of HotKeySets
|
||||
/// </summary>
|
||||
public sealed class HotKeySetCollection : List<HotKeySet>
|
||||
{
|
||||
private KeyChainHandler m_keyChain;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a HotKeySet to the collection.
|
||||
/// </summary>
|
||||
/// <param name="hks"></param>
|
||||
public new void Add(HotKeySet hks)
|
||||
{
|
||||
m_keyChain += hks.OnKey;
|
||||
base.Add(hks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the HotKeySet from the collection.
|
||||
/// </summary>
|
||||
/// <param name="hks"></param>
|
||||
public new void Remove(HotKeySet hks)
|
||||
{
|
||||
m_keyChain -= hks.OnKey;
|
||||
base.Remove(hks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a multi-case delegate to invoke individual HotKeySets if the Key is in use by any HotKeySets.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
internal void OnKey(KeyEventArgsExt e)
|
||||
{
|
||||
if (m_keyChain != null)
|
||||
m_keyChain(e);
|
||||
}
|
||||
|
||||
private delegate void KeyChainHandler(KeyEventArgsExt kex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
Until a separate, full-featured test version is ready, here's a quick update that can be made to the TestFormHookListeners:
|
||||
|
||||
//HotKeySetsListener inherits KeyboardHookListener
|
||||
private readonly HotKeySetsListener m_KeyboardHookManager;
|
||||
private readonly MouseHookListener m_MouseHookManager;
|
||||
|
||||
public TestFormHookListeners()
|
||||
{
|
||||
InitializeComponent();
|
||||
//m_KeyboardHookManager = new KeyboardHookListener(new GlobalHooker());
|
||||
//m_KeyboardHookManager.Enabled = true;
|
||||
|
||||
m_MouseHookManager = new MouseHookListener( new GlobalHooker() ) { Enabled = true };
|
||||
|
||||
HotKeySetCollection hkscoll = new HotKeySetCollection();
|
||||
m_KeyboardHookManager = new HotKeySetsListener( hkscoll, new GlobalHooker() ) { Enabled = true };
|
||||
|
||||
BuildHotKeyTests( hkscoll );
|
||||
}
|
||||
|
||||
private void BuildHotKeyTests( HotKeySetCollection hkscoll )
|
||||
{
|
||||
//Hot Keys are enabled by default. Use the Enabled property to adjust.
|
||||
hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LShiftKey }, null, OnHotKeyDownOnce1, OnHotKeyDownHold1, OnHotKeyUp1, "test1" ) );
|
||||
hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LControlKey, Keys.RControlKey }, new[] { Keys.LControlKey, Keys.RControlKey }, OnHotKeyDownGeneral2, OnHotKeyDownGeneral2, OnHotKeyUp1, "test2" ) );
|
||||
}
|
||||
|
||||
private static HotKeySet BindHotKeySet( IEnumerable<Keys> ks,
|
||||
IEnumerable<Keys> xorKeys,
|
||||
HotKeySet.HotKeyHandler onEventDownOnce,
|
||||
HotKeySet.HotKeyHandler onEventDownHold,
|
||||
HotKeySet.HotKeyHandler onEventUp,
|
||||
string name )
|
||||
{
|
||||
|
||||
//Declare ALL Keys that will be available in this set, including any keys you want to register as an either/or subset
|
||||
HotKeySet hks = new HotKeySet( ks );
|
||||
|
||||
//Indicates that the keys in this array will be treated as an OR rather than AND: LShiftKey or RShiftKey
|
||||
//The keys MUST be a subset of the ks Keys array.
|
||||
if ( hks.RegisterExclusiveOrKey( xorKeys ) == Keys.None ) //Keys.None indicates an error
|
||||
{
|
||||
MessageBox.Show( null, @"Unable to register subset: " + String.Join( ", ", xorKeys ),
|
||||
@"Subset registration error", MessageBoxButtons.OK, MessageBoxIcon.Error );
|
||||
}
|
||||
|
||||
hks.OnHotKeysDownOnce += onEventDownOnce; //The first time the key is down
|
||||
hks.OnHotKeysDownHold += onEventDownHold; //Fired as long as the user holds the hot keys down but is not fired the first time.
|
||||
hks.OnHotKeysUp += onEventUp; //Whenever a key from the set is no longer being held down
|
||||
|
||||
hks.Name = ( name ?? String.Empty );
|
||||
|
||||
return hks;
|
||||
|
||||
}
|
||||
|
||||
private void GeneralHotKeyEvent( object sender, DateTime timeTriggered, string eventType )
|
||||
{
|
||||
HotKeySet hks = sender as HotKeySet;
|
||||
string kstring = String.Join( ", ", hks.HotKeys );
|
||||
Log( String.Format( "{0}: {2} {1} - {3}\r\n", timeTriggered.TimeOfDay, eventType, hks.Name, kstring ) );
|
||||
}
|
||||
|
||||
private void OnHotKeyDownGeneral2( object sender, HotKeyArgs e )
|
||||
{
|
||||
GeneralHotKeyEvent( sender, e.Time, "ONCE/HOLD" );
|
||||
}
|
||||
|
||||
private void OnHotKeyDownOnce1( object sender, HotKeyArgs e )
|
||||
{
|
||||
GeneralHotKeyEvent( sender, e.Time, "ONCE" );
|
||||
}
|
||||
|
||||
private void OnHotKeyDownHold1( object sender, HotKeyArgs e )
|
||||
{
|
||||
GeneralHotKeyEvent( sender, e.Time, "HOLD" );
|
||||
}
|
||||
|
||||
private void OnHotKeyUp1( object sender, HotKeyArgs e )
|
||||
{
|
||||
GeneralHotKeyEvent( sender, e.Time, "UP" );
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides keyboard events
|
||||
/// </summary>
|
||||
public interface IKeyboardEvents
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when a key is pressed.
|
||||
/// </summary>
|
||||
event KeyEventHandler KeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key is pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Key events occur in the following order:
|
||||
/// <list type="number">
|
||||
/// <item>KeyDown</item>
|
||||
/// <item>KeyPress</item>
|
||||
/// <item>KeyUp</item>
|
||||
/// </list>
|
||||
/// The KeyPress event is not raised by non-character keys; however, the non-character keys do raise the KeyDown and
|
||||
/// KeyUp events.
|
||||
/// Use the KeyChar property to sample keystrokes at run time and to consume or modify a subset of common keystrokes.
|
||||
/// To handle keyboard events only in your application and not enable other applications to receive keyboard events,
|
||||
/// set the <see cref="KeyPressEventArgs.Handled" /> property in your form's KeyPress event-handling method to
|
||||
/// <b>true</b>.
|
||||
/// </remarks>
|
||||
event KeyPressEventHandler KeyPress;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key is released.
|
||||
/// </summary>
|
||||
event KeyEventHandler KeyUp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides keyboard and mouse events.
|
||||
/// </summary>
|
||||
public interface IKeyboardMouseEvents : IKeyboardEvents, IMouseEvents, IDisposable
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides all mouse events.
|
||||
/// </summary>
|
||||
public interface IMouseEvents
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when the mouse pointer is moved.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseMove;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the mouse pointer is moved.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
|
||||
/// suppress further processing of mouse movement in other applications.
|
||||
/// </remarks>
|
||||
event EventHandler<MouseEventExtArgs> MouseMoveExt;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a click was performed by the mouse.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseClick;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the mouse a mouse button is pressed.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the mouse a mouse button is pressed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
|
||||
/// suppress further processing of mouse click in other applications.
|
||||
/// </remarks>
|
||||
event EventHandler<MouseEventExtArgs> MouseDownExt;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a mouse button is released.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseUp;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a mouse button is released.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
|
||||
/// suppress further processing of mouse click in other applications.
|
||||
/// </remarks>
|
||||
event EventHandler<MouseEventExtArgs> MouseUpExt;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the mouse wheel moves.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseWheel;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a mouse button is double-clicked.
|
||||
/// </summary>
|
||||
event MouseEventHandler MouseDoubleClick;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class AppEventFacade : EventFacade
|
||||
{
|
||||
protected override MouseListener CreateMouseListener()
|
||||
{
|
||||
return new AppMouseListener();
|
||||
}
|
||||
|
||||
protected override KeyListener CreateKeyListener()
|
||||
{
|
||||
return new AppKeyListener();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Collections.Generic;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class AppKeyListener : KeyListener
|
||||
{
|
||||
public AppKeyListener()
|
||||
: base(HookHelper.HookAppKeyboard)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
|
||||
{
|
||||
return KeyPressEventArgsExt.FromRawDataApp(data);
|
||||
}
|
||||
|
||||
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
|
||||
{
|
||||
return KeyEventArgsExt.FromRawDataApp(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class AppMouseListener : MouseListener
|
||||
{
|
||||
public AppMouseListener()
|
||||
: base(HookHelper.HookAppMouse)
|
||||
{
|
||||
}
|
||||
|
||||
protected override MouseEventExtArgs GetEventArgs(CallbackData data)
|
||||
{
|
||||
return MouseEventExtArgs.FromRawDataApp(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal abstract class BaseListener : IDisposable
|
||||
{
|
||||
protected BaseListener(Subscribe subscribe)
|
||||
{
|
||||
Handle = subscribe(Callback);
|
||||
}
|
||||
|
||||
protected HookResult Handle { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Handle.Dispose();
|
||||
}
|
||||
|
||||
protected abstract bool Callback(CallbackData data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class ButtonSet
|
||||
{
|
||||
private MouseButtons m_Set;
|
||||
|
||||
public ButtonSet()
|
||||
{
|
||||
m_Set = MouseButtons.None;
|
||||
}
|
||||
|
||||
public void Add(MouseButtons element)
|
||||
{
|
||||
m_Set |= element;
|
||||
}
|
||||
|
||||
public void Remove(MouseButtons element)
|
||||
{
|
||||
m_Set &= ~element;
|
||||
}
|
||||
|
||||
public bool Contains(MouseButtons element)
|
||||
{
|
||||
return (m_Set & element) != MouseButtons.None;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal delegate bool Callback(CallbackData data);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal abstract class EventFacade : IKeyboardMouseEvents
|
||||
{
|
||||
private KeyListener m_KeyListenerCache;
|
||||
private MouseListener m_MouseListenerCache;
|
||||
|
||||
public event KeyEventHandler KeyDown
|
||||
{
|
||||
add { GetKeyListener().KeyDown += value; }
|
||||
remove { GetKeyListener().KeyDown -= value; }
|
||||
}
|
||||
|
||||
public event KeyPressEventHandler KeyPress
|
||||
{
|
||||
add { GetKeyListener().KeyPress += value; }
|
||||
remove { GetKeyListener().KeyPress -= value; }
|
||||
}
|
||||
|
||||
public event KeyEventHandler KeyUp
|
||||
{
|
||||
add { GetKeyListener().KeyUp += value; }
|
||||
remove { GetKeyListener().KeyUp -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseMove
|
||||
{
|
||||
add { GetMouseListener().MouseMove += value; }
|
||||
remove { GetMouseListener().MouseMove -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseEventExtArgs> MouseMoveExt
|
||||
{
|
||||
add { GetMouseListener().MouseMoveExt += value; }
|
||||
remove { GetMouseListener().MouseMoveExt -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseClick
|
||||
{
|
||||
add { GetMouseListener().MouseClick += value; }
|
||||
remove { GetMouseListener().MouseClick -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseDown
|
||||
{
|
||||
add { GetMouseListener().MouseDown += value; }
|
||||
remove { GetMouseListener().MouseDown -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseEventExtArgs> MouseDownExt
|
||||
{
|
||||
add { GetMouseListener().MouseDownExt += value; }
|
||||
remove { GetMouseListener().MouseDownExt -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseUp
|
||||
{
|
||||
add { GetMouseListener().MouseUp += value; }
|
||||
remove { GetMouseListener().MouseUp -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseEventExtArgs> MouseUpExt
|
||||
{
|
||||
add { GetMouseListener().MouseUpExt += value; }
|
||||
remove { GetMouseListener().MouseUpExt -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseWheel
|
||||
{
|
||||
add { GetMouseListener().MouseWheel += value; }
|
||||
remove { GetMouseListener().MouseWheel -= value; }
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseDoubleClick
|
||||
{
|
||||
add { GetMouseListener().MouseDoubleClick += value; }
|
||||
remove { GetMouseListener().MouseDoubleClick -= value; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_MouseListenerCache != null) m_MouseListenerCache.Dispose();
|
||||
if (m_KeyListenerCache != null) m_KeyListenerCache.Dispose();
|
||||
}
|
||||
|
||||
private KeyListener GetKeyListener()
|
||||
{
|
||||
var target = m_KeyListenerCache;
|
||||
if (target != null) return target;
|
||||
target = CreateKeyListener();
|
||||
m_KeyListenerCache = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
private MouseListener GetMouseListener()
|
||||
{
|
||||
var target = m_MouseListenerCache;
|
||||
if (target != null) return target;
|
||||
target = CreateMouseListener();
|
||||
m_MouseListenerCache = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
protected abstract MouseListener CreateMouseListener();
|
||||
protected abstract KeyListener CreateKeyListener();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class GlobalEventFacade : EventFacade
|
||||
{
|
||||
protected override MouseListener CreateMouseListener()
|
||||
{
|
||||
return new GlobalMouseListener();
|
||||
}
|
||||
|
||||
protected override KeyListener CreateKeyListener()
|
||||
{
|
||||
return new GlobalKeyListener();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Collections.Generic;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class GlobalKeyListener : KeyListener
|
||||
{
|
||||
public GlobalKeyListener()
|
||||
: base(HookHelper.HookGlobalKeyboard)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
|
||||
{
|
||||
return KeyPressEventArgsExt.FromRawDataGlobal(data);
|
||||
}
|
||||
|
||||
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
|
||||
{
|
||||
return KeyEventArgsExt.FromRawDataGlobal(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal class GlobalMouseListener : MouseListener
|
||||
{
|
||||
private readonly int m_SystemDoubleClickTime;
|
||||
private MouseButtons m_PreviousClicked;
|
||||
private Point m_PreviousClickedPosition;
|
||||
private int m_PreviousClickedTime;
|
||||
|
||||
public GlobalMouseListener()
|
||||
: base(HookHelper.HookGlobalMouse)
|
||||
{
|
||||
m_SystemDoubleClickTime = MouseNativeMethods.GetDoubleClickTime();
|
||||
}
|
||||
|
||||
protected override void ProcessDown(ref MouseEventExtArgs e)
|
||||
{
|
||||
if (IsDoubleClick(e))
|
||||
{
|
||||
e = e.ToDoubleClickEventArgs();
|
||||
}
|
||||
base.ProcessDown(ref e);
|
||||
}
|
||||
|
||||
protected override void ProcessUp(ref MouseEventExtArgs e)
|
||||
{
|
||||
base.ProcessUp(ref e);
|
||||
if (e.Clicks == 2)
|
||||
{
|
||||
StopDoubleClickWaiting();
|
||||
}
|
||||
|
||||
if (e.Clicks == 1)
|
||||
{
|
||||
StartDoubleClickWaiting(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartDoubleClickWaiting(MouseEventExtArgs e)
|
||||
{
|
||||
m_PreviousClicked = e.Button;
|
||||
m_PreviousClickedTime = e.Timestamp;
|
||||
m_PreviousClickedPosition = e.Point;
|
||||
}
|
||||
|
||||
private void StopDoubleClickWaiting()
|
||||
{
|
||||
m_PreviousClicked = MouseButtons.None;
|
||||
m_PreviousClickedTime = 0;
|
||||
m_PreviousClickedPosition = new Point(0, 0);
|
||||
}
|
||||
|
||||
private bool IsDoubleClick(MouseEventExtArgs e)
|
||||
{
|
||||
return
|
||||
e.Button == m_PreviousClicked &&
|
||||
e.Point == m_PreviousClickedPosition && // Click-move-click exception, see Patch 11222
|
||||
e.Timestamp - m_PreviousClickedTime <= m_SystemDoubleClickTime;
|
||||
}
|
||||
|
||||
protected override MouseEventExtArgs GetEventArgs(CallbackData data)
|
||||
{
|
||||
return MouseEventExtArgs.FromRawDataGlobal(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal abstract class KeyListener : BaseListener, IKeyboardEvents
|
||||
{
|
||||
protected KeyListener(Subscribe subscribe)
|
||||
: base(subscribe)
|
||||
{
|
||||
}
|
||||
|
||||
public event KeyEventHandler KeyDown;
|
||||
public event KeyPressEventHandler KeyPress;
|
||||
public event KeyEventHandler KeyUp;
|
||||
|
||||
public void InvokeKeyDown(KeyEventArgsExt e)
|
||||
{
|
||||
var handler = KeyDown;
|
||||
if (handler == null || e.Handled || !e.IsKeyDown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
handler(this, e);
|
||||
}
|
||||
|
||||
public void InvokeKeyPress(KeyPressEventArgsExt e)
|
||||
{
|
||||
var handler = KeyPress;
|
||||
if (handler == null || e.Handled || e.IsNonChar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
handler(this, e);
|
||||
}
|
||||
|
||||
public void InvokeKeyUp(KeyEventArgsExt e)
|
||||
{
|
||||
var handler = KeyUp;
|
||||
if (handler == null || e.Handled || !e.IsKeyUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
handler(this, e);
|
||||
}
|
||||
|
||||
protected override bool Callback(CallbackData data)
|
||||
{
|
||||
var eDownUp = GetDownUpEventArgs(data);
|
||||
var pressEventArgs = GetPressEventArgs(data);
|
||||
|
||||
InvokeKeyDown(eDownUp);
|
||||
foreach (var pressEventArg in pressEventArgs)
|
||||
{
|
||||
InvokeKeyPress(pressEventArg);
|
||||
}
|
||||
|
||||
InvokeKeyUp(eDownUp);
|
||||
|
||||
return !eDownUp.Handled;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data);
|
||||
protected abstract KeyEventArgsExt GetDownUpEventArgs(CallbackData data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a snapshot of a keyboard state at certain moment and provides methods
|
||||
/// of querying whether specific keys are pressed or locked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is basically a managed wrapper of GetKeyboardState API function
|
||||
/// http://msdn.microsoft.com/en-us/library/ms646299
|
||||
/// </remarks>
|
||||
internal class KeyboardState
|
||||
{
|
||||
private readonly byte[] m_KeyboardStateNative;
|
||||
|
||||
private KeyboardState(byte[] keyboardStateNative)
|
||||
{
|
||||
m_KeyboardStateNative = keyboardStateNative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a snapshot of a keyboard state to the moment of call and returns an
|
||||
/// instance of <see cref="KeyboardState" /> class.
|
||||
/// </summary>
|
||||
/// <returns>An instance of <see cref="KeyboardState" /> class representing a snapshot of keyboard state at certain moment.</returns>
|
||||
public static KeyboardState GetCurrent()
|
||||
{
|
||||
byte[] keyboardStateNative = new byte[256];
|
||||
KeyboardNativeMethods.GetKeyboardState(keyboardStateNative);
|
||||
return new KeyboardState(keyboardStateNative);
|
||||
}
|
||||
|
||||
internal byte[] GetNativeState()
|
||||
{
|
||||
return m_KeyboardStateNative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether specified key was down at the moment when snapshot was created or not.
|
||||
/// </summary>
|
||||
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
|
||||
/// <returns><b>true</b> if key was down, <b>false</b> - if key was up.</returns>
|
||||
public bool IsDown(Keys key)
|
||||
{
|
||||
byte keyState = GetKeyState(key);
|
||||
bool isDown = GetHighBit(keyState);
|
||||
return isDown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate weather specified key was toggled at the moment when snapshot was created or not.
|
||||
/// </summary>
|
||||
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
|
||||
/// <returns>
|
||||
/// <b>true</b> if toggle key like (CapsLock, NumLocke, etc.) was on. <b>false</b> if it was off.
|
||||
/// Ordinal (non toggle) keys return always false.
|
||||
/// </returns>
|
||||
public bool IsToggled(Keys key)
|
||||
{
|
||||
byte keyState = GetKeyState(key);
|
||||
bool isToggled = GetLowBit(keyState);
|
||||
return isToggled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates weather every of specified keys were down at the moment when snapshot was created.
|
||||
/// The method returns false if even one of them was up.
|
||||
/// </summary>
|
||||
/// <param name="keys">Keys to verify whether they were down or not.</param>
|
||||
/// <returns><b>true</b> - all were down. <b>false</b> - at least one was up.</returns>
|
||||
public bool AreAllDown(IEnumerable<Keys> keys)
|
||||
{
|
||||
foreach (Keys key in keys)
|
||||
{
|
||||
if (!IsDown(key))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private byte GetKeyState(Keys key)
|
||||
{
|
||||
int virtualKeyCode = (int) key;
|
||||
if (virtualKeyCode < 0 || virtualKeyCode > 255)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("key", key, "The value must be between 0 and 255.");
|
||||
}
|
||||
return m_KeyboardStateNative[virtualKeyCode];
|
||||
}
|
||||
|
||||
private static bool GetHighBit(byte value)
|
||||
{
|
||||
return (value >> 7) != 0;
|
||||
}
|
||||
|
||||
private static bool GetLowBit(byte value)
|
||||
{
|
||||
return (value & 1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal abstract class MouseListener : BaseListener, IMouseEvents
|
||||
{
|
||||
private readonly ButtonSet m_DoubleDown;
|
||||
private readonly ButtonSet m_SingleDown;
|
||||
private Point m_PreviousPosition;
|
||||
|
||||
protected MouseListener(Subscribe subscribe)
|
||||
: base(subscribe)
|
||||
{
|
||||
m_PreviousPosition = new Point(-1, -1);
|
||||
m_DoubleDown = new ButtonSet();
|
||||
m_SingleDown = new ButtonSet();
|
||||
}
|
||||
|
||||
protected override bool Callback(CallbackData data)
|
||||
{
|
||||
var e = GetEventArgs(data);
|
||||
|
||||
if (e.IsMouseKeyDown)
|
||||
{
|
||||
ProcessDown(ref e);
|
||||
}
|
||||
|
||||
if (e.IsMouseKeyUp)
|
||||
{
|
||||
ProcessUp(ref e);
|
||||
}
|
||||
|
||||
if (e.WheelScrolled)
|
||||
{
|
||||
ProcessWheel(ref e);
|
||||
}
|
||||
|
||||
if (HasMoved(e.Point))
|
||||
{
|
||||
ProcessMove(ref e);
|
||||
}
|
||||
|
||||
return !e.Handled;
|
||||
}
|
||||
|
||||
protected abstract MouseEventExtArgs GetEventArgs(CallbackData data);
|
||||
|
||||
protected virtual void ProcessWheel(ref MouseEventExtArgs e)
|
||||
{
|
||||
OnWheel(e);
|
||||
}
|
||||
|
||||
protected virtual void ProcessDown(ref MouseEventExtArgs e)
|
||||
{
|
||||
OnDown(e);
|
||||
OnDownExt(e);
|
||||
if (e.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Clicks == 2)
|
||||
{
|
||||
m_DoubleDown.Add(e.Button);
|
||||
}
|
||||
|
||||
if (e.Clicks == 1)
|
||||
{
|
||||
m_SingleDown.Add(e.Button);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ProcessUp(ref MouseEventExtArgs e)
|
||||
{
|
||||
if (m_SingleDown.Contains(e.Button))
|
||||
{
|
||||
OnUp(e);
|
||||
OnUpExt(e);
|
||||
if (e.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OnClick(e);
|
||||
m_SingleDown.Remove(e.Button);
|
||||
}
|
||||
|
||||
if (m_DoubleDown.Contains(e.Button))
|
||||
{
|
||||
e = e.ToDoubleClickEventArgs();
|
||||
OnUp(e);
|
||||
OnDoubleClick(e);
|
||||
m_DoubleDown.Remove(e.Button);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMove(ref MouseEventExtArgs e)
|
||||
{
|
||||
m_PreviousPosition = e.Point;
|
||||
|
||||
OnMove(e);
|
||||
OnMoveExt(e);
|
||||
}
|
||||
|
||||
private bool HasMoved(Point actualPoint)
|
||||
{
|
||||
return m_PreviousPosition != actualPoint;
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseMove;
|
||||
public event EventHandler<MouseEventExtArgs> MouseMoveExt;
|
||||
public event MouseEventHandler MouseClick;
|
||||
public event MouseEventHandler MouseDown;
|
||||
public event EventHandler<MouseEventExtArgs> MouseDownExt;
|
||||
public event MouseEventHandler MouseUp;
|
||||
public event EventHandler<MouseEventExtArgs> MouseUpExt;
|
||||
public event MouseEventHandler MouseWheel;
|
||||
public event MouseEventHandler MouseDoubleClick;
|
||||
|
||||
protected virtual void OnMove(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseMove;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnMoveExt(MouseEventExtArgs e)
|
||||
{
|
||||
var handler = MouseMoveExt;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnClick(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseClick;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDown(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseDown;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDownExt(MouseEventExtArgs e)
|
||||
{
|
||||
var handler = MouseDownExt;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnUp(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseUp;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnUpExt(MouseEventExtArgs e)
|
||||
{
|
||||
var handler = MouseUpExt;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnWheel(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseWheel;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnDoubleClick(MouseEventArgs e)
|
||||
{
|
||||
var handler = MouseDoubleClick;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.Implementation
|
||||
{
|
||||
internal delegate HookResult Subscribe(Callback callbck);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extended argument data for the <see cref='KeyListener.KeyDown' /> or
|
||||
/// <see cref='KeyListener.KeyUp' /> event.
|
||||
/// </summary>
|
||||
public class KeyEventArgsExt : KeyEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyEventArgsExt" /> class.
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
public KeyEventArgsExt(Keys keyData)
|
||||
: base(keyData)
|
||||
{
|
||||
}
|
||||
|
||||
internal KeyEventArgsExt(Keys keyData, int timestamp, bool isKeyDown, bool isKeyUp)
|
||||
: this(keyData)
|
||||
{
|
||||
Timestamp = timestamp;
|
||||
IsKeyDown = isKeyDown;
|
||||
IsKeyUp = isKeyUp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The system tick count of when the event occurred.
|
||||
/// </summary>
|
||||
public int Timestamp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if event signals key down..
|
||||
/// </summary>
|
||||
public bool IsKeyDown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if event signals key up.
|
||||
/// </summary>
|
||||
public bool IsKeyUp { get; private set; }
|
||||
|
||||
internal static KeyEventArgsExt FromRawDataApp(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
|
||||
//http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx
|
||||
|
||||
const uint maskKeydown = 0x40000000; // for bit 30
|
||||
const uint maskKeyup = 0x80000000; // for bit 31
|
||||
|
||||
int timestamp = Environment.TickCount;
|
||||
|
||||
var flags = (uint) lParam.ToInt64();
|
||||
|
||||
//bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
|
||||
bool wasKeyDown = (flags & maskKeydown) > 0;
|
||||
//bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
|
||||
bool isKeyReleased = (flags & maskKeyup) > 0;
|
||||
|
||||
Keys keyData = AppendModifierStates((Keys) wParam);
|
||||
|
||||
bool isKeyDown = !wasKeyDown && !isKeyReleased;
|
||||
bool isKeyUp = wasKeyDown && isKeyReleased;
|
||||
|
||||
return new KeyEventArgsExt(keyData, timestamp, isKeyDown, isKeyUp);
|
||||
}
|
||||
|
||||
internal static KeyEventArgsExt FromRawDataGlobal(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
var keyboardHookStruct =
|
||||
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct));
|
||||
var keyData = AppendModifierStates((Keys) keyboardHookStruct.VirtualKeyCode);
|
||||
|
||||
var keyCode = (int) wParam;
|
||||
bool isKeyDown = (keyCode == Messages.WM_KEYDOWN || keyCode == Messages.WM_SYSKEYDOWN);
|
||||
bool isKeyUp = (keyCode == Messages.WM_KEYUP || keyCode == Messages.WM_SYSKEYUP);
|
||||
|
||||
return new KeyEventArgsExt(keyData, keyboardHookStruct.Time, isKeyDown, isKeyUp);
|
||||
}
|
||||
|
||||
// # It is not possible to distinguish Keys.LControlKey and Keys.RControlKey when they are modifiers
|
||||
// Check for Keys.Control instead
|
||||
// Same for Shift and Alt(Menu)
|
||||
// See more at http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.windowsforms/2008-04/msg00127.html #
|
||||
|
||||
// A shortcut to make life easier
|
||||
private static bool CheckModifier(int vKey)
|
||||
{
|
||||
return (KeyboardNativeMethods.GetKeyState(vKey) & 0x8000) > 0;
|
||||
}
|
||||
|
||||
private static Keys AppendModifierStates(Keys keyData)
|
||||
{
|
||||
// Is Control being held down?
|
||||
bool control = CheckModifier(KeyboardNativeMethods.VK_CONTROL);
|
||||
// Is Shift being held down?
|
||||
bool shift = CheckModifier(KeyboardNativeMethods.VK_SHIFT);
|
||||
// Is Alt being held down?
|
||||
bool alt = CheckModifier(KeyboardNativeMethods.VK_MENU);
|
||||
|
||||
// Windows keys
|
||||
// # combine LWin and RWin key with other keys will potentially corrupt the data
|
||||
// notable F5 | Keys.LWin == F12, see https://globalmousekeyhook.codeplex.com/workitem/1188
|
||||
// and the KeyEventArgs.KeyData don't recognize combined data either
|
||||
|
||||
// Function (Fn) key
|
||||
// # CANNOT determine state due to conversion inside keyboard
|
||||
// See http://en.wikipedia.org/wiki/Fn_key#Technical_details #
|
||||
|
||||
return keyData |
|
||||
(control ? Keys.Control : Keys.None) |
|
||||
(shift ? Keys.Shift : Keys.None) |
|
||||
(alt ? Keys.Alt : Keys.None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extended data for the <see cref='KeyListener.KeyPress' /> event.
|
||||
/// </summary>
|
||||
public class KeyPressEventArgsExt : KeyPressEventArgs
|
||||
{
|
||||
internal KeyPressEventArgsExt(char keyChar, int timestamp)
|
||||
: base(keyChar)
|
||||
{
|
||||
IsNonChar = keyChar == (char) 0x0;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref='KeyPressEventArgsExt' /> class.
|
||||
/// </summary>
|
||||
/// <param name="keyChar">
|
||||
/// Character corresponding to the key pressed. 0 char if represents a system or functional non char
|
||||
/// key.
|
||||
/// </param>
|
||||
public KeyPressEventArgsExt(char keyChar)
|
||||
: this(keyChar, Environment.TickCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if represents a system or functional non char key.
|
||||
/// </summary>
|
||||
public bool IsNonChar { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The system tick count of when the event occurred.
|
||||
/// </summary>
|
||||
public int Timestamp { get; private set; }
|
||||
|
||||
internal static IEnumerable<KeyPressEventArgsExt> FromRawDataApp(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
|
||||
//http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx
|
||||
|
||||
const uint maskKeydown = 0x40000000; // for bit 30
|
||||
const uint maskKeyup = 0x80000000; // for bit 31
|
||||
const uint maskScanCode = 0xff0000; // for bit 23-16
|
||||
|
||||
var flags = (uint) lParam.ToInt64();
|
||||
|
||||
//bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
|
||||
var wasKeyDown = (flags & maskKeydown) > 0;
|
||||
//bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
|
||||
var isKeyReleased = (flags & maskKeyup) > 0;
|
||||
|
||||
if (!wasKeyDown && !isKeyReleased)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var virtualKeyCode = (int) wParam;
|
||||
var scanCode = checked((int) (flags & maskScanCode));
|
||||
const int fuState = 0;
|
||||
|
||||
char[] chars;
|
||||
|
||||
KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars);
|
||||
if (chars == null) yield break;
|
||||
foreach (var ch in chars)
|
||||
{
|
||||
yield return new KeyPressEventArgsExt(ch);
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<KeyPressEventArgsExt> FromRawDataGlobal(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
|
||||
if ((int) wParam != Messages.WM_KEYDOWN)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
KeyboardHookStruct keyboardHookStruct =
|
||||
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct));
|
||||
|
||||
var virtualKeyCode = keyboardHookStruct.VirtualKeyCode;
|
||||
var scanCode = keyboardHookStruct.ScanCode;
|
||||
var fuState = keyboardHookStruct.Flags;
|
||||
|
||||
if (virtualKeyCode == KeyboardNativeMethods.VK_PACKET)
|
||||
{
|
||||
var ch = (char) scanCode;
|
||||
yield return new KeyPressEventArgsExt(ch, keyboardHookStruct.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
char[] chars;
|
||||
KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars);
|
||||
if (chars == null) yield break;
|
||||
foreach (var current in chars)
|
||||
{
|
||||
yield return new KeyPressEventArgsExt(current, keyboardHookStruct.Time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.WinApi;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extended data for the MouseClickExt and MouseMoveExt events.
|
||||
/// </summary>
|
||||
public class MouseEventExtArgs : MouseEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MouseEventExtArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="buttons">One of the MouseButtons values indicating which mouse button was pressed.</param>
|
||||
/// <param name="clicks">The number of times a mouse button was pressed.</param>
|
||||
/// <param name="point">The x and y -coordinate of a mouse click, in pixels.</param>
|
||||
/// <param name="delta">A signed count of the number of detents the wheel has rotated.</param>
|
||||
/// <param name="timestamp">The system tick count when the event occurred.</param>
|
||||
/// <param name="isMouseKeyDown">True if event signals mouse button down.</param>
|
||||
/// <param name="isMouseKeyUp">True if event signals mouse button up.</param>
|
||||
internal MouseEventExtArgs(MouseButtons buttons, int clicks, Point point, int delta, int timestamp,
|
||||
bool isMouseKeyDown, bool isMouseKeyUp)
|
||||
: base(buttons, clicks, point.X, point.Y, delta)
|
||||
{
|
||||
IsMouseKeyDown = isMouseKeyDown;
|
||||
IsMouseKeyUp = isMouseKeyUp;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set this property to <b>true</b> inside your event handler to prevent further processing of the event in other
|
||||
/// applications.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if event contains information about wheel scroll.
|
||||
/// </summary>
|
||||
public bool WheelScrolled
|
||||
{
|
||||
get { return Delta != 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if event signals a click. False if it was only a move or wheel scroll.
|
||||
/// </summary>
|
||||
public bool Clicked
|
||||
{
|
||||
get { return Clicks > 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if event signals mouse button down.
|
||||
/// </summary>
|
||||
public bool IsMouseKeyDown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if event signals mouse button up.
|
||||
/// </summary>
|
||||
public bool IsMouseKeyUp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The system tick count of when the event occurred.
|
||||
/// </summary>
|
||||
public int Timestamp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
internal Point Point
|
||||
{
|
||||
get { return new Point(X, Y); }
|
||||
}
|
||||
|
||||
internal static MouseEventExtArgs FromRawDataApp(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
|
||||
AppMouseStruct marshalledMouseStruct =
|
||||
(AppMouseStruct) Marshal.PtrToStructure(lParam, typeof (AppMouseStruct));
|
||||
return FromRawDataUniversal(wParam, marshalledMouseStruct.ToMouseStruct());
|
||||
}
|
||||
|
||||
internal static MouseEventExtArgs FromRawDataGlobal(CallbackData data)
|
||||
{
|
||||
var wParam = data.WParam;
|
||||
var lParam = data.LParam;
|
||||
|
||||
MouseStruct marshalledMouseStruct = (MouseStruct) Marshal.PtrToStructure(lParam, typeof (MouseStruct));
|
||||
return FromRawDataUniversal(wParam, marshalledMouseStruct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="MouseEventExtArgs" /> from relevant mouse data.
|
||||
/// </summary>
|
||||
/// <param name="wParam">First Windows Message parameter.</param>
|
||||
/// <param name="mouseInfo">A MouseStruct containing information from which to construct MouseEventExtArgs.</param>
|
||||
/// <returns>A new MouseEventExtArgs object.</returns>
|
||||
private static MouseEventExtArgs FromRawDataUniversal(IntPtr wParam, MouseStruct mouseInfo)
|
||||
{
|
||||
MouseButtons button = MouseButtons.None;
|
||||
short mouseDelta = 0;
|
||||
int clickCount = 0;
|
||||
|
||||
bool isMouseKeyDown = false;
|
||||
bool isMouseKeyUp = false;
|
||||
|
||||
|
||||
switch ((long) wParam)
|
||||
{
|
||||
case Messages.WM_LBUTTONDOWN:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Left;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_LBUTTONUP:
|
||||
isMouseKeyUp = true;
|
||||
button = MouseButtons.Left;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_LBUTTONDBLCLK:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Left;
|
||||
clickCount = 2;
|
||||
break;
|
||||
case Messages.WM_RBUTTONDOWN:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Right;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_RBUTTONUP:
|
||||
isMouseKeyUp = true;
|
||||
button = MouseButtons.Right;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_RBUTTONDBLCLK:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Right;
|
||||
clickCount = 2;
|
||||
break;
|
||||
case Messages.WM_MBUTTONDOWN:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Middle;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_MBUTTONUP:
|
||||
isMouseKeyUp = true;
|
||||
button = MouseButtons.Middle;
|
||||
clickCount = 1;
|
||||
break;
|
||||
case Messages.WM_MBUTTONDBLCLK:
|
||||
isMouseKeyDown = true;
|
||||
button = MouseButtons.Middle;
|
||||
clickCount = 2;
|
||||
break;
|
||||
case Messages.WM_MOUSEWHEEL:
|
||||
mouseDelta = mouseInfo.MouseData;
|
||||
break;
|
||||
case Messages.WM_XBUTTONDOWN:
|
||||
button = mouseInfo.MouseData == 1
|
||||
? MouseButtons.XButton1
|
||||
: MouseButtons.XButton2;
|
||||
isMouseKeyDown = true;
|
||||
clickCount = 1;
|
||||
break;
|
||||
|
||||
case Messages.WM_XBUTTONUP:
|
||||
button = mouseInfo.MouseData == 1
|
||||
? MouseButtons.XButton1
|
||||
: MouseButtons.XButton2;
|
||||
isMouseKeyUp = true;
|
||||
clickCount = 1;
|
||||
break;
|
||||
|
||||
case Messages.WM_XBUTTONDBLCLK:
|
||||
isMouseKeyDown = true;
|
||||
button = mouseInfo.MouseData == 1
|
||||
? MouseButtons.XButton1
|
||||
: MouseButtons.XButton2;
|
||||
clickCount = 2;
|
||||
break;
|
||||
|
||||
case Messages.WM_MOUSEHWHEEL:
|
||||
mouseDelta = mouseInfo.MouseData;
|
||||
break;
|
||||
}
|
||||
|
||||
var e = new MouseEventExtArgs(
|
||||
button,
|
||||
clickCount,
|
||||
mouseInfo.Point,
|
||||
mouseDelta,
|
||||
mouseInfo.Timestamp,
|
||||
isMouseKeyDown,
|
||||
isMouseKeyUp);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
internal MouseEventExtArgs ToDoubleClickEventArgs()
|
||||
{
|
||||
return new MouseEventExtArgs(Button, 2, Point, Delta, Timestamp, IsMouseKeyDown, IsMouseKeyUp);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The AppMouseStruct structure contains information about a application-level mouse input event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct AppMouseStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)] public Point Point;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies information associated with the message.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The possible values are:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description>0 - No Information</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>1 - X-Button1 Click</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>2 - X-Button2 Click</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>120 - Mouse Scroll Away from User</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>-120 - Mouse Scroll Toward User</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
#if IS_X64
|
||||
[FieldOffset(0x22)]
|
||||
#else
|
||||
[FieldOffset(0x16)]
|
||||
#endif
|
||||
public Int16 MouseData;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the current <see cref="AppMouseStruct" /> into a <see cref="MouseStruct" />.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The AppMouseStruct does not have a timestamp, thus one is generated at the time of this call.
|
||||
/// </remarks>
|
||||
public MouseStruct ToMouseStruct()
|
||||
{
|
||||
MouseStruct tmp = new MouseStruct();
|
||||
tmp.Point = Point;
|
||||
tmp.MouseData = MouseData;
|
||||
tmp.Timestamp = Environment.TickCount;
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal struct CallbackData
|
||||
{
|
||||
private readonly IntPtr m_LParam;
|
||||
private readonly IntPtr m_WParam;
|
||||
|
||||
public CallbackData(IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
m_WParam = wParam;
|
||||
m_LParam = lParam;
|
||||
}
|
||||
|
||||
public IntPtr WParam
|
||||
{
|
||||
get { return m_WParam; }
|
||||
}
|
||||
|
||||
public IntPtr LParam
|
||||
{
|
||||
get { return m_LParam; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class HookHelper
|
||||
{
|
||||
public static HookResult HookAppMouse(Callback callback)
|
||||
{
|
||||
return HookApp(HookIds.WH_MOUSE, callback);
|
||||
}
|
||||
|
||||
public static HookResult HookAppKeyboard(Callback callback)
|
||||
{
|
||||
return HookApp(HookIds.WH_KEYBOARD, callback);
|
||||
}
|
||||
|
||||
public static HookResult HookGlobalMouse(Callback callback)
|
||||
{
|
||||
return HookGlobal(HookIds.WH_MOUSE_LL, callback);
|
||||
}
|
||||
|
||||
public static HookResult HookGlobalKeyboard(Callback callback)
|
||||
{
|
||||
return HookGlobal(HookIds.WH_KEYBOARD_LL, callback);
|
||||
}
|
||||
|
||||
private static HookResult HookApp(int hookId, Callback callback)
|
||||
{
|
||||
HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback);
|
||||
|
||||
var hookHandle = HookNativeMethods.SetWindowsHookEx(
|
||||
hookId,
|
||||
hookProcedure,
|
||||
IntPtr.Zero,
|
||||
ThreadNativeMethods.GetCurrentThreadId());
|
||||
|
||||
if (hookHandle.IsInvalid)
|
||||
{
|
||||
ThrowLastUnmanagedErrorAsException();
|
||||
}
|
||||
|
||||
return new HookResult(hookHandle, hookProcedure);
|
||||
}
|
||||
|
||||
private static HookResult HookGlobal(int hookId, Callback callback)
|
||||
{
|
||||
HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback);
|
||||
|
||||
var hookHandle = HookNativeMethods.SetWindowsHookEx(
|
||||
hookId,
|
||||
hookProcedure,
|
||||
Process.GetCurrentProcess().MainModule.BaseAddress,
|
||||
0);
|
||||
|
||||
if (hookHandle.IsInvalid)
|
||||
{
|
||||
ThrowLastUnmanagedErrorAsException();
|
||||
}
|
||||
|
||||
return new HookResult(hookHandle, hookProcedure);
|
||||
}
|
||||
|
||||
private static IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam, Callback callback)
|
||||
{
|
||||
var passThrough = nCode != 0;
|
||||
if (passThrough)
|
||||
{
|
||||
return CallNextHookEx(nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
var callbackData = new CallbackData(wParam, lParam);
|
||||
var continueProcessing = callback(callbackData);
|
||||
|
||||
if (!continueProcessing)
|
||||
{
|
||||
return new IntPtr(-1);
|
||||
}
|
||||
|
||||
return CallNextHookEx(nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private static IntPtr CallNextHookEx(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
return HookNativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private static void ThrowLastUnmanagedErrorAsException()
|
||||
{
|
||||
var errorCode = Marshal.GetLastWin32Error();
|
||||
throw new Win32Exception(errorCode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class HookIds
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
|
||||
/// </summary>
|
||||
internal const int WH_MOUSE = 7;
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
internal const int WH_KEYBOARD = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level mouse input events.
|
||||
/// </summary>
|
||||
internal const int WH_MOUSE_LL = 14;
|
||||
|
||||
/// <summary>
|
||||
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level keyboard input events.
|
||||
/// </summary>
|
||||
internal const int WH_KEYBOARD_LL = 13;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class HookNativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
|
||||
/// A hook procedure can call this function either before or after processing the hook information.
|
||||
/// </summary>
|
||||
/// <param name="idHook">This parameter is ignored.</param>
|
||||
/// <param name="nCode">[in] Specifies the hook code passed to the current hook procedure.</param>
|
||||
/// <param name="wParam">[in] Specifies the wParam value passed to the current hook procedure.</param>
|
||||
/// <param name="lParam">[in] Specifies the lParam value passed to the current hook procedure.</param>
|
||||
/// <returns>This value is returned by the next hook procedure in the chain.</returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto,
|
||||
CallingConvention = CallingConvention.StdCall)]
|
||||
internal static extern IntPtr CallNextHookEx(
|
||||
IntPtr idHook,
|
||||
int nCode,
|
||||
IntPtr wParam,
|
||||
IntPtr lParam);
|
||||
|
||||
/// <summary>
|
||||
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
|
||||
/// You would install a hook procedure to monitor the system for certain types of events. These events
|
||||
/// are associated either with a specific thread or with all threads in the same desktop as the calling thread.
|
||||
/// </summary>
|
||||
/// <param name="idHook">
|
||||
/// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
|
||||
/// </param>
|
||||
/// <param name="lpfn">
|
||||
/// [in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a
|
||||
/// thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link
|
||||
/// library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
|
||||
/// </param>
|
||||
/// <param name="hMod">
|
||||
/// [in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
|
||||
/// The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by
|
||||
/// the current process and if the hook procedure is within the code associated with the current process.
|
||||
/// </param>
|
||||
/// <param name="dwThreadId">
|
||||
/// [in] Specifies the identifier of the thread with which the hook procedure is to be associated.
|
||||
/// If this parameter is zero, the hook procedure is associated with all existing threads running in the
|
||||
/// same desktop as the calling thread.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is the handle to the hook procedure.
|
||||
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto,
|
||||
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
internal static extern HookProcedureHandle SetWindowsHookEx(
|
||||
int idHook,
|
||||
HookProcedure lpfn,
|
||||
IntPtr hMod,
|
||||
int dwThreadId);
|
||||
|
||||
/// <summary>
|
||||
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx
|
||||
/// function.
|
||||
/// </summary>
|
||||
/// <param name="idHook">
|
||||
/// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to
|
||||
/// SetWindowsHookEx.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto,
|
||||
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||
internal static extern int UnhookWindowsHookEx(IntPtr idHook);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The CallWndProc hook procedure is an application-defined or library-defined callback
|
||||
/// function used with the SetWindowsHookEx function. The HOOKPROC type defines a pointer
|
||||
/// to this callback function. CallWndProc is a placeholder for the application-defined
|
||||
/// or library-defined function name.
|
||||
/// </summary>
|
||||
/// <param name="nCode">
|
||||
/// [in] Specifies whether the hook procedure must process the message.
|
||||
/// If nCode is HC_ACTION, the hook procedure must process the message.
|
||||
/// If nCode is less than zero, the hook procedure must pass the message to the
|
||||
/// CallNextHookEx function without further processing and must return the
|
||||
/// value returned by CallNextHookEx.
|
||||
/// </param>
|
||||
/// <param name="wParam">
|
||||
/// [in] Specifies whether the message was sent by the current thread.
|
||||
/// If the message was sent by the current thread, it is nonzero; otherwise, it is zero.
|
||||
/// </param>
|
||||
/// <param name="lParam">
|
||||
/// [in] Pointer to a CWPSTRUCT structure that contains details about the message.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
|
||||
/// If nCode is greater than or equal to zero, it is highly recommended that you call CallNextHookEx
|
||||
/// and return the value it returns; otherwise, other applications that have installed WH_CALLWNDPROC
|
||||
/// hooks will not receive hook notifications and may behave incorrectly as a result. If the hook
|
||||
/// procedure does not call CallNextHookEx, the return value should be zero.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp
|
||||
/// </remarks>
|
||||
public delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal class HookProcedureHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private static bool _closing;
|
||||
|
||||
static HookProcedureHandle()
|
||||
{
|
||||
Application.ApplicationExit += (sender, e) => { _closing = true; };
|
||||
}
|
||||
|
||||
public HookProcedureHandle()
|
||||
: base(true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
//NOTE Calling Unhook during processexit causes deley
|
||||
if (_closing) return true;
|
||||
return HookNativeMethods.UnhookWindowsHookEx(handle) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal class HookResult : IDisposable
|
||||
{
|
||||
private readonly HookProcedureHandle m_Handle;
|
||||
private readonly HookProcedure m_Procedure;
|
||||
|
||||
public HookResult(HookProcedureHandle handle, HookProcedure procedure)
|
||||
{
|
||||
m_Handle = handle;
|
||||
m_Procedure = procedure;
|
||||
}
|
||||
|
||||
public HookProcedureHandle Handle
|
||||
{
|
||||
get { return m_Handle; }
|
||||
}
|
||||
|
||||
public HookProcedure Procedure
|
||||
{
|
||||
get { return m_Procedure; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Handle.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The KeyboardHookStruct structure contains information about a low-level keyboard input event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct KeyboardHookStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
public int VirtualKeyCode;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a hardware scan code for the key.
|
||||
/// </summary>
|
||||
public int ScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
|
||||
/// </summary>
|
||||
public int Flags;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the Time stamp for this message.
|
||||
/// </summary>
|
||||
public int Time;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies extra information associated with the message.
|
||||
/// </summary>
|
||||
public int ExtraInfo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using xServer.Core.MouseKeyHook.Implementation;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class KeyboardNativeMethods
|
||||
{
|
||||
//values from Winuser.h in Microsoft SDK.
|
||||
public const byte VK_SHIFT = 0x10;
|
||||
public const byte VK_CAPITAL = 0x14;
|
||||
public const byte VK_NUMLOCK = 0x90;
|
||||
public const byte VK_LSHIFT = 0xA0;
|
||||
public const byte VK_RSHIFT = 0xA1;
|
||||
public const byte VK_LCONTROL = 0xA2;
|
||||
public const byte VK_RCONTROL = 0xA3;
|
||||
public const byte VK_LMENU = 0xA4;
|
||||
public const byte VK_RMENU = 0xA5;
|
||||
public const byte VK_LWIN = 0x5B;
|
||||
public const byte VK_RWIN = 0x5C;
|
||||
public const byte VK_SCROLL = 0x91;
|
||||
public const byte VK_INSERT = 0x2D;
|
||||
//may be possible to use these aggregates instead of L and R separately (untested)
|
||||
public const byte VK_CONTROL = 0x11;
|
||||
public const byte VK_MENU = 0x12;
|
||||
public const byte VK_PACKET = 0xE7;
|
||||
//Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods
|
||||
private static int lastVirtualKeyCode = 0;
|
||||
private static int lastScanCode = 0;
|
||||
private static byte[] lastKeyState = new byte[256];
|
||||
private static bool lastIsDead = false;
|
||||
|
||||
/// <summary>
|
||||
/// Translates a virtual key to its character equivalent using the current keyboard layout without knowing the
|
||||
/// scancode in advance.
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode"></param>
|
||||
/// <param name="fuState"></param>
|
||||
/// <param name="chars"></param>
|
||||
/// <returns></returns>
|
||||
internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int fuState, out char[] chars)
|
||||
{
|
||||
var dwhkl = GetActiveKeyboard();
|
||||
int scanCode = MapVirtualKeyEx(virtualKeyCode, (int) MapType.MAPVK_VK_TO_VSC, dwhkl);
|
||||
TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates a virtual key to its character equivalent using the current keyboard layout
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode"></param>
|
||||
/// <param name="scanCode"></param>
|
||||
/// <param name="fuState"></param>
|
||||
/// <param name="chars"></param>
|
||||
/// <returns></returns>
|
||||
internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, out char[] chars)
|
||||
{
|
||||
var dwhkl = GetActiveKeyboard(); //get the active keyboard layout
|
||||
TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates a virtual key to its character equivalent using a specified keyboard layout
|
||||
/// </summary>
|
||||
/// <param name="virtualKeyCode"></param>
|
||||
/// <param name="scanCode"></param>
|
||||
/// <param name="fuState"></param>
|
||||
/// <param name="dwhkl"></param>
|
||||
/// <param name="chars"></param>
|
||||
/// <returns></returns>
|
||||
internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, IntPtr dwhkl, out char[] chars)
|
||||
{
|
||||
StringBuilder pwszBuff = new StringBuilder(64);
|
||||
KeyboardState keyboardState = KeyboardState.GetCurrent();
|
||||
byte[] currentKeyboardState = keyboardState.GetNativeState();
|
||||
bool isDead = false;
|
||||
|
||||
if (keyboardState.IsDown(Keys.ShiftKey))
|
||||
currentKeyboardState[(byte) Keys.ShiftKey] = 0x80;
|
||||
|
||||
if (keyboardState.IsToggled(Keys.CapsLock))
|
||||
currentKeyboardState[(byte) Keys.CapsLock] = 0x01;
|
||||
|
||||
var relevantChars = ToUnicodeEx(virtualKeyCode, scanCode, currentKeyboardState, pwszBuff, pwszBuff.Capacity, fuState, dwhkl);
|
||||
|
||||
switch (relevantChars)
|
||||
{
|
||||
case -1:
|
||||
isDead = true;
|
||||
ClearKeyboardBuffer(virtualKeyCode, scanCode, dwhkl);
|
||||
chars = null;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
chars = null;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (pwszBuff.Length > 0) chars = new[] { pwszBuff[0] };
|
||||
else chars = null;
|
||||
break;
|
||||
|
||||
// Two or more (only two of them is relevant)
|
||||
default:
|
||||
if (pwszBuff.Length > 1) chars = new[] { pwszBuff[0], pwszBuff[1] };
|
||||
else chars = new[] { pwszBuff[0] };
|
||||
break;
|
||||
}
|
||||
|
||||
if (lastVirtualKeyCode != 0 && lastIsDead)
|
||||
{
|
||||
if (chars != null)
|
||||
{
|
||||
StringBuilder sbTemp = new StringBuilder(5);
|
||||
ToUnicodeEx(lastVirtualKeyCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, 0, dwhkl);
|
||||
lastIsDead = false;
|
||||
lastVirtualKeyCode = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lastScanCode = scanCode;
|
||||
lastVirtualKeyCode = virtualKeyCode;
|
||||
lastIsDead = isDead;
|
||||
lastKeyState = (byte[]) currentKeyboardState.Clone();
|
||||
}
|
||||
|
||||
|
||||
private static void ClearKeyboardBuffer(int vk, int sc, IntPtr hkl)
|
||||
{
|
||||
var sb = new StringBuilder(10);
|
||||
|
||||
int rc;
|
||||
do
|
||||
{
|
||||
byte[] lpKeyStateNull = new Byte[255];
|
||||
rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
|
||||
} while (rc < 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input locale identifier for the active application's thread. Using this combined with the ToUnicodeEx and
|
||||
/// MapVirtualKeyEx enables Windows to properly translate keys based on the keyboard layout designated for the
|
||||
/// application.
|
||||
/// </summary>
|
||||
/// <returns>HKL</returns>
|
||||
private static IntPtr GetActiveKeyboard()
|
||||
{
|
||||
IntPtr hActiveWnd = ThreadNativeMethods.GetForegroundWindow(); //handle to focused window
|
||||
int dwProcessId;
|
||||
int hCurrentWnd = ThreadNativeMethods.GetWindowThreadProcessId(hActiveWnd, out dwProcessId);
|
||||
//thread of focused window
|
||||
return GetKeyboardLayout(hCurrentWnd); //get the layout identifier for the thread whose window is focused
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ToAscii function translates the specified virtual-key code and keyboard
|
||||
/// state to the corresponding character or characters. The function translates the code
|
||||
/// using the input language and physical keyboard layout identified by the keyboard layout handle.
|
||||
/// </summary>
|
||||
/// <param name="uVirtKey">
|
||||
/// [in] Specifies the virtual-key code to be translated.
|
||||
/// </param>
|
||||
/// <param name="uScanCode">
|
||||
/// [in] Specifies the hardware scan code of the key to be translated.
|
||||
/// The high-order bit of this value is set if the key is up (not pressed).
|
||||
/// </param>
|
||||
/// <param name="lpbKeyState">
|
||||
/// [in] Pointer to a 256-byte array that contains the current keyboard state.
|
||||
/// Each element (byte) in the array contains the state of one key.
|
||||
/// If the high-order bit of a byte is set, the key is down (pressed).
|
||||
/// The low bit, if set, indicates that the key is toggled on. In this function,
|
||||
/// only the toggle bit of the CAPS LOCK key is relevant. The toggle state
|
||||
/// of the NUM LOCK and SCROLL LOCK keys is ignored.
|
||||
/// </param>
|
||||
/// <param name="lpwTransKey">
|
||||
/// [out] Pointer to the buffer that receives the translated character or characters.
|
||||
/// </param>
|
||||
/// <param name="fuState">
|
||||
/// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.
|
||||
/// Value Meaning
|
||||
/// 0 The specified virtual key has no translation for the current state of the keyboard.
|
||||
/// 1 One character was copied to the buffer.
|
||||
/// 2 Two characters were copied to the buffer. This usually happens when a dead-key character
|
||||
/// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified
|
||||
/// virtual key to form a single character.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
|
||||
/// </remarks>
|
||||
[Obsolete("Use ToUnicodeEx instead")]
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int ToAscii(
|
||||
int uVirtKey,
|
||||
int uScanCode,
|
||||
byte[] lpbKeyState,
|
||||
byte[] lpwTransKey,
|
||||
int fuState);
|
||||
|
||||
/// <summary>
|
||||
/// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters.
|
||||
/// </summary>
|
||||
/// <param name="wVirtKey">[in] The virtual-key code to be translated.</param>
|
||||
/// <param name="wScanCode">
|
||||
/// [in] The hardware scan code of the key to be translated. The high-order bit of this value is
|
||||
/// set if the key is up.
|
||||
/// </param>
|
||||
/// <param name="lpKeyState">
|
||||
/// [in, optional] A pointer to a 256-byte array that contains the current keyboard state. Each
|
||||
/// element (byte) in the array contains the state of one key. If the high-order bit of a byte is set, the key is down.
|
||||
/// </param>
|
||||
/// <param name="pwszBuff">
|
||||
/// [out] The buffer that receives the translated Unicode character or characters. However, this
|
||||
/// buffer may be returned without being null-terminated even though the variable name suggests that it is
|
||||
/// null-terminated.
|
||||
/// </param>
|
||||
/// <param name="cchBuff">[in] The size, in characters, of the buffer pointed to by the pwszBuff parameter.</param>
|
||||
/// <param name="wFlags">
|
||||
/// [in] The behavior of the function. If bit 0 is set, a menu is active. Bits 1 through 31 are
|
||||
/// reserved.
|
||||
/// </param>
|
||||
/// <param name="dwhkl">The input locale identifier used to translate the specified code.</param>
|
||||
/// <returns>
|
||||
/// -1 <= return <= n
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// -1 = The specified virtual key is a dead-key character (accent or diacritic). This value is returned
|
||||
/// regardless of the keyboard layout, even if several characters have been typed and are stored in the
|
||||
/// keyboard state. If possible, even with Unicode keyboard layouts, the function has written a spacing version
|
||||
/// of the dead-key character to the buffer specified by pwszBuff. For example, the function writes the
|
||||
/// character SPACING ACUTE (0x00B4), rather than the character NON_SPACING ACUTE (0x0301).
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// 0 = The specified virtual key has no translation for the current state of the keyboard. Nothing was
|
||||
/// written to the buffer specified by pwszBuff.
|
||||
/// </item>
|
||||
/// <item> 1 = One character was written to the buffer specified by pwszBuff.</item>
|
||||
/// <item>
|
||||
/// n = Two or more characters were written to the buffer specified by pwszBuff. The most common cause
|
||||
/// for this is that a dead-key character (accent or diacritic) stored in the keyboard layout could not be
|
||||
/// combined with the specified virtual key to form a single character. However, the buffer may contain more
|
||||
/// characters than the return value specifies. When this happens, any extra characters are invalid and should
|
||||
/// be ignored.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int ToUnicodeEx(int wVirtKey,
|
||||
int wScanCode,
|
||||
byte[] lpKeyState,
|
||||
[Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder pwszBuff,
|
||||
int cchBuff,
|
||||
int wFlags,
|
||||
IntPtr dwhkl);
|
||||
|
||||
/// <summary>
|
||||
/// The GetKeyboardState function copies the status of the 256 virtual keys to the
|
||||
/// specified buffer.
|
||||
/// </summary>
|
||||
/// <param name="pbKeyState">
|
||||
/// [in] Pointer to a 256-byte array that contains keyboard key states.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetKeyboardState(byte[] pbKeyState);
|
||||
|
||||
/// <summary>
|
||||
/// The GetKeyState function retrieves the status of the specified virtual key. The status specifies whether the key is
|
||||
/// up, down, or toggled
|
||||
/// (on, off—alternating each time the key is pressed).
|
||||
/// </summary>
|
||||
/// <param name="vKey">
|
||||
/// [in] Specifies a virtual key. If the desired virtual key is a letter or digit (A through Z, a through z, or 0
|
||||
/// through 9), nVirtKey must be set to the ASCII value of that character. For other keys, it must be a virtual-key
|
||||
/// code.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The return value specifies the status of the specified virtual key, as follows:
|
||||
/// If the high-order bit is 1, the key is down; otherwise, it is up.
|
||||
/// If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The
|
||||
/// key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be
|
||||
/// on when the key is toggled, and off when the key is untoggled.
|
||||
/// </returns>
|
||||
/// <remarks>http://msdn.microsoft.com/en-us/library/ms646301.aspx</remarks>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern short GetKeyState(int vKey);
|
||||
|
||||
/// <summary>
|
||||
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a
|
||||
/// virtual-key code.
|
||||
/// </summary>
|
||||
/// <param name="uCode">
|
||||
/// [in] The virtual key code or scan code for a key. How this value is interpreted depends on the
|
||||
/// value of the uMapType parameter.
|
||||
/// </param>
|
||||
/// <param name="uMapType">
|
||||
/// [in] The translation to be performed. The value of this parameter depends on the value of the
|
||||
/// uCode parameter.
|
||||
/// </param>
|
||||
/// <param name="dwhkl">[in] The input locale identifier used to translate the specified code.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
internal static extern int MapVirtualKeyEx(int uCode, int uMapType, IntPtr dwhkl);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the active input locale identifier (formerly called the keyboard layout) for the specified thread.
|
||||
/// If the idThread parameter is zero, the input locale identifier for the active thread is returned.
|
||||
/// </summary>
|
||||
/// <param name="dwLayout">[in] The identifier of the thread to query, or 0 for the current thread. </param>
|
||||
/// <returns>
|
||||
/// The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the
|
||||
/// input
|
||||
/// language and the high word contains a device handle to the physical layout of the keyboard.
|
||||
/// </returns>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
internal static extern IntPtr GetKeyboardLayout(int dwLayout);
|
||||
|
||||
/// <summary>
|
||||
/// MapVirtualKeys uMapType
|
||||
/// </summary>
|
||||
internal enum MapType
|
||||
{
|
||||
/// <summary>
|
||||
/// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return
|
||||
/// value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation,
|
||||
/// the function returns 0.
|
||||
/// </summary>
|
||||
MAPVK_VK_TO_VSC,
|
||||
|
||||
/// <summary>
|
||||
/// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not
|
||||
/// distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the
|
||||
/// function returns 0.
|
||||
/// </summary>
|
||||
MAPVK_VSC_TO_VK,
|
||||
|
||||
/// <summary>
|
||||
/// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and
|
||||
/// right-hand keys. If there is no translation, the function returns 0.
|
||||
/// </summary>
|
||||
MAPVK_VK_TO_CHAR,
|
||||
|
||||
/// <summary>
|
||||
/// uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand
|
||||
/// keys. If there is no translation, the function returns 0.
|
||||
/// </summary>
|
||||
MAPVK_VSC_TO_VK_EX
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class Messages
|
||||
{
|
||||
//values from Winuser.h in Microsoft SDK.
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves.
|
||||
/// </summary>
|
||||
public const int WM_MOUSEMOVE = 0x200;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_LBUTTONDOWN message is posted when the user presses the left mouse button
|
||||
/// </summary>
|
||||
public const int WM_LBUTTONDOWN = 0x201;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
|
||||
/// </summary>
|
||||
public const int WM_RBUTTONDOWN = 0x204;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MBUTTONDOWN message is posted when the user presses the middle mouse button
|
||||
/// </summary>
|
||||
public const int WM_MBUTTONDOWN = 0x207;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_LBUTTONUP message is posted when the user releases the left mouse button
|
||||
/// </summary>
|
||||
public const int WM_LBUTTONUP = 0x202;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_RBUTTONUP message is posted when the user releases the right mouse button
|
||||
/// </summary>
|
||||
public const int WM_RBUTTONUP = 0x205;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MBUTTONUP message is posted when the user releases the middle mouse button
|
||||
/// </summary>
|
||||
public const int WM_MBUTTONUP = 0x208;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_LBUTTONDBLCLK message is posted when the user double-clicks the left mouse button
|
||||
/// </summary>
|
||||
public const int WM_LBUTTONDBLCLK = 0x203;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_RBUTTONDBLCLK message is posted when the user double-clicks the right mouse button
|
||||
/// </summary>
|
||||
public const int WM_RBUTTONDBLCLK = 0x206;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
|
||||
/// </summary>
|
||||
public const int WM_MBUTTONDBLCLK = 0x209;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEWHEEL message is posted when the user presses the mouse wheel.
|
||||
/// </summary>
|
||||
public const int WM_MOUSEWHEEL = 0x020A;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_XBUTTONDOWN message is posted when the user presses the first or second X mouse
|
||||
/// button.
|
||||
/// </summary>
|
||||
public const int WM_XBUTTONDOWN = 0x20B;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_XBUTTONUP message is posted when the user releases the first or second X mouse
|
||||
/// button.
|
||||
/// </summary>
|
||||
public const int WM_XBUTTONUP = 0x20C;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_XBUTTONDBLCLK message is posted when the user double-clicks the first or second
|
||||
/// X mouse button.
|
||||
/// </summary>
|
||||
/// <remarks>Only windows that have the CS_DBLCLKS style can receive WM_XBUTTONDBLCLK messages.</remarks>
|
||||
public const int WM_XBUTTONDBLCLK = 0x20D;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEHWHEEL message Sent to the active window when the mouse's horizontal scroll
|
||||
/// wheel is tilted or rotated.
|
||||
/// </summary>
|
||||
public const int WM_MOUSEHWHEEL = 0x20E;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_KEYDOWN message is posted to the window with the keyboard focus when a non-system
|
||||
/// key is pressed. A non-system key is a key that is pressed when the ALT key is not pressed.
|
||||
/// </summary>
|
||||
public const int WM_KEYDOWN = 0x100;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_KEYUP message is posted to the window with the keyboard focus when a non-system
|
||||
/// key is released. A non-system key is a key that is pressed when the ALT key is not pressed,
|
||||
/// or a keyboard key that is pressed when a window has the keyboard focus.
|
||||
/// </summary>
|
||||
public const int WM_KEYUP = 0x101;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when the user
|
||||
/// presses the F10 key (which activates the menu bar) or holds down the ALT key and then
|
||||
/// presses another key. It also occurs when no window currently has the keyboard focus;
|
||||
/// in this case, the WM_SYSKEYDOWN message is sent to the active window. The window that
|
||||
/// receives the message can distinguish between these two contexts by checking the context
|
||||
/// code in the lParam parameter.
|
||||
/// </summary>
|
||||
public const int WM_SYSKEYDOWN = 0x104;
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYUP message is posted to the window with the keyboard focus when the user
|
||||
/// releases a key that was pressed while the ALT key was held down. It also occurs when no
|
||||
/// window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent
|
||||
/// to the active window. The window that receives the message can distinguish between
|
||||
/// these two contexts by checking the context code in the lParam parameter.
|
||||
/// </summary>
|
||||
public const int WM_SYSKEYUP = 0x105;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class MouseNativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// The GetDoubleClickTime function retrieves the current double-click time for the mouse. A double-click is a series
|
||||
/// of two clicks of the
|
||||
/// mouse button, the second occurring within a specified time after the first. The double-click time is the maximum
|
||||
/// number of
|
||||
/// milliseconds that may occur between the first and second click of a double-click.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The return value specifies the current double-click time, in milliseconds.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/ms646258(VS.85).aspx
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int GetDoubleClickTime();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="MouseStruct" /> structure contains information about a mouse input event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct MouseStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)] public Point Point;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies information associated with the message.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The possible values are:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description>0 - No Information</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>1 - X-Button1 Click</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>2 - X-Button2 Click</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>120 - Mouse Scroll Away from User</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description>-120 - Mouse Scroll Toward User</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
[FieldOffset(0x0A)] public Int16 MouseData;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Timestamp associated with the input, in System Ticks.
|
||||
/// </summary>
|
||||
[FieldOffset(0x10)] public Int32 Timestamp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The Point structure defines the X- and Y- coordinates of a point.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/rectangl_0tiq.asp
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct Point
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the X-coordinate of the point.
|
||||
/// </summary>
|
||||
public int X;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the Y-coordinate of the point.
|
||||
/// </summary>
|
||||
public int Y;
|
||||
|
||||
public Point(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public static bool operator ==(Point a, Point b)
|
||||
{
|
||||
return a.X == b.X && a.Y == b.Y;
|
||||
}
|
||||
|
||||
public static bool operator !=(Point a, Point b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public bool Equals(Point other)
|
||||
{
|
||||
return other.X == X && other.Y == Y;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (obj.GetType() != typeof (Point)) return false;
|
||||
return Equals((Point) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (X*397) ^ Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// This code is distributed under MIT license.
|
||||
// Copyright (c) 2015 George Mamaladze
|
||||
// See license.txt or http://opensource.org/licenses/mit-license.php
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace xServer.Core.MouseKeyHook.WinApi
|
||||
{
|
||||
internal static class ThreadNativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the unmanaged thread identifier of the calling thread.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern int GetCurrentThreadId();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a handle to the foreground window (the window with which the user is currently working).
|
||||
/// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to
|
||||
/// other threads.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the
|
||||
/// process that
|
||||
/// created the window.
|
||||
/// </summary>
|
||||
/// <param name="handle">A handle to the window. </param>
|
||||
/// <param name="processId">
|
||||
/// A pointer to a variable that receives the process identifier. If this parameter is not NULL,
|
||||
/// GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not.
|
||||
/// </param>
|
||||
/// <returns>The return value is the identifier of the thread that created the window. </returns>
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
|
||||
}
|
||||
}
|
|
@ -136,6 +136,7 @@ public ConnectionHandler()
|
|||
typeof (Packets.ServerPackets.GetDirectory),
|
||||
typeof (Packets.ServerPackets.DoDownloadFile),
|
||||
typeof (Packets.ServerPackets.DoMouseEvent),
|
||||
typeof (Packets.ServerPackets.DoKeyboardEvent),
|
||||
typeof (Packets.ServerPackets.GetSystemInfo),
|
||||
typeof (Packets.ServerPackets.DoVisitWebsite),
|
||||
typeof (Packets.ServerPackets.DoShowMessageBox),
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using ProtoBuf;
|
||||
using xServer.Core.Networking;
|
||||
|
||||
namespace xServer.Core.Packets.ServerPackets
|
||||
{
|
||||
[ProtoContract]
|
||||
public class DoKeyboardEvent : IPacket
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public byte Key { get; set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public bool KeyDown { get; set; }
|
||||
|
||||
public DoKeyboardEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public DoKeyboardEvent(byte key, bool keyDown)
|
||||
{
|
||||
this.Key = key;
|
||||
this.KeyDown = keyDown;
|
||||
}
|
||||
|
||||
public void Execute(Client client)
|
||||
{
|
||||
client.Send(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,12 @@ public static class NativeMethods
|
|||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, int vk);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
|
||||
public extern static int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ public enum MouseAction
|
|||
RightDown,
|
||||
RightUp,
|
||||
MoveCursor,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ protected override void Dispose(bool disposing)
|
|||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRemoteDesktop));
|
||||
this.btnStart = new System.Windows.Forms.Button();
|
||||
this.btnStop = new System.Windows.Forms.Button();
|
||||
|
@ -35,14 +36,16 @@ private void InitializeComponent()
|
|||
this.lblQuality = new System.Windows.Forms.Label();
|
||||
this.lblQualityShow = new System.Windows.Forms.Label();
|
||||
this.btnMouse = new System.Windows.Forms.Button();
|
||||
this.picDesktop = new xServer.Controls.RapidPictureBox();
|
||||
this.panelTop = new System.Windows.Forms.Panel();
|
||||
this.btnKeyboard = new System.Windows.Forms.Button();
|
||||
this.cbMonitors = new System.Windows.Forms.ComboBox();
|
||||
this.btnHide = new System.Windows.Forms.Button();
|
||||
this.btnShow = new System.Windows.Forms.Button();
|
||||
this.toolTipButtons = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.picDesktop = new xServer.Controls.RapidPictureBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit();
|
||||
this.panelTop.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnStart
|
||||
|
@ -68,11 +71,11 @@ private void InitializeComponent()
|
|||
//
|
||||
// barQuality
|
||||
//
|
||||
this.barQuality.Location = new System.Drawing.Point(230, 3);
|
||||
this.barQuality.Location = new System.Drawing.Point(206, -1);
|
||||
this.barQuality.Maximum = 100;
|
||||
this.barQuality.Minimum = 1;
|
||||
this.barQuality.Name = "barQuality";
|
||||
this.barQuality.Size = new System.Drawing.Size(104, 45);
|
||||
this.barQuality.Size = new System.Drawing.Size(76, 45);
|
||||
this.barQuality.TabIndex = 3;
|
||||
this.barQuality.Value = 75;
|
||||
this.barQuality.Scroll += new System.EventHandler(this.barQuality_Scroll);
|
||||
|
@ -80,7 +83,7 @@ private void InitializeComponent()
|
|||
// lblQuality
|
||||
//
|
||||
this.lblQuality.AutoSize = true;
|
||||
this.lblQuality.Location = new System.Drawing.Point(184, 18);
|
||||
this.lblQuality.Location = new System.Drawing.Point(167, 5);
|
||||
this.lblQuality.Name = "lblQuality";
|
||||
this.lblQuality.Size = new System.Drawing.Size(47, 13);
|
||||
this.lblQuality.TabIndex = 4;
|
||||
|
@ -89,7 +92,7 @@ private void InitializeComponent()
|
|||
// lblQualityShow
|
||||
//
|
||||
this.lblQualityShow.AutoSize = true;
|
||||
this.lblQualityShow.Location = new System.Drawing.Point(254, 38);
|
||||
this.lblQualityShow.Location = new System.Drawing.Point(220, 26);
|
||||
this.lblQualityShow.Name = "lblQualityShow";
|
||||
this.lblQualityShow.Size = new System.Drawing.Size(52, 13);
|
||||
this.lblQualityShow.TabIndex = 5;
|
||||
|
@ -98,34 +101,18 @@ private void InitializeComponent()
|
|||
// btnMouse
|
||||
//
|
||||
this.btnMouse.Image = global::xServer.Properties.Resources.mouse_delete;
|
||||
this.btnMouse.Location = new System.Drawing.Point(340, 10);
|
||||
this.btnMouse.Location = new System.Drawing.Point(302, 5);
|
||||
this.btnMouse.Name = "btnMouse";
|
||||
this.btnMouse.Size = new System.Drawing.Size(28, 28);
|
||||
this.btnMouse.TabIndex = 6;
|
||||
this.toolTipButtons.SetToolTip(this.btnMouse, "Enable mouse input.");
|
||||
this.btnMouse.UseVisualStyleBackColor = true;
|
||||
this.btnMouse.Click += new System.EventHandler(this.btnMouse_Click);
|
||||
//
|
||||
// picDesktop
|
||||
//
|
||||
this.picDesktop.BackColor = System.Drawing.Color.Black;
|
||||
this.picDesktop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.picDesktop.Cursor = System.Windows.Forms.Cursors.Default;
|
||||
this.picDesktop.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.picDesktop.GetImageSafe = null;
|
||||
this.picDesktop.Location = new System.Drawing.Point(0, 0);
|
||||
this.picDesktop.Name = "picDesktop";
|
||||
this.picDesktop.Running = false;
|
||||
this.picDesktop.Size = new System.Drawing.Size(784, 562);
|
||||
this.picDesktop.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
|
||||
this.picDesktop.TabIndex = 0;
|
||||
this.picDesktop.TabStop = false;
|
||||
this.picDesktop.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseDown);
|
||||
this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove);
|
||||
this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp);
|
||||
//
|
||||
// panelTop
|
||||
//
|
||||
this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.panelTop.Controls.Add(this.btnKeyboard);
|
||||
this.panelTop.Controls.Add(this.cbMonitors);
|
||||
this.panelTop.Controls.Add(this.btnHide);
|
||||
this.panelTop.Controls.Add(this.lblQualityShow);
|
||||
|
@ -139,6 +126,17 @@ private void InitializeComponent()
|
|||
this.panelTop.Size = new System.Drawing.Size(384, 57);
|
||||
this.panelTop.TabIndex = 7;
|
||||
//
|
||||
// btnKeyboard
|
||||
//
|
||||
this.btnKeyboard.Image = global::xServer.Properties.Resources.keyboard_delete;
|
||||
this.btnKeyboard.Location = new System.Drawing.Point(336, 5);
|
||||
this.btnKeyboard.Name = "btnKeyboard";
|
||||
this.btnKeyboard.Size = new System.Drawing.Size(28, 28);
|
||||
this.btnKeyboard.TabIndex = 9;
|
||||
this.toolTipButtons.SetToolTip(this.btnKeyboard, "Enable keyboard input.");
|
||||
this.btnKeyboard.UseVisualStyleBackColor = true;
|
||||
this.btnKeyboard.Click += new System.EventHandler(this.btnKeyboard_Click);
|
||||
//
|
||||
// cbMonitors
|
||||
//
|
||||
this.cbMonitors.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
|
@ -169,6 +167,24 @@ private void InitializeComponent()
|
|||
this.btnShow.Visible = false;
|
||||
this.btnShow.Click += new System.EventHandler(this.btnShow_Click);
|
||||
//
|
||||
// picDesktop
|
||||
//
|
||||
this.picDesktop.BackColor = System.Drawing.Color.Black;
|
||||
this.picDesktop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.picDesktop.Cursor = System.Windows.Forms.Cursors.Default;
|
||||
this.picDesktop.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.picDesktop.GetImageSafe = null;
|
||||
this.picDesktop.Location = new System.Drawing.Point(0, 0);
|
||||
this.picDesktop.Name = "picDesktop";
|
||||
this.picDesktop.Running = false;
|
||||
this.picDesktop.Size = new System.Drawing.Size(784, 562);
|
||||
this.picDesktop.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
|
||||
this.picDesktop.TabIndex = 0;
|
||||
this.picDesktop.TabStop = false;
|
||||
this.picDesktop.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseDown);
|
||||
this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove);
|
||||
this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp);
|
||||
//
|
||||
// FrmRemoteDesktop
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
@ -188,9 +204,9 @@ private void InitializeComponent()
|
|||
this.Load += new System.EventHandler(this.FrmRemoteDesktop_Load);
|
||||
this.Resize += new System.EventHandler(this.FrmRemoteDesktop_Resize);
|
||||
((System.ComponentModel.ISupportInitialize)(this.barQuality)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).EndInit();
|
||||
this.panelTop.ResumeLayout(false);
|
||||
this.panelTop.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
@ -208,5 +224,7 @@ private void InitializeComponent()
|
|||
private System.Windows.Forms.Button btnShow;
|
||||
private xServer.Controls.RapidPictureBox picDesktop;
|
||||
private System.Windows.Forms.ComboBox cbMonitors;
|
||||
private System.Windows.Forms.Button btnKeyboard;
|
||||
private System.Windows.Forms.ToolTip toolTipButtons;
|
||||
}
|
||||
}
|
|
@ -4,20 +4,25 @@
|
|||
using xServer.Core.Helper;
|
||||
using xServer.Core.Networking;
|
||||
using xServer.Core.Utilities;
|
||||
using xServer.Core.MouseKeyHook;
|
||||
using xServer.Enums;
|
||||
|
||||
namespace xServer.Forms
|
||||
{
|
||||
//TODO: Register Hotkeys for WIN - and ALT-key combinations
|
||||
public partial class FrmRemoteDesktop : Form
|
||||
{
|
||||
public bool IsStarted { get; private set; }
|
||||
private readonly Client _connectClient;
|
||||
private bool _enableMouseInput;
|
||||
private bool _enableKeyboardInput;
|
||||
private IKeyboardMouseEvents _mEvents;
|
||||
|
||||
public FrmRemoteDesktop(Client c)
|
||||
{
|
||||
_connectClient = c;
|
||||
_connectClient.Value.FrmRdp = this;
|
||||
Subscribe(Hook.GlobalEvents());
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
@ -32,10 +37,28 @@ private void FrmRemoteDesktop_Load(object sender, EventArgs e)
|
|||
btnShow.Location = new Point(377, 0);
|
||||
btnShow.Left = (this.Width / 2) - (btnShow.Width / 2);
|
||||
|
||||
_enableKeyboardInput = false;
|
||||
|
||||
if (_connectClient.Value != null)
|
||||
new Core.Packets.ServerPackets.GetMonitors().Execute(_connectClient);
|
||||
}
|
||||
|
||||
private void Subscribe(IKeyboardMouseEvents events)
|
||||
{
|
||||
_mEvents = events;
|
||||
_mEvents.MouseWheel += MouseWheelEvent;
|
||||
_mEvents.KeyDown += OnKeyDown;
|
||||
_mEvents.KeyUp += OnKeyUp;
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (_mEvents == null) return;
|
||||
_mEvents.MouseWheel -= MouseWheelEvent;
|
||||
_mEvents.KeyDown -= OnKeyDown;
|
||||
_mEvents.KeyUp -= OnKeyUp;
|
||||
}
|
||||
|
||||
public void AddMonitors(int monitors)
|
||||
{
|
||||
try
|
||||
|
@ -94,6 +117,8 @@ private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e)
|
|||
picDesktop.Dispose();
|
||||
if (_connectClient.Value != null)
|
||||
_connectClient.Value.FrmRdp = null;
|
||||
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
private void FrmRemoteDesktop_Resize(object sender, EventArgs e)
|
||||
|
@ -118,6 +143,8 @@ private void btnStart_Click(object sender, EventArgs e)
|
|||
// Subscribe to the new frame counter.
|
||||
picDesktop.SetFrameUpdatedEvent(_frameCounter_FrameUpdated);
|
||||
|
||||
this.ActiveControl = picDesktop;
|
||||
|
||||
new Core.Packets.ServerPackets.GetDesktop(barQuality.Value, cbMonitors.SelectedIndex).Execute(_connectClient);
|
||||
}
|
||||
|
||||
|
@ -129,6 +156,8 @@ private void btnStop_Click(object sender, EventArgs e)
|
|||
|
||||
// Unsubscribe from the frame counter. It will be re-created when starting again.
|
||||
picDesktop.UnsetFrameUpdatedEvent(_frameCounter_FrameUpdated);
|
||||
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
|
||||
private void barQuality_Scroll(object sender, EventArgs e)
|
||||
|
@ -144,6 +173,8 @@ private void barQuality_Scroll(object sender, EventArgs e)
|
|||
lblQualityShow.Text += " (high)";
|
||||
else if (value >= 25)
|
||||
lblQualityShow.Text += " (mid)";
|
||||
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
|
||||
private void btnMouse_Click(object sender, EventArgs e)
|
||||
|
@ -152,14 +183,36 @@ private void btnMouse_Click(object sender, EventArgs e)
|
|||
{
|
||||
this.picDesktop.Cursor = Cursors.Default;
|
||||
btnMouse.Image = Properties.Resources.mouse_delete;
|
||||
toolTipButtons.SetToolTip(btnMouse, "Enable mouse input.");
|
||||
_enableMouseInput = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.picDesktop.Cursor = Cursors.Hand;
|
||||
btnMouse.Image = Properties.Resources.mouse_add;
|
||||
toolTipButtons.SetToolTip(btnMouse, "Disable mouse input.");
|
||||
_enableMouseInput = true;
|
||||
}
|
||||
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
|
||||
private void btnKeyboard_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_enableKeyboardInput)
|
||||
{
|
||||
btnKeyboard.Image = Properties.Resources.keyboard_delete;
|
||||
toolTipButtons.SetToolTip(btnMouse, "Enable keyboard input.");
|
||||
_enableKeyboardInput = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
btnKeyboard.Image = Properties.Resources.keyboard_add;
|
||||
toolTipButtons.SetToolTip(btnMouse, "Disable keyboard input.");
|
||||
_enableKeyboardInput = true;
|
||||
}
|
||||
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
|
||||
private int GetRemoteWidth(int localX)
|
||||
|
@ -174,7 +227,7 @@ private int GetRemoteHeight(int localY)
|
|||
|
||||
private void picDesktop_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled)
|
||||
if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
int local_x = e.X;
|
||||
int local_y = e.Y;
|
||||
|
@ -198,7 +251,7 @@ private void picDesktop_MouseDown(object sender, MouseEventArgs e)
|
|||
|
||||
private void picDesktop_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled)
|
||||
if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
int local_x = e.X;
|
||||
int local_y = e.Y;
|
||||
|
@ -222,7 +275,7 @@ private void picDesktop_MouseUp(object sender, MouseEventArgs e)
|
|||
|
||||
private void picDesktop_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled)
|
||||
if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
int local_x = e.X;
|
||||
int local_y = e.Y;
|
||||
|
@ -237,11 +290,39 @@ private void picDesktop_MouseMove(object sender, MouseEventArgs e)
|
|||
}
|
||||
}
|
||||
|
||||
private void MouseWheelEvent(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
if (_connectClient != null)
|
||||
new Core.Packets.ServerPackets.DoMouseEvent(e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, false, 0, 0, cbMonitors.SelectedIndex).Execute(_connectClient);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
if (_connectClient != null)
|
||||
new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, true).Execute(_connectClient);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus)
|
||||
{
|
||||
if (_connectClient != null)
|
||||
new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, false).Execute(_connectClient);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnHide_Click(object sender, EventArgs e)
|
||||
{
|
||||
panelTop.Visible = false;
|
||||
btnShow.Visible = true;
|
||||
btnHide.Visible = false;
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
|
||||
private void btnShow_Click(object sender, EventArgs e)
|
||||
|
@ -249,6 +330,7 @@ private void btnShow_Click(object sender, EventArgs e)
|
|||
panelTop.Visible = true;
|
||||
btnShow.Visible = false;
|
||||
btnHide.Visible = true;
|
||||
this.ActiveControl = picDesktop;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -200,6 +200,26 @@ internal class Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap keyboard_add {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("keyboard_add", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap keyboard_delete {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("keyboard_delete", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
|
|
@ -239,12 +239,18 @@
|
|||
<data name="application_delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\application_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="keyboard_add" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="logger" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\logger.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="run" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\run.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="registry" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="server_disconnect" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\server-disconnect.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
@ -255,7 +261,7 @@
|
|||
<value>..\images\information.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="registry" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="keyboard_delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\keyboard_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
|
@ -77,6 +77,47 @@
|
|||
<Compile Include="Core\Build\IconInjector.cs" />
|
||||
<Compile Include="Core\Helper\FileHelper.cs" />
|
||||
<Compile Include="Core\Helper\FormatHelper.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Hook.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeyArgs.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySet.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySetCollection.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySetsListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\IKeyboardEvents.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\IKeyboardMouseEvents.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\IMouseEvents.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppEventFacade.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppKeyListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppMouseListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\BaseListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\ButtonSet.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\Callback.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\EventFacade.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalEventFacade.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalKeyListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalMouseListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\KeyboardState.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\KeyListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\MouseListener.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\Implementation\Subscribe.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\KeyEventArgsExt.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\KeyPressEventArgsExt.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\MouseEventExtArgs.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\AppMouseStruct.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\CallbackData.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookHelper.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookIds.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookNativeMethods.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookProcedure.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookProcedureHandle.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookResult.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\KeyboardHookStruct.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\KeyboardNativeMethods.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\Messages.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\MouseNativeMethods.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\MouseStruct.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\Point.cs" />
|
||||
<Compile Include="Core\Helper\MouseKeyHook\WinApi\ThreadNativeMethods.cs" />
|
||||
<Compile Include="Core\Packets\ServerPackets\DoKeyboardEvent.cs" />
|
||||
<Compile Include="Core\Utilities\FrameCounter.cs" />
|
||||
<Compile Include="Core\Helper\NativeMethodsHelper.cs" />
|
||||
<Compile Include="Core\Helper\PlatformHelper.cs" />
|
||||
|
@ -456,6 +497,7 @@
|
|||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Core\Helper\MouseKeyHook\HotKeys\ReadMe.txt" />
|
||||
<Content Include="xRAT-64x64.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 683 B |
Binary file not shown.
After Width: | Height: | Size: 681 B |
Loading…
Reference in New Issue