Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SuperPutty/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
Expand All @@ -17,6 +18,7 @@
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
[assembly: InternalsVisibleTo("SuperPuttyUnitTests")]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7f061c0e-1ca2-43a8-92f5-49563ff4ee81")]
Expand Down
3 changes: 2 additions & 1 deletion SuperPutty/SuperPutty.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@
<Compile Include="Utils\GlobalHotKey.cs" />
<Compile Include="Utils\GlobalWindowEvents.cs" />
<Compile Include="Utils\HostConnectionString.cs" />
<Compile Include="Utils\KeyboardHookState.cs" />
<Compile Include="Utils\MinttyStartInfo.cs" />
<Compile Include="Utils\VNCStartInfo.cs" />
<Compile Include="Utils\RDPStartInfo.cs" />
Expand Down Expand Up @@ -518,4 +519,4 @@ SET errorlevel=</PostBuildEvent>
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
130 changes: 130 additions & 0 deletions SuperPutty/Utils/KeyboardHookState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Windows.Forms;

namespace SuperPutty.Utils
{
internal sealed class KeyboardHookState
{
internal static readonly UIntPtr SuperPuttyInputMarker = new UIntPtr(0x53505459u);

private bool isLeftControlDown;
private bool isRightControlDown;
private bool isLeftShiftDown;
private bool isRightShiftDown;
private bool isLeftAltDown;
private bool isRightAltDown;
private bool isLeftWinDown;
private bool isRightWinDown;

internal long Version { get; private set; }

internal bool IsControlDown { get { return isLeftControlDown || isRightControlDown; } }
internal bool IsShiftDown { get { return isLeftShiftDown || isRightShiftDown; } }
internal bool IsAltDown { get { return isLeftAltDown || isRightAltDown; } }
internal bool IsWinDown { get { return isLeftWinDown || isRightWinDown; } }

internal void Update(Keys keyCode, bool isKeyDown)
{
switch (keyCode)
{
case Keys.LControlKey:
SetState(ref isLeftControlDown, isKeyDown);
break;
case Keys.RControlKey:
SetState(ref isRightControlDown, isKeyDown);
break;
case Keys.LShiftKey:
SetState(ref isLeftShiftDown, isKeyDown);
break;
case Keys.RShiftKey:
SetState(ref isRightShiftDown, isKeyDown);
break;
case Keys.LMenu:
SetState(ref isLeftAltDown, isKeyDown);
break;
case Keys.RMenu:
SetState(ref isRightAltDown, isKeyDown);
break;
case Keys.LWin:
SetState(ref isLeftWinDown, isKeyDown);
break;
case Keys.RWin:
SetState(ref isRightWinDown, isKeyDown);
break;
}
}

internal void Synchronize(Func<Keys, bool> isKeyDown)
{
if (isKeyDown == null)
{
throw new ArgumentNullException("isKeyDown");
}

bool leftControlDown = isKeyDown(Keys.LControlKey);
bool rightControlDown = isKeyDown(Keys.RControlKey);
bool leftShiftDown = isKeyDown(Keys.LShiftKey);
bool rightShiftDown = isKeyDown(Keys.RShiftKey);
bool leftAltDown = isKeyDown(Keys.LMenu);
bool rightAltDown = isKeyDown(Keys.RMenu);
bool leftWinDown = isKeyDown(Keys.LWin);
bool rightWinDown = isKeyDown(Keys.RWin);

bool changed =
isLeftControlDown != leftControlDown ||
isRightControlDown != rightControlDown ||
isLeftShiftDown != leftShiftDown ||
isRightShiftDown != rightShiftDown ||
isLeftAltDown != leftAltDown ||
isRightAltDown != rightAltDown ||
isLeftWinDown != leftWinDown ||
isRightWinDown != rightWinDown;

isLeftControlDown = leftControlDown;
isRightControlDown = rightControlDown;
isLeftShiftDown = leftShiftDown;
isRightShiftDown = rightShiftDown;
isLeftAltDown = leftAltDown;
isRightAltDown = rightAltDown;
isLeftWinDown = leftWinDown;
isRightWinDown = rightWinDown;

if (changed)
{
Version++;
}
}

internal void Reset()
{
if (IsControlDown || IsShiftDown || IsAltDown || IsWinDown)
{
Version++;
}

isLeftControlDown = false;
isRightControlDown = false;
isLeftShiftDown = false;
isRightShiftDown = false;
isLeftAltDown = false;
isRightAltDown = false;
isLeftWinDown = false;
isRightWinDown = false;
}

internal static bool IsSuperPuttyInjectedInput(NativeMethods.KBDLLHOOKSTRUCT keyboardData)
{
return (keyboardData.flags & NativeMethods.LowLevelKeyboardFlags.Injected) != 0 &&
keyboardData.dwExtraInfo.Equals(SuperPuttyInputMarker);
}

private void SetState(ref bool currentState, bool newState)
{
if (currentState != newState)
{
currentState = newState;
Version++;
}
}
}
}
80 changes: 77 additions & 3 deletions SuperPutty/Utils/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,77 @@ public const uint
KEYEVENTF_EXTENDEDKEY = 0x0001,
KEYEVENTF_KEYUP = 0x0002;

public const uint INPUT_KEYBOARD = 1;

[Flags]
public enum LowLevelKeyboardFlags : uint
{
Extended = 0x01,
LowerIntegrityLevelInjected = 0x02,
Injected = 0x10,
AltDown = 0x20,
KeyUp = 0x80
}

[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public LowLevelKeyboardFlags flags;
public uint time;
public UIntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public uint type;
public InputUnion data;
}

[StructLayout(LayoutKind.Explicit)]
public struct InputUnion
{
[FieldOffset(0)]
public MOUSEINPUT mouse;

[FieldOffset(0)]
public KEYBDINPUT keyboard;

[FieldOffset(0)]
public HARDWAREINPUT hardware;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public UIntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public UIntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}

[Flags]
public enum AnimateWindowFlags
{
Expand Down Expand Up @@ -1102,9 +1173,6 @@ public static class HotKeysConstants
[DllImport("user32.dll")]
public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

[DllImport("user32.dll")]
public static extern short GetKeyState(int nVirtKey);

[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

Expand Down Expand Up @@ -1239,6 +1307,12 @@ public override string ToString()
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(uint idHook, LowLevelKMProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vKey);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);

Expand Down
Loading