├── KeyNStroke ├── app.ico ├── mouse.png ├── mouse_left.png ├── mouse_right.png ├── Resources │ ├── app.ico │ ├── mouse.png │ ├── mouse_left.png │ ├── mouse_right.png │ ├── mouse_middle.png │ ├── mouse_wheel_up.png │ ├── mouse_wheel_down.png │ ├── mouse_left_double.png │ ├── mouse_modifier_alt.png │ ├── mouse_modifier_ctrl.png │ ├── mouse_modifier_win.png │ ├── mouse_right_double.png │ ├── mouse_modifier_shift.png │ ├── updateKey.pub.xml │ └── mouse_test.svg ├── mouse_middle.png ├── mouse_wheel_up.png ├── mouse_wheel_down.png ├── mouse_left_double.png ├── mouse_right_double.png ├── FodyWeavers.xml ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── App.config ├── App.xaml ├── ResizeButton.xaml ├── ButtonIndicator2.xaml ├── Themes │ └── Generic.xaml ├── CursorIndicator1.xaml ├── ReadShortcut.xaml ├── AnnotateLine.xaml ├── UrlOpener.cs ├── Log.cs ├── LabeledSlider.xaml ├── ReadShortcut.xaml.cs ├── ButtonIndicator1.Designer.cs ├── EnumBindingSourceExtention.cs ├── UIHelper.cs ├── app.manifest ├── Welcome.xaml.cs ├── packages.config ├── Welcome.xaml ├── KeystrokeDisplay.xaml ├── NativeMethodsGWL.cs ├── NativeMethodsMouse.cs ├── Updater │ ├── Updater.cs │ └── Admininstration.cs ├── LabeledSlider.py ├── ButtonIndicator1.resx ├── MouseRawEvent.cs ├── ResizeButton.xaml.cs ├── CursorIndicator1.xaml.cs ├── SpecialkeysParser.cs ├── NativeMethodsKeyboard.cs ├── KeystrokeEvent.cs ├── AnnotateLine.xaml.cs ├── NativeMethodsWindow.cs ├── LabeledSlider.xaml.cs ├── FodyWeavers.xsd ├── KeyboardLayoutParser.cs └── MouseHook.cs ├── Other └── twittericon.png ├── Screenshots ├── mouse.png ├── cursor.png ├── example1.png ├── settings.png ├── resizemode.png ├── Smartscreen1.png ├── bottom_center.png ├── bottom_right.png ├── comic_sans_ms.png └── ctrl_scroll.png ├── Releases ├── v0.0.1 │ └── PxKeystrokesUi.exe ├── v0.1.1 │ └── PxKeystrokesUi.exe ├── v0.1.2 │ └── PxKeystrokesUi.exe ├── v0.1.3 │ └── PxKeystrokesUi.exe ├── v0.2.0 │ └── PxKeystrokesUi.exe ├── v0.3.0 │ └── PxKeystrokesUi.exe ├── v0.3.1 │ └── PxKeystrokesUi.exe ├── v1.0.0 │ └── Key-n-Stroke.exe ├── v1.0.1 │ └── Key-n-Stroke.exe └── v1.1.0 │ └── Key-n-Stroke.exe ├── version.json ├── Directory.Build.props ├── KeyNStroke.sln ├── .github └── workflows │ └── ci.yml ├── .gitattributes ├── .gitignore ├── README.md └── DEVELOP.md /KeyNStroke/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/app.ico -------------------------------------------------------------------------------- /KeyNStroke/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse.png -------------------------------------------------------------------------------- /Other/twittericon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Other/twittericon.png -------------------------------------------------------------------------------- /Screenshots/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/mouse.png -------------------------------------------------------------------------------- /Screenshots/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/cursor.png -------------------------------------------------------------------------------- /Screenshots/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/example1.png -------------------------------------------------------------------------------- /Screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/settings.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_left.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_right.png -------------------------------------------------------------------------------- /Screenshots/resizemode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/resizemode.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/app.ico -------------------------------------------------------------------------------- /KeyNStroke/mouse_middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_middle.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_wheel_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_wheel_up.png -------------------------------------------------------------------------------- /Screenshots/Smartscreen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/Smartscreen1.png -------------------------------------------------------------------------------- /Screenshots/bottom_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/bottom_center.png -------------------------------------------------------------------------------- /Screenshots/bottom_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/bottom_right.png -------------------------------------------------------------------------------- /Screenshots/comic_sans_ms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/comic_sans_ms.png -------------------------------------------------------------------------------- /Screenshots/ctrl_scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Screenshots/ctrl_scroll.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_wheel_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_wheel_down.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_left_double.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_left_double.png -------------------------------------------------------------------------------- /KeyNStroke/mouse_right_double.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/mouse_right_double.png -------------------------------------------------------------------------------- /Releases/v0.0.1/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.0.1/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.1.1/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.1.1/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.1.2/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.1.2/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.1.3/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.1.3/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.2.0/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.2.0/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.3.0/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.3.0/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v0.3.1/PxKeystrokesUi.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v0.3.1/PxKeystrokesUi.exe -------------------------------------------------------------------------------- /Releases/v1.0.0/Key-n-Stroke.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v1.0.0/Key-n-Stroke.exe -------------------------------------------------------------------------------- /Releases/v1.0.1/Key-n-Stroke.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v1.0.1/Key-n-Stroke.exe -------------------------------------------------------------------------------- /Releases/v1.1.0/Key-n-Stroke.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/Releases/v1.1.0/Key-n-Stroke.exe -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_left.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_right.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_middle.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_wheel_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_wheel_up.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_wheel_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_wheel_down.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_left_double.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_left_double.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_modifier_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_modifier_alt.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_modifier_ctrl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_modifier_ctrl.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_modifier_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_modifier_win.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_right_double.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_right_double.png -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_modifier_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/HEAD/KeyNStroke/Resources/mouse_modifier_shift.png -------------------------------------------------------------------------------- /KeyNStroke/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /KeyNStroke/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /KeyNStroke/Resources/updateKey.pub.xml: -------------------------------------------------------------------------------- 1 | pKQQkG41DRCmvzfZbQUleBei4oBubdxsxyqM9LbKMI42EysS6Cpgsc4M90T9c7+nhLqPmqHTsU0pPJoABJe80NnaPvqUK16fIApn0jPjCYTTdnMUS7+Gzq8rBzwdgv0J6cSc5GMPDqnpKcpn1Jl5sX6RvSTH8r9MBgHAu+jMheU=AQAB -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "1.1.0", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/master$", 6 | "^refs/heads/develop$", 7 | "^refs/heads/rel/v\\d+\\.\\d+" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /KeyNStroke/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /KeyNStroke/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 3.4.244 6 | all 7 | 8 | 9 | -------------------------------------------------------------------------------- /KeyNStroke/ResizeButton.xaml: -------------------------------------------------------------------------------- 1 | 9 | 81 | 91 | 94 | 95 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /KeyNStroke/NativeMethodsGWL.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace KeyNStroke 9 | { 10 | /// 11 | /// Each Window has some associated memory with holds flags for window behaviour 12 | /// The bytes that hold these informations are numbered as nIndex with the values GWL.GWL_**** 13 | /// These Methods can be used to define a click through behaviour 14 | /// 15 | public class NativeMethodsGWL 16 | { 17 | /// 18 | /// Activate Click Through 19 | /// 20 | /// The Forms myform.Handle 21 | public static void ClickThrough(IntPtr Handle) 22 | { 23 | uint current_val = GetWindowLong(Handle, GWL.GWL_EXSTYLE); 24 | uint changed_val = Convert.ToUInt32(current_val | (uint)WindowStyles.WS_EX_LAYERED | (uint)WindowStyles.WS_EX_TRANSPARENT); 25 | SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val); 26 | } 27 | 28 | /// 29 | /// Deactivate Click Through 30 | /// 31 | /// The Forms myform.Handle 32 | public static void CatchClicks(IntPtr Handle) 33 | { 34 | uint current_val = GetWindowLong(Handle, GWL.GWL_EXSTYLE); 35 | uint changed_val = Convert.ToUInt32(current_val & ~((uint)WindowStyles.WS_EX_LAYERED)) & ~((uint)WindowStyles.WS_EX_TRANSPARENT); 36 | SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val); 37 | } 38 | 39 | public static void HideFromAltTab(IntPtr Handle) 40 | { 41 | int current_val = (int)GetWindowLong(Handle, GWL.GWL_EXSTYLE); 42 | uint changed_val = Convert.ToUInt32(current_val | (uint)WindowStyles.WS_EX_TOOLWINDOW); 43 | SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val); 44 | } 45 | 46 | 47 | /// 48 | /// These are the possible longs which can be get and set via GetWindowLong and ... 49 | /// 50 | public enum GWL : int 51 | { 52 | GWL_WNDPROC = (-4), 53 | GWL_HINSTANCE = (-6), 54 | GWL_HWNDPARENT = (-8), 55 | GWL_STYLE = (-16), 56 | GWL_EXSTYLE = (-20), 57 | GWL_USERDATA = (-21), 58 | GWL_ID = (-12) 59 | } 60 | 61 | 62 | /// 63 | /// These are the Flags for the Extended Style Bytes (nIndex=GWL_EXTSTYLE) 64 | /// 65 | [Flags] 66 | public enum WindowStyles : uint 67 | { 68 | //Extended Window Styles 69 | 70 | WS_EX_DLGMODALFRAME = 0x00000001, 71 | WS_EX_NOPARENTNOTIFY = 0x00000004, 72 | WS_EX_TOPMOST = 0x00000008, 73 | WS_EX_ACCEPTFILES = 0x00000010, 74 | WS_EX_TRANSPARENT = 0x00000020, 75 | WS_EX_LAYERED = 0x00080000, 76 | 77 | WS_EX_TOOLWINDOW = 0x00000080, 78 | } 79 | 80 | // Choose 32/64 bit variant of function 81 | 82 | /// 83 | /// returns long bytes at nIndex 84 | /// 85 | /// 86 | /// 87 | /// the LONG value 88 | public static uint GetWindowLong(IntPtr hWnd, GWL nIndex) 89 | { 90 | if (IntPtr.Size == 4) 91 | { 92 | return (uint)GetWindowLong32(hWnd, (int)nIndex); 93 | } 94 | return (uint)GetWindowLongPtr64(hWnd, (int)nIndex); 95 | } 96 | 97 | /// 98 | /// sets the LONG value at nIndex to nwNewLong 99 | /// 100 | /// 101 | /// 102 | /// 103 | /// dunno 104 | public static int SetWindowLong(IntPtr hWnd, GWL nIndex, uint dwNewLong) 105 | { 106 | if (IntPtr.Size == 4) 107 | { 108 | return SetWindowLong32(hWnd, (int)nIndex, dwNewLong); 109 | } 110 | return SetWindowLongPtr64(hWnd, (int)nIndex, dwNewLong); 111 | } 112 | 113 | // Different Functions for Win32 and 64 bit 114 | 115 | [DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)] 116 | private static extern int GetWindowLong32(IntPtr hWnd, int nIndex); 117 | 118 | [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)] 119 | private static extern int GetWindowLongPtr64(IntPtr hWnd, int nIndex); 120 | 121 | [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)] 122 | private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, uint dwNewLong); 123 | 124 | [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)] 125 | private static extern int SetWindowLongPtr64(IntPtr hWnd, int nIndex, uint dwNewLong); 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /KeyNStroke/NativeMethodsMouse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace KeyNStroke 9 | { 10 | [ComVisibleAttribute(false), 11 | System.Security.SuppressUnmanagedCodeSecurity()] 12 | public class NativeMethodsMouse 13 | { 14 | 15 | public const int WM_MOUSEMOVE = 0x200; 16 | public const int WM_LBUTTONDOWN = 0x201; 17 | public const int WM_LBUTTONUP = 0x202; 18 | public const int WM_LBUTTONDBLCLK = 0x203; 19 | public const int WM_RBUTTONDOWN = 0x204; 20 | public const int WM_RBUTTONUP = 0x205; 21 | public const int WM_RBUTTONDBLCLK = 0x206; 22 | public const int WM_MBUTTONDOWN = 0x207; 23 | public const int WM_MBUTTONUP = 0x208; 24 | public const int WM_MBUTTONDBLCLK = 0x209; 25 | public const int WM_MOUSEWHEEL1 = 0x20A; 26 | public const int WM_XBUTTONDOWN = 0x20B; 27 | public const int WM_XBUTTONUP = 0x20C; 28 | public const int WM_XBUTTONDBLCLK = 0x20D; 29 | public const int WM_MOUSEHWHEEL2 = 0x20E; 30 | 31 | 32 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 33 | public static extern IntPtr SetWindowsHookEx(int idHook, 34 | HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId); 35 | 36 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 37 | [return: MarshalAs(UnmanagedType.Bool)] 38 | public static extern bool UnhookWindowsHookEx(IntPtr hhk); 39 | 40 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 41 | public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 42 | UIntPtr wParam, ref MSLLHOOKSTRUCT lParam); 43 | 44 | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 45 | public static extern int GetDoubleClickTime(); 46 | 47 | public const int WH_MOUSE_LL = 14; 48 | public const int HC_ACTION = 0; // The wParam and lParam parameters contain information about a mouse message. 49 | 50 | [StructLayout(LayoutKind.Sequential)] 51 | public struct POINT 52 | { 53 | public Int32 X; 54 | public Int32 Y; 55 | 56 | public POINT(int x, int y) 57 | { 58 | this.X = x; 59 | this.Y = y; 60 | } 61 | 62 | public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { } 63 | 64 | public static implicit operator System.Drawing.Point(POINT p) 65 | { 66 | return new System.Drawing.Point(p.X, p.Y); 67 | } 68 | 69 | public static implicit operator POINT(System.Drawing.Point p) 70 | { 71 | return new POINT(p.X, p.Y); 72 | } 73 | 74 | public static implicit operator System.Windows.Point(POINT p) 75 | { 76 | return new System.Windows.Point(p.X, p.Y); 77 | } 78 | 79 | public bool Equals(POINT p2) 80 | { 81 | return X == p2.X && Y == p2.Y; 82 | } 83 | } 84 | 85 | [StructLayout(LayoutKind.Sequential)] 86 | public struct MSLLHOOKSTRUCT { 87 | public POINT pt; 88 | public UInt32 mouseData; // be careful, this must be ints, not uints (was wrong before I changed it...). regards, cmew. 89 | public UInt32 flags; 90 | public UInt32 time; 91 | public UIntPtr dwExtraInfo; 92 | } 93 | 94 | public delegate IntPtr HookHandlerDelegate(int nCode, 95 | UIntPtr wParam, 96 | ref MSLLHOOKSTRUCT lParam); 97 | 98 | 99 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 100 | public static extern bool GetCursorPos(ref NativeMethodsMouse.POINT lpPoint); 101 | 102 | public static POINT CursorPosition 103 | { 104 | get 105 | { 106 | POINT pos = new POINT(0, 0); 107 | GetCursorPos(ref pos); 108 | return pos; 109 | } 110 | } 111 | 112 | 113 | [StructLayout(LayoutKind.Sequential)] 114 | public struct CURSORINFO { 115 | public UInt32 cbSize; 116 | public UInt32 flags; 117 | public UIntPtr hCursor; 118 | public POINT ptScreenPos; 119 | } 120 | public const UInt32 CURSOR_HIDDEN = 0x00000000; 121 | public const UInt32 CURSOR_SHOWING = 0x00000001; 122 | public const UInt32 CURSOR_SUPPRESSED = 0x00000002; 123 | 124 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 125 | public static extern bool GetCursorInfo(ref CURSORINFO pci); 126 | 127 | public static CURSORINFO GetCursorInfoWrapper() 128 | { 129 | CURSORINFO info = new CURSORINFO(); 130 | info.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(); 131 | GetCursorInfo(ref info); 132 | return info; 133 | } 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /KeyNStroke/Updater/Updater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Diagnostics; 4 | 5 | namespace KeyNStroke.Updater 6 | { 7 | class Updater 8 | { 9 | public static Statemachine Instance 10 | { 11 | get { return Statemachine.Instance; } 12 | } 13 | 14 | 15 | private Updater() 16 | { 17 | } 18 | 19 | /// 20 | /// Returns true if the program should exit 21 | /// 22 | /// 23 | static public bool HandleArgs(string[] args) 24 | { 25 | if (args.Length > 0) 26 | { 27 | if (args[0] == "--update-step2" && args.Length == 3) 28 | { 29 | UpdateStep2(args[1], int.Parse(args[2])); 30 | return true; 31 | } 32 | if (args[0] == "--update-step3" && args.Length == 3) 33 | { 34 | UpdateStep3(args[1], int.Parse(args[2])); 35 | return false; // continue running 36 | } 37 | if (Admininstration.HandleArgs(args)) 38 | { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | 46 | 47 | static public void TriggerUpdateStep2(byte[] update) 48 | { 49 | string oldExePath = System.Reflection.Assembly.GetEntryAssembly().Location; 50 | string tmpExeName = $"Key-n-Stroke_Updater.exe"; 51 | string oldExeParentFolder = Path.GetDirectoryName(oldExePath); // may not be writable 52 | string systemTmpFolder = Path.GetTempPath(); // Should be writable in any case 53 | string tmpExePath = Path.Combine(systemTmpFolder, tmpExeName); 54 | 55 | using (var fs = new FileStream(tmpExePath, FileMode.Create, FileAccess.Write)) 56 | { 57 | fs.Write(update, 0, update.Length); 58 | } 59 | int ownPid = Process.GetCurrentProcess().Id; 60 | 61 | ProcessStartInfo psi = new ProcessStartInfo 62 | { 63 | FileName = tmpExePath, 64 | WorkingDirectory = Path.GetDirectoryName(tmpExePath), 65 | Arguments = $"--update-step2 \"{oldExePath}\" {ownPid}" 66 | }; 67 | 68 | if (!Utils.IsDirectoryWritable(oldExeParentFolder)) 69 | { 70 | psi.UseShellExecute = true; 71 | psi.Verb = "runas"; 72 | } 73 | 74 | Process.Start(psi); 75 | // Shutdown own process immediately! 76 | } 77 | 78 | /// 79 | /// Handles the swap of the executables. 80 | /// The we are the updated executable and the previous executable (the one that started us) must be overwritten by us. 81 | /// 82 | /// 83 | static public void UpdateStep2(string oldExePath, int startingProcessPid) 84 | { 85 | try 86 | { 87 | Process orig = Process.GetProcessById(startingProcessPid); 88 | if (!orig.WaitForExit(60000)) 89 | { 90 | Console.WriteLine("ERROR: Calling process did not exit in time"); 91 | return; 92 | } 93 | orig.Close(); 94 | } catch (ArgumentException) 95 | { 96 | // The process specified by the processId parameter is not running. The identifier might be expired. 97 | } 98 | 99 | string tmpExePath = System.Reflection.Assembly.GetEntryAssembly().Location; 100 | Log.e("UPDATE", $"Copy from {tmpExePath} to {oldExePath}"); 101 | if (File.Exists(oldExePath)) 102 | { 103 | File.Delete(oldExePath); 104 | } 105 | File.Copy(tmpExePath, oldExePath); 106 | 107 | int ownPid = Process.GetCurrentProcess().Id; 108 | 109 | ProcessStartInfo psi = new ProcessStartInfo 110 | { 111 | FileName = oldExePath, 112 | WorkingDirectory = Path.GetDirectoryName(oldExePath), 113 | Arguments = $"--update-step3 \"{tmpExePath}\" {ownPid}" 114 | }; 115 | 116 | Process.Start(psi); // Even if the updater has been started as Admin, this will start as the original user again. 117 | // Shutdown own process immediately 118 | } 119 | 120 | /// 121 | /// Delete the temporary executable 122 | /// 123 | /// 124 | /// 125 | static public void UpdateStep3(string tmpExePath, int startingProcessPid) 126 | { 127 | try 128 | { 129 | Process orig = Process.GetProcessById(startingProcessPid); 130 | if (!orig.WaitForExit(60000)) 131 | { 132 | Console.WriteLine("ERROR: Calling process did not exit in time"); 133 | return; 134 | } 135 | orig.Close(); 136 | } 137 | catch (ArgumentException) 138 | { 139 | // The process specified by the processId parameter is not running. The identifier might be expired. 140 | } 141 | 142 | if (File.Exists(tmpExePath)) 143 | { 144 | Log.e("UPDATE", $"Deleted {tmpExePath}"); 145 | File.Delete(tmpExePath); 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /KeyNStroke/LabeledSlider.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import math 4 | from matplotlib.widgets import Slider 5 | 6 | 7 | # LabeledSlider.Value X 8 | # XX 9 | # f(x) ^ + XX 10 | # | | XX 11 | # | | XX 12 | # ymax +--+ +------XX------+ 13 | # | XX 14 | # | X| 15 | # | XX | 16 | # | X + 17 | # | XX 18 | # | XXX 19 | # | XX 20 | # | XXX 21 | # | XXX 22 | # | XX 23 | # | + XXX 24 | # | | XXXX 25 | # | | XXXX 26 | #ys(ysper) +--+ +---XXXX--+ 27 | # | XXXX | 28 | # | XXXXXX | 29 | # | XXXXXXXX | 30 | # ymin +--XXXXXXXXXXX | 31 | # | + 32 | # | 33 | # | 34 | # +----------------------------+--------------------------+-----------------------> inner Slider.Value 35 | # | | | x 36 | # + + + 37 | # x0=0 xs=x1/2 x1 38 | # 39 | # 40 | # y = f(x) = a + b * exp(c * x) 41 | # 42 | # Calculate a, b, c from the constants ymin, ys, ymax, x1 so that f(x) fulfills (1), (2) and (3). 43 | # 44 | # (1) f(x = x0 = 0) = ymin 45 | # (2) f(x = xs = x1 / 2) = ys 46 | # (3) f(x = x1) = ymax 47 | # 48 | # xs is not free to chose but set to x1/2 so that while solving the equations, we magically get a quadratic equation. 49 | # 50 | # (1) ymin = a + b 51 | # (2) ys = a + b * exp(c * x1 / 2) 52 | # (3) ymax = a + b * exp(c * x1) 53 | # 54 | # (1) for a (4) a = ymin - b 55 | # (4) in (2) (5) ys = ymin - b + b * exp(c * x1 / 2) 56 | # (4) in (3) (6) ymax = ymin - b + b * exp(c * x1) 57 | # -ymin (6) ymax - ymin = b * (exp(c * x1) - 1) 58 | # solve (5) for b (7) b = (ys - ymin) / (exp(c * x1 / 2) - 1) 59 | # (7) in (6) (8) ymax - ymin = (ys - ymin) / (exp(c * x1 / 2) - 1) * (exp(c * x1) - 1) 60 | # *exp(..), -rhs (8) (ymax - ymin) * (exp(c * x1 / 2) - 1) - (ys - ymin) * (exp(c * x1) - 1) = 0 61 | # set (9) d = (ymin - ymax) / (ys - ymin) 62 | # substitute (9) in (8) (10) exp(c * x1 ) + d * e(c * x1 / 2) + (-1 - d) = 0 63 | # in first exp: /2*2 (10) exp(c * x1 / 2) ** 2 + d * e(c * x1 / 2) + (-1 - d) = 0 64 | # set (11) v = exp(c * x1 / 2) 65 | # substitute (11) in (10) (12) v ** 2 + d * v + (-1 - d) == 0 66 | # solve quadratic eq (12) (13) v1,2 = - (d/2) +- sqrt( (d/2)**2 - (-1-d) ) 67 | # solve (11) for c (14) c = 2 * ln(g) / x1 68 | # 69 | # calculate v1 with (13) and (9). Use (14), (7), (4) to calculate a,b,c 70 | 71 | 72 | fig, ax = plt.subplots() 73 | plt.subplots_adjust(bottom=0.25) 74 | 75 | 76 | 77 | 78 | ymax = 120. 79 | ymin = 3. 80 | #ys= 81 | ysper = 0.2 # value from 0 to 1 that gets mapped to the intervall [ymin+1%, ((ymin+ymax/2)-1%] and used as ys 82 | 83 | x0=0. 84 | x1=1000. 85 | xs=x1/2 86 | 87 | 88 | def pq(p, q): 89 | r1 = (-(p/2)) 90 | r2 = np.sqrt((p/2)**2 - q) 91 | return (r1 + r2, r1 - r2) 92 | 93 | def calc(ysper): 94 | ysmin = ymin * 1.01 95 | ysmax = ((ymin+ymax)/2) * 0.99 96 | ys = ysmin + ysper * (ysmax - ysmin) 97 | 98 | d=(ymin-ymax)/(ys-ymin) 99 | v1, v2 = pq(d, -1-d) 100 | 101 | c = 2 * np.log(v1) / x1 102 | b = (ys - ymin) / (np.exp(c*x1/2) - 1) 103 | a = ymin - b 104 | 105 | t = np.exp(c*x1) + 1 + d * np.exp(c*x1/2) - d 106 | t1 = (ymax-ymin) * (np.exp(c*x1/2)-1) - (ys-ymin) * (np.exp(c*x1)-1) 107 | 108 | print(f"v1={v1:.3} v2={v2:.3} a={a:.3} b={b:.3} c={c:.3} f(x0)=a+b={a+b:.3}=={ymin:.3}" 109 | f" f(xs)=a+bexp(c*x1/2)={a+b*np.exp(c*x1/2):.3}=={ys:.3}" 110 | f" f(x1)=a+bexp(c*x1)={a+b*np.exp(c*x1):.3}=={ymax:.3}") 111 | 112 | x = np.arange(x0, x1, (x1-x0)/1000) 113 | y = a + b * np.exp(c*x) 114 | 115 | return x, y, ys 116 | 117 | x, y, ys = calc(ysper) 118 | l1, = plt.plot(x, y) 119 | 120 | plt.plot([x0, x1], [ymax, ymax]) 121 | plt.plot([x0, x1], [ymin, ymin]) 122 | l2, = plt.plot([x0, x1], [ys, ys]) 123 | 124 | plt.plot([x0, x0], [0, ymax]) 125 | plt.plot([x1, x1], [0, ymax]) 126 | plt.plot([xs, xs], [0, ymax]) 127 | 128 | 129 | def update(ysper): 130 | x, y, ys = calc(ysper) 131 | l1.set_ydata(y) 132 | l1.set_xdata(x) 133 | l2.set_ydata([ys, ys]) 134 | 135 | axys = plt.axes([0.25, 0.1, 0.65, 0.03]) 136 | sys = Slider(axys, 'ys %', 0, 1.0, valinit=ysper) 137 | 138 | sys.on_changed(update) 139 | 140 | plt.show() -------------------------------------------------------------------------------- /KeyNStroke/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /KeyNStroke/ButtonIndicator1.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /KeyNStroke/MouseRawEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using KeyNStroke; 8 | 9 | namespace KeyNStroke 10 | { 11 | public enum MouseEventType 12 | { 13 | MOUSEMOVE, 14 | LBUTTONDOWN, 15 | LBUTTONUP, 16 | LBUTTONDBLCLK, 17 | RBUTTONDOWN, 18 | RBUTTONUP, 19 | RBUTTONDBLCLK, 20 | MBUTTONDOWN, 21 | MBUTTONUP, 22 | MBUTTONDBLCLK, 23 | MOUSEWHEEL1, 24 | XBUTTONDOWN, 25 | XBUTTONUP, 26 | XBUTTONDBLCLK, 27 | MOUSEHWHEEL2 28 | } 29 | 30 | public enum MouseButton 31 | { 32 | LButton, 33 | RButton, 34 | MButton, 35 | XButton, 36 | None 37 | } 38 | 39 | public enum MouseAction 40 | { 41 | Up, 42 | Down, 43 | DblClk, 44 | Move, 45 | Wheel, 46 | } 47 | 48 | public class MouseRawEventArgs 49 | { 50 | public NativeMethodsMouse.MSLLHOOKSTRUCT Msllhookstruct; 51 | public MouseEventType Event; 52 | public MouseButton Button; 53 | public MouseAction Action; 54 | public int wheelDelta = 0; 55 | public bool preventDefault = false; 56 | 57 | public MouseRawEventArgs(NativeMethodsMouse.MSLLHOOKSTRUCT msllhookstruct) 58 | { 59 | this.Msllhookstruct = msllhookstruct; 60 | } 61 | 62 | public NativeMethodsMouse.POINT Position 63 | { 64 | get { return Msllhookstruct.pt; } 65 | } 66 | 67 | public void ParseWparam(UIntPtr wParam) 68 | { 69 | switch((int) wParam) 70 | { 71 | case NativeMethodsMouse.WM_MOUSEMOVE: 72 | Event = MouseEventType.MOUSEMOVE; 73 | Button = MouseButton.None; 74 | Action = MouseAction.Move; 75 | break; 76 | case NativeMethodsMouse.WM_LBUTTONDOWN: 77 | Event = MouseEventType.LBUTTONDOWN; 78 | Button = MouseButton.LButton; 79 | Action = MouseAction.Down; 80 | break; 81 | case NativeMethodsMouse.WM_LBUTTONUP: 82 | Event = MouseEventType.LBUTTONUP; 83 | Button = MouseButton.LButton; 84 | Action = MouseAction.Up; 85 | break; 86 | case NativeMethodsMouse.WM_LBUTTONDBLCLK: 87 | Event = MouseEventType.LBUTTONDBLCLK; 88 | Button = MouseButton.LButton; 89 | Action = MouseAction.DblClk; 90 | break; 91 | case NativeMethodsMouse.WM_RBUTTONDOWN: 92 | Event = MouseEventType.RBUTTONDOWN; 93 | Button = MouseButton.RButton; 94 | Action = MouseAction.Down; 95 | break; 96 | case NativeMethodsMouse.WM_RBUTTONUP: 97 | Event = MouseEventType.RBUTTONUP; 98 | Button = MouseButton.RButton; 99 | Action = MouseAction.Up; 100 | break; 101 | case NativeMethodsMouse.WM_RBUTTONDBLCLK: 102 | Event = MouseEventType.RBUTTONDBLCLK; 103 | Button = MouseButton.RButton; 104 | Action = MouseAction.DblClk; 105 | break; 106 | case NativeMethodsMouse.WM_MBUTTONDOWN: 107 | Event = MouseEventType.MBUTTONDOWN; 108 | Button = MouseButton.MButton; 109 | Action = MouseAction.Down; 110 | break; 111 | case NativeMethodsMouse.WM_MBUTTONUP: 112 | Event = MouseEventType.MBUTTONUP; 113 | Button = MouseButton.MButton; 114 | Action = MouseAction.Up; 115 | break; 116 | case NativeMethodsMouse.WM_MBUTTONDBLCLK: 117 | Event = MouseEventType.MBUTTONDBLCLK; 118 | Button = MouseButton.MButton; 119 | Action = MouseAction.DblClk; 120 | break; 121 | case NativeMethodsMouse.WM_XBUTTONDOWN: 122 | Event = MouseEventType.XBUTTONDOWN; 123 | Button = MouseButton.XButton; 124 | Action = MouseAction.Down; 125 | break; 126 | case NativeMethodsMouse.WM_XBUTTONUP: 127 | Event = MouseEventType.XBUTTONUP; 128 | Button = MouseButton.XButton; 129 | Action = MouseAction.Up; 130 | break; 131 | case NativeMethodsMouse.WM_XBUTTONDBLCLK: 132 | Event = MouseEventType.XBUTTONDBLCLK; 133 | Button = MouseButton.XButton; 134 | Action = MouseAction.DblClk; 135 | break; 136 | case NativeMethodsMouse.WM_MOUSEWHEEL1: 137 | Event = MouseEventType.MOUSEWHEEL1; 138 | Button = MouseButton.None; 139 | Action = MouseAction.Wheel; 140 | 141 | unchecked { 142 | wheelDelta = BitConverter.ToInt16(BitConverter.GetBytes(Msllhookstruct.mouseData), 2); 143 | } 144 | break; 145 | case NativeMethodsMouse.WM_MOUSEHWHEEL2: 146 | Event = MouseEventType.MOUSEHWHEEL2; 147 | Button = MouseButton.None; 148 | Action = MouseAction.Wheel; 149 | unchecked { 150 | wheelDelta = BitConverter.ToInt16(BitConverter.GetBytes(Msllhookstruct.mouseData), 2); 151 | } 152 | break; 153 | default: 154 | Log.e("ME", "Unknown Mouse Event: " + wParam.ToString()); 155 | break; 156 | } 157 | } 158 | } 159 | 160 | public delegate void MouseRawEventHandler(MouseRawEventArgs raw_e); 161 | public delegate void CursorEventHandler(bool visible); 162 | 163 | public interface IMouseRawEventProvider : IDisposable 164 | { 165 | event MouseRawEventHandler MouseEvent; 166 | event CursorEventHandler CursorEvent; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /KeyNStroke/ResizeButton.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Interop; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | using System.Windows.Threading; 17 | 18 | namespace KeyNStroke 19 | { 20 | 21 | 22 | /// 23 | /// Interaktionslogik für WindowResizeButton.xaml 24 | /// 25 | public partial class ResizeButton : UserControl 26 | { 27 | 28 | private Point dragStartCursorPosition; 29 | private Point lastTickCursorPosition; 30 | private Point currentCursorPosition; 31 | private Size dragStartSize; 32 | private Vector dragStartWindowSizeReal; 33 | private Vector minSize; 34 | 35 | private Window window; 36 | private IntPtr windowHandle; 37 | private FrameworkElement CurrentParent; 38 | private MouseHook mouseHook = null; 39 | 40 | public ResizeButton() 41 | { 42 | InitializeComponent(); 43 | } 44 | 45 | private SettingsStore settings; 46 | 47 | public SettingsStore Settings 48 | { 49 | set { settings = value; } 50 | get { return settings; } 51 | } 52 | 53 | public ResizeTarget ResizeTarget 54 | { 55 | get { return (ResizeTarget)this.GetValue(ResizeTargetProperty); } 56 | set { this.SetValue(ResizeTargetProperty, value); } 57 | } 58 | public static readonly DependencyProperty ResizeTargetProperty = DependencyProperty.Register( 59 | "ResizeTarget", typeof(ResizeTarget), typeof(ResizeButton), new PropertyMetadata(ResizeTarget.Window)); 60 | 61 | private void resizeWindowButton_MouseDown(object sender, MouseButtonEventArgs e) 62 | { 63 | if (mouseHook == null) 64 | { 65 | if (ResizeTarget == ResizeTarget.Window) 66 | { 67 | window = Window.GetWindow(this); 68 | windowHandle = new WindowInteropHelper(window).Handle; 69 | dragStartSize = new Size(window.Width, window.Height); 70 | dragStartWindowSizeReal = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.Transform(new Vector(dragStartSize.Width, dragStartSize.Height)); 71 | minSize = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.Transform(new Vector(this.Width, this.Height)); 72 | } 73 | else 74 | { 75 | CurrentParent = (FrameworkElement) Parent; 76 | dragStartSize = new Size(CurrentParent.Width, CurrentParent.Height); 77 | minSize = new Vector(this.Width, this.Height); 78 | } 79 | mouseHook = new MouseHook(settings); 80 | mouseHook.MouseEvent += MouseHook_MouseEvent; 81 | 82 | lastTickCursorPosition = currentCursorPosition = dragStartCursorPosition = NativeMethodsMouse.CursorPosition; 83 | } 84 | } 85 | 86 | private void MouseHook_MouseEvent(MouseRawEventArgs raw_e) 87 | { 88 | if (raw_e.Event == MouseEventType.LBUTTONUP || raw_e.Event == MouseEventType.RBUTTONUP) 89 | { 90 | mouseHook.MouseEvent -= MouseHook_MouseEvent; 91 | mouseHook.Dispose(); 92 | CurrentParent = null; 93 | window = null; 94 | mouseHook = null; 95 | return; 96 | } 97 | else 98 | { 99 | Dispatcher.BeginInvoke((Action) tick, 100 | DispatcherPriority.Normal); 101 | } 102 | 103 | } 104 | 105 | private void tick() 106 | { 107 | currentCursorPosition = NativeMethodsMouse.CursorPosition; 108 | 109 | if (currentCursorPosition != lastTickCursorPosition) 110 | { 111 | lastTickCursorPosition = currentCursorPosition; 112 | 113 | Vector diffReal = new Vector((currentCursorPosition.X - dragStartCursorPosition.X), (currentCursorPosition.Y - dragStartCursorPosition.Y)); 114 | Vector diffDpiIndependend = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice.Transform(diffReal); 115 | 116 | if (window != null) 117 | { 118 | // This renders once after setting Width and another time after setting Height, causing a visual staircase effect while resizing. 119 | //Window.Width = dragStartSize.Width + diffDpiIndependend.X; 120 | //Window.Height = dragStartSize.Height + diffDpiIndependend.Y; 121 | 122 | // As a fix: Use native SetWindowPos() API for smoother resize. The window will automatically redraw itself. 123 | int width = (int)Math.Max(minSize.X, (dragStartWindowSizeReal.X + diffReal.X)); 124 | int height = (int)Math.Max(minSize.Y, (dragStartWindowSizeReal.Y + diffReal.Y)); 125 | NativeMethodsWindow.SetWindowSize(windowHandle, width, height); 126 | 127 | settings.SetWindowSizeWithoutOnSettingChangedEvent(new System.Drawing.Size((int)(dragStartSize.Width + diffDpiIndependend.X), (int)(dragStartSize.Height + diffDpiIndependend.Y))); 128 | } 129 | 130 | if (CurrentParent != null) 131 | { 132 | CurrentParent.Width = Math.Max(minSize.X, dragStartSize.Width + diffDpiIndependend.X); 133 | CurrentParent.Height = Math.Max(minSize.Y, dragStartSize.Height + diffDpiIndependend.Y); 134 | 135 | settings.SetPanelSizeWithoutOnSettingChangedEvent(new System.Drawing.Size((int)(dragStartSize.Width + diffDpiIndependend.X), (int)(dragStartSize.Height + diffDpiIndependend.Y))); 136 | } 137 | } 138 | } 139 | } 140 | public enum ResizeTarget 141 | { 142 | Window, 143 | Parent 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /KeyNStroke/CursorIndicator1.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows; 4 | using System.Windows.Interop; 5 | using System.Windows.Media; 6 | using static KeyNStroke.NativeMethodsMouse; 7 | 8 | namespace KeyNStroke 9 | { 10 | /// 11 | /// Interaktionslogik für CursorIndicator1.xaml 12 | /// 13 | public partial class CursorIndicator1 : Window 14 | { 15 | IMouseRawEventProvider m; 16 | SettingsStore s; 17 | IntPtr windowHandle; 18 | bool isHidden; 19 | bool isDown; 20 | 21 | public CursorIndicator1(IMouseRawEventProvider m, SettingsStore s) 22 | { 23 | InitializeComponent(); 24 | 25 | this.m = m; 26 | this.s = s; 27 | 28 | s.PropertyChanged += settingChanged; 29 | this.isHidden = false; 30 | } 31 | 32 | private void Window_Loaded(object sender, RoutedEventArgs e) 33 | { 34 | m.MouseEvent += m_MouseEvent; 35 | m.CursorEvent += m_CursorEvent; 36 | windowHandle = new WindowInteropHelper(this).Handle; 37 | SetFormStyles(); 38 | } 39 | 40 | void m_MouseEvent(MouseRawEventArgs raw_e) 41 | { 42 | if (raw_e.Action == MouseAction.Move) 43 | { 44 | UpdatePosition(raw_e.Position); 45 | } 46 | else if (raw_e.Action == MouseAction.Down || raw_e.Action == MouseAction.DblClk) 47 | { 48 | isDown = true; 49 | UpdateColor(); 50 | } 51 | else if (raw_e.Action == MouseAction.Up) 52 | { 53 | isDown = false; 54 | UpdateColor(); 55 | } 56 | } 57 | 58 | void m_CursorEvent(bool visible) 59 | { 60 | if (visible) 61 | { 62 | if (isHidden) 63 | { 64 | this.Show(); 65 | isHidden = false; 66 | } 67 | } 68 | else 69 | { 70 | if (!isHidden) 71 | { 72 | this.Hide(); 73 | isHidden = true; 74 | } 75 | } 76 | } 77 | 78 | void SetFormStyles() 79 | { 80 | //this.Opacity = s.CursorIndicatorOpacity; 81 | Log.e("CI", $"WindowHandle={windowHandle}"); 82 | NativeMethodsGWL.ClickThrough(windowHandle); 83 | NativeMethodsGWL.HideFromAltTab(windowHandle); 84 | 85 | UpdateSize(); 86 | UpdateColor(); 87 | UpdatePosition(NativeMethodsMouse.CursorPosition); 88 | } 89 | 90 | void UpdateSize() 91 | { 92 | this.Width = s.CursorIndicatorSize; 93 | this.Height = s.CursorIndicatorSize; 94 | } 95 | 96 | void UpdatePosition(NativeMethodsMouse.POINT cursorPosition) 97 | { 98 | IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST); 99 | uint adpiX = 0, adpiY = 0; 100 | NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY); 101 | Log.e("CI", $"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={cursorPosition.X} cy={cursorPosition.Y}"); 102 | NativeMethodsWindow.SetWindowPosition(windowHandle, 103 | (int)(cursorPosition.X - (this.ActualWidth / 2) * (double)adpiX / 96.0), 104 | (int)(cursorPosition.Y - (this.ActualHeight / 2) * (double)adpiY / 96.0)); 105 | } 106 | 107 | private void settingChanged(object sender, PropertyChangedEventArgs e) 108 | { 109 | this.Dispatcher.BeginInvoke((Action) (() => 110 | { 111 | if (s == null) return; 112 | switch (e.PropertyName) 113 | { 114 | case "EnableCursorIndicator": 115 | break; 116 | case "CursorIndicatorOpacity": 117 | UpdateColor(); 118 | break; 119 | case "CursorIndicatorSize": 120 | UpdateSize(); 121 | break; 122 | case "CursorIndicatorColor": 123 | UpdateColor(); 124 | break; 125 | case "CursorIndicatorEdgeColor": 126 | UpdateColor(); 127 | break; 128 | case "CursorIndicatorEdgeStrokeThickness": 129 | UpdateColor(); 130 | break; 131 | case "CursorIndicatorDrawEdge": 132 | UpdateColor(); 133 | break; 134 | } 135 | })); 136 | } 137 | 138 | private void UpdateColor() 139 | { 140 | Color c; 141 | if (isDown && s.CursorIndicatorFlashOnClick) 142 | { 143 | c = UIHelper.ToMediaColor(s.CursorIndicatorClickColor); 144 | } 145 | else 146 | { 147 | c = UIHelper.ToMediaColor(s.CursorIndicatorColor); 148 | } 149 | circle.Fill = new SolidColorBrush(Color.FromArgb((byte)(255 * (1 - s.CursorIndicatorOpacity)), c.R, c.G, c.B)); 150 | if (s.CursorIndicatorDrawEdge) 151 | { 152 | circle.Stroke = new SolidColorBrush(UIHelper.ToMediaColor(s.CursorIndicatorEdgeColor)); 153 | circle.StrokeThickness = s.CursorIndicatorEdgeStrokeThickness; 154 | } 155 | else 156 | { 157 | circle.Stroke = null; 158 | circle.StrokeThickness = 0; 159 | } 160 | } 161 | 162 | 163 | private void Window_Closed(object sender, EventArgs e) 164 | { 165 | if (m != null) 166 | { 167 | m.MouseEvent -= m_MouseEvent; 168 | m.CursorEvent -= m_CursorEvent; 169 | } 170 | if (s != null) 171 | s.PropertyChanged -= settingChanged; 172 | m = null; 173 | s = null; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Key'n'Stroke 2 | 3 | Displays Keystrokes in an overlay window and visualize mouse clicks. 4 | 5 | 6 | # Features 7 | 8 | - Displays special keys (e.g. volume up: 🔊) and shortcuts 9 | - Visually indicate mouse cursor position 10 | - Visually indicate pressed buttons 11 | - Click Through 12 | - Opacity 13 | - Customizable: Icons, Colors, Font, Size and Position, Text Position and Orientation 14 | - Displays history of pressed keys 15 | - Icon in notification area 16 | - One small file, no installation required 17 | - Works with high-dpi screens 18 | - Works with multi-dpi screen setups 19 | 20 | Requirements: Windows 10 21 | 22 | 23 | # Download 24 | 25 | Download Key-n-Stroke.exe 1.1.0 (most recent, 2022-02-08) 26 | 27 | 28 | There is no installation step. Just keep that exe file to start the application. 29 | 30 | You will once be greeted with a [smart screen warning](https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/Smartscreen1.png). Click "More Info" and "Execute Anyway". It's fine if the issuer name is 'Open Source Developer, Daniel Seemer'. 31 | 32 | # Screenshots 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ## Releases 61 | 62 | You can find all the exe files in the Releases folder 63 | 64 | - v0.0.1 (Text positioning not implemented yet) 65 | - v0.1.1 (Looks fine) 66 | - v0.1.2 (More Whitespace between special chars) 67 | - v0.1.3 (fix bug with Win+? shortcuts) 68 | - v0.2.0 (indicator for cursor position, history timeout, bugfixes, documentation) 69 | - v0.3.0 (indicator for pressed buttons, deadkey fixes, backspace functionality) 70 | - v0.3.1 (new icons, little fixes and internal improvements) 71 | - v1.0.0 72 | - Rebrand as Key'n'Stroke for better discoverability, previously PxKeystrokesForScreencasts 73 | - Switch to .NET 4.8 and WPF 74 | - Nicer icon rendering 75 | - High-Dpi compatability 76 | - Custom Icons 77 | - v1.0.1 78 | - No changes, just testing the update mechanism 79 | - v1.1.0 80 | - New: Draw lines on the screen 81 | - New: Shortcut mode for keystroke window 82 | - New: Standby mode 83 | - New: Hide cursor indicator if cursor is hidden 84 | - New: Draw edge of cursor indicator 85 | - New: Indicate clicks via color change in cursor indicator 86 | - New: Setting to hide welcome window on startup 87 | - Fix: Capture shortcuts 88 | - Fix: Keystroke window startup position overlaps taskbar 89 | - Fix: Crash on modifier+click with button indicators enabled 90 | - Fix: Show shortcut shift+backspace 91 | - v1.2.0 92 | - Fix: Fix display of special characters (e.g / was showing as ?) 93 | 94 | 95 | # Feature Requests 96 | 97 | This app is Open Source. If you miss a feature, write an issue or go for it and send a Pull-Request afterwards! 98 | 99 | 100 | # Donations and Financial 101 | 102 | You can donate via paypal.me/phaiax, send Bitcoins (`1JWER55pheUeJzaUcqaYwP8ZaGe5C16Rp9`) or Stellar Lumens (`phaiax*keybase.io`). You can verify the wallet addresses cryptographically on keybase.io/phaiax. Add the message "pkfs" or "keynstroke" to the payment so I can associate it with this project. After funds for the next six years of developer certificates are raised (which I assume will never happen), I will use any additional funds to buy and eat icecream for myself and anyone I like. 103 | 104 | This app shows a smart screen warning. To fix this, I bought a validated security certificate from Certum. Unfortunately that did not fix it. (And the internet says so). At least it now shows my name as the Author. 105 | 106 | I said that if someone funds at least three years of certification (€ 75.00), I will use that money to buy a new certificate each year and implement a secure autoupdater. It's not that much, but I started anyway :smile: Here is the list of donations for full transparency: (three as of Feb 2022) 107 | 108 | | Date | Amount | From | 109 | |------------|--------|------------------| 110 | | 2023-11-20 | € 20 | S. M. | 111 | | 2023-06-11 | € 9.10 | R. G. R. | 112 | | 2023-12-02 | € 22.22| A. V. N. | 113 | | 2023-07-25 | € 5 | D. N. | 114 | | 2023-05-30 | € 10.26| I. J. | 115 | | 2023-03-14 | € 5 | J. L. | 116 | | 2022-02-02 | € 50 | Mario Pastoor | 117 | | 2021-11-28 | € 16.95| Joshua Taylor | 118 | | 2021-03-05 | € 11.14| Heitor K. M. M. | 119 | | 2020-05-17 | € 5 | Marek S. | 120 | | 2020-08-06 | € 10 | AGlass0fMilk | 121 | 122 | Expenses: Certum Open Source Certificate and cryptographic card reader + Shipping: €119.87 123 | 124 | Total: €164.67 - €119.87 = €44.8 125 | 126 | Thx a lot, its time for a new certificate since the first one is expired :D 127 | 128 | 129 | 130 | # License 131 | 132 | Apache Version 2, refer to the file LICENSE for details 133 | 134 | 135 | # Developed and signed by 136 | 137 | - Daniel Seemer (mail-oscert@invisibletower.de) 138 | -------------------------------------------------------------------------------- /KeyNStroke/SpecialkeysParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Input; 7 | 8 | namespace KeyNStroke 9 | { 10 | class SpecialkeysParser 11 | { 12 | public static string ToString(Key k) 13 | { 14 | switch(k){ 15 | case Key.LeftShift: 16 | case Key.RightShift: 17 | return "⇧"; 18 | case Key.LeftCtrl: 19 | case Key.RightCtrl: 20 | return "Ctrl"; 21 | case Key.LWin: 22 | case Key.RWin: 23 | return "Win"; 24 | case Key.LeftAlt: 25 | case Key.RightAlt: 26 | return "Alt"; 27 | case Key.CapsLock: 28 | return "⇪"; 29 | case Key.LineFeed: 30 | case Key.Return: 31 | return " ⏎"; 32 | case Key.Back: 33 | return " ⌫ "; 34 | case Key.Left: 35 | return " ← "; 36 | case Key.Right: 37 | return " → "; 38 | case Key.Down: 39 | return " ↓ "; 40 | case Key.Up: 41 | return " ↑ "; 42 | case Key.Escape: 43 | return " [Esc] "; 44 | case Key.PrintScreen: 45 | return " [Print] "; 46 | case Key.Pause: 47 | return " [Pause] "; 48 | case Key.Insert: 49 | return " [Insert] "; 50 | case Key.Delete: 51 | return " [Delete] "; 52 | 53 | 54 | case Key.Tab: 55 | return "↹"; 56 | case Key.Space: 57 | return "␣"; 58 | case Key.PageUp: 59 | return " ↖ "; 60 | case Key.PageDown: 61 | return " ↘ "; 62 | case Key.End: 63 | return " ⇲ "; 64 | case Key.Home: 65 | return " ⇱ "; 66 | case Key.Print: 67 | return " ⎙ "; 68 | 69 | case Key.Clear: 70 | case Key.ImeProcessed: 71 | case Key.Attn: 72 | case Key.CrSel: 73 | case Key.ExSel: 74 | case Key.EraseEof: 75 | case Key.Cancel: 76 | case Key.Select: 77 | case Key.Execute: 78 | case Key.Help: 79 | case Key.Apps: 80 | case Key.Pa1: 81 | case Key.Sleep: 82 | return " [" + k.ToString() + "] "; 83 | 84 | 85 | 86 | //case Key.OemSemicolon: // Key.Oem1 // Let ToUnicode handle this character 87 | // return ";"; 88 | //case Key.OemComma: // Let ToUnicode handle this character 89 | // return ","; 90 | //case Key.OemQuestion: // Key.Oem2 // Let ToUnicode handle this character 91 | // return "?"; 92 | //case Key.OemTilde: // Key.Oem3 // Let ToUnicode handle this character 93 | // return "~"; 94 | //case Key.AbntC1: 95 | //case Key.AbntC2: 96 | //case Key.OemOpenBrackets: 97 | //case Key.OemCloseBrackets: 98 | //case Key.OemQuotes: // Key.Oem7 // Let ToUnicode handle this character 99 | // return "\""; 100 | 101 | //case Key.Oem102: 102 | //case Key.OemPipe: // Key.Oem5 // Let ToUnicode handle this character 103 | // return "|"; 104 | // case Key.OemBackslash: // Let ToUnicode handle this character 105 | // return "\\"; 106 | 107 | 108 | //case Key.Multiply: // Let ToUnicode handle this character 109 | // return "*"; 110 | //case Key.Add: // Let ToUnicode handle this character 111 | // return "+"; 112 | case Key.Separator: 113 | return " [Seperator] "; 114 | //case Key.Subtract: 115 | //case Key.OemMinus: // Let ToUnicode handle this character 116 | // return "-"; 117 | //case Key.OemPeriod: 118 | //case Key.Decimal: // Let ToUnicode handle this character 119 | // return "."; 120 | //case Key.Divide: // Let ToUnicode handle this character 121 | // return "/"; 122 | case Key.NumLock: 123 | return " [NumLock] "; 124 | case Key.Scroll: 125 | return " [ScrollLock] "; 126 | 127 | case Key.BrowserBack: 128 | return " [🌐⇦] "; 129 | case Key.BrowserForward: 130 | return " [🌐⇨] "; 131 | case Key.BrowserRefresh: 132 | return " [🌐↻] "; 133 | case Key.BrowserStop: 134 | return " [🌐✋] "; 135 | case Key.BrowserSearch: 136 | return " [🌐🔎] "; 137 | case Key.BrowserFavorites: 138 | return " [🌐★] "; 139 | case Key.BrowserHome: 140 | return " [🌐⌂] "; 141 | 142 | 143 | case Key.VolumeMute: 144 | return " 🔇 "; 145 | case Key.VolumeDown: 146 | return " 🔉⏬ "; 147 | case Key.VolumeUp: 148 | return " 🔊⏫ "; 149 | case Key.MediaNextTrack: 150 | return " ⏭ "; 151 | case Key.MediaPreviousTrack: 152 | return " ⏮ "; 153 | case Key.MediaStop: 154 | return " ◼ "; 155 | case Key.MediaPlayPause: 156 | return " ⏯ "; 157 | case Key.LaunchMail: 158 | return " 📧 "; 159 | case Key.SelectMedia: 160 | return " ♪ "; 161 | case Key.LaunchApplication1: 162 | return " ① "; 163 | case Key.LaunchApplication2: 164 | return " ② "; 165 | 166 | case Key.Play: 167 | return " ▶ "; 168 | case Key.Zoom: 169 | return " [🔎±] "; 170 | 171 | 172 | } 173 | if(Key.F1 <= k && k <= Key.F24) 174 | return " " + k.ToString() + " "; 175 | 176 | throw new NotImplementedException(); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /DEVELOP.md: -------------------------------------------------------------------------------- 1 | # Key'n'Stroke 2 | 3 | (previously PxKeystrokesForScreencasts) 4 | 5 | This is a little documentation about how the source code is organized and works 6 | 7 | ## How to make a new release 8 | 9 | 1. Change Version: 10 | - Twice in AssemblyInfo.cs 11 | - In project settings under "Publish" 12 | 2. Build the App in Release mode 13 | 3. Sign the file using the certum certificate 14 | - cmd.exe: `signtool.exe sign /n Open /t http://time.certum.pl/ /fd sha256 /v Key-n-Stroke.exe` 15 | 4. Verify the signature: 16 | - cmd.exe: `signtool.exe verify /pa Key-n-Stroke.exe` 17 | 4. `./Key-n-Stroke.exe --create-update-manifest` 18 | 5. Change manifest: Update description 19 | 6. `./Key-n-Stroke.exe --sign-update-manifest` 20 | 7. Copy executable into release folder 21 | 8. Commit and push 22 | 7. Upload manifest `scp updateManifest.xml $SSHSERVER:html/key-n-stroke/` 23 | 24 | ## How to prepare the development environment 25 | 26 | - Install Visual Studio 2019 27 | - Download dependencies: 28 | - Clean the folder `packages` in the repository root 29 | - Search a msbuild.exe and run it like this: 30 | - e.g. `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" KeyNStroke.sln /t:Restore /p:RestorePackagesConfig=true /p:Configuration=Debug` 31 | - Then run the application using Visual Studio 32 | 33 | ## Main (Program.cs) 34 | 35 | The main function is found in Program.cs. 36 | 37 | It initializes the classes decribed below. 38 | * new KeyboardHook() 39 | * new KeystrokeParser() 40 | * new SettingsStore() 41 | * new KeystrokeDisplay() 42 | 43 | The keypress information is intercepted in KeyboardHook, passed to KeystrokeParser and then passed to KeystrokeDisplay. 44 | 45 | 46 | ## How key events are intercepted from the system 47 | 48 | In Windows, you can register a callback (also known as hook) for certain process messages like [keyboard][LowLevelKeyboardProc] and mouse events. 49 | 50 | Callbacks are registered using the function [SetWindowsHookEx(event type, callback function, ...)][SetWindowsHookEx]. 51 | This function is not part of PxKS but part of the system library user32.dll. In PxKS we only need to define how each of those system functions looks. This is done in the files NativeMethodsKeyboard.cs, NativeMethodsMouse.cs, NativeMethodsGWL.cs and NativeMethodsSWP.cs. 52 | 53 | Sometimes special data structures and constants are needed for these system functions (for example [KBDLLHOOKSTRUCT][KBDLLHOOKSTRUCT]).They are also defined in these files. 54 | 55 | 56 | ### System calls (KeyboardHook.cs KeyboardRawEvents.cs) 57 | 58 | Now back to the key events. The class that encapsulates the calls to the system functions is KeyboardHook in KeyboardHook.cs. It registers the keyboard event system callback on creation and unregisters it on disposing/deconstructing. The related functions are RegisterKeyboardHook() and UnregisterKeyboardHook(). 59 | 60 | KeyboardHook exposes the C# event KeyEvent that takes methods of delegate type KeyboardRawEventHandler. (via interface IKeyboardRawEventProvider in KeyboardRawEvent.cs). 61 | 62 | The idea is, that you just do this nice pure C# thing 63 | 64 | ``` void hook_KeyEvent(KeyboardRawEventArgs raw_e) 65 | { 66 | // process hook 67 | } 68 | IKeyboardRawEventProvider myKeyboardHook = new KeyboardHook(); 69 | hook.KeyEvent += hook_KeyEvent; 70 | ``` 71 | 72 | ... instead of dealing with the raw system library calls. 73 | 74 | The KeyboardHook class does a little bit more. It executes multiple system calls to find out which modifier keys (shift, ...) are currently pressed and appends this information to raw_e. 75 | 76 | ### Key event processing (KeystrokeParser.cs KeystrokeEvent.cs) 77 | 78 | Next, the KeyboardRawEventArgs raw_e are converted into Keystrokes. This is happening in a similar interface/event pattern. 79 | 80 | The idea is, that the KeystrokeParser gets the RawEvents as input, determines what should be displayed to the user (for example a simple letter or a more complex information like CTRL + A), and forwards the result to the next program part using a C# event. 81 | 82 | Input: The KeystrokeParser registers itself on the KeyEvent of the KeyboardHook in the constructor. 83 | 84 | Output: The KeystrokeParser exposes the C# event KeystrokeEvent that takes methods of delegate type KeystrokeEventHandler. (via interface IKeystrokeEventProvider in KeystrokeEvent.cs). 85 | 86 | During Calculation, the KeystrokeParser uses the static methods in KeyboardLayoutParser and SpecialkeysParser (can be found in the two .cs files) to do some of the conversion. 87 | 88 | The KeyboardLayoutParser wraps some other system library functions that convert raw key information to corresponding letters with respect to the chosen keyboard layout by the user (for example us QWERTY vs german QWERTZ). 89 | 90 | The SpecialkeysParser is simply a big switch statement that converts special function keys on keyboards like 'volume up' and normal keys like 'ESC' and 'tab' to text or unicode symbols like 🔊, which are then displayed in the UI. 91 | 92 | 93 | The output of the KeystrokeParser is used by the KeystrokesDisplay. 94 | 95 | [SetWindowsHookEx]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx "SetWindowsHookEx function" 96 | [LowLevelKeyboardProc]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985%28v=vs.85%29.aspx "LowLevelKeyboardProc callback function" 97 | [TranslateMessage]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955%28v=vs.85%29.aspx "TranslateMessage function" 98 | [KBDLLHOOKSTRUCT]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx "KBDLLHOOKSTRUCT structure" 99 | 100 | ## Displaying Keystrokes (KeystrokesDisplay.cs code (F7)) 101 | 102 | * displays new keys as wished by the information in KeystrokeEventArgs (k_KeystrokeEvent()) 103 | * Checks if resizing mode is activated (CheckForSettingsMode()) 104 | * allows resizing and moving of window 105 | * keeps track of the History of keystrokes (List<TweenLabel> tweenLabels) 106 | 107 | ### TweenLabel (TweenLabel.cs code (F7)) 108 | 109 | The TweenLabel is a C# System.Windows.Forms.Control that displays a line of keystrokes in the UI and is capable of fading in and out and nicely moving around in the window. 110 | 111 | ## Settings 112 | 113 | Are stored in the C# Application.UserAppDataRegistry using the wrapper functions in SettingsStore.cs. 114 | 115 | The Settings are changed in the UI window Settings.cs 116 | 117 | ## NativeMethodsSWP.cs 118 | 119 | A wrapper for calling the system library function SetWindowPos. 120 | The class NativeMethodsSWP provides a method to pin the UI on top of everything. 121 | 122 | ## NativeMethodsGWL.cs 123 | 124 | Some wrappers for calling system library functions. 125 | They provide methods to make the UI through clickable and clickable again. 126 | 127 | ## UrlOpener.cs 128 | 129 | Open a browser. -------------------------------------------------------------------------------- /KeyNStroke/NativeMethodsKeyboard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace KeyNStroke 9 | { 10 | [ComVisibleAttribute(false), 11 | System.Security.SuppressUnmanagedCodeSecurity()] 12 | public class NativeMethodsKeyboard 13 | { 14 | // Structure returned by the hook whenever a key is pressed 15 | [StructLayout(LayoutKind.Sequential)] 16 | public struct KBDLLHOOKSTRUCT 17 | { 18 | public int vkCode; 19 | public int scanCode; 20 | public int flags; 21 | public int time; 22 | public int dwExtraInfo; 23 | } 24 | 25 | public delegate IntPtr HookHandlerDelegate(int nCode, 26 | IntPtr wParam, 27 | ref KBDLLHOOKSTRUCT lParam); 28 | 29 | 30 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 31 | public static extern IntPtr GetModuleHandle(string lpModuleName); 32 | 33 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 34 | public static extern IntPtr SetWindowsHookEx(int idHook, 35 | HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId); 36 | 37 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 38 | [return: MarshalAs(UnmanagedType.Bool)] 39 | public static extern bool UnhookWindowsHookEx(IntPtr hhk); 40 | 41 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 42 | public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 43 | IntPtr wParam, ref KBDLLHOOKSTRUCT lParam); 44 | 45 | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] 46 | public static extern UInt16 GetKeyState(int keyCode); 47 | 48 | 49 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 50 | public static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nSize); 51 | 52 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 53 | public static extern uint MapVirtualKey(uint uCode, uint uMapType); 54 | public const uint MAPVK_VK_TO_CHAR = 2; 55 | 56 | /// 57 | /// The ToAscii function translates the specified virtual-key code and keyboard 58 | /// state to the corresponding character or characters. The function translates the code 59 | /// using the input language and physical keyboard layout identified by the keyboard layout handle. 60 | /// 61 | /// 62 | /// [in] Specifies the virtual-key code to be translated. 63 | /// 64 | /// 65 | /// [in] Specifies the hardware scan code of the key to be translated. 66 | /// The high-order bit of this value is set if the key is up (not pressed). 67 | /// 68 | /// 69 | /// [in] Pointer to a 256-byte array that contains the current keyboard state. 70 | /// Each element (byte) in the array contains the state of one key. 71 | /// If the high-order bit of a byte is set, the key is down (pressed). 72 | /// The low bit, if set, indicates that the key is toggled on. In this function, 73 | /// only the toggle bit of the CAPS LOCK key is relevant. The toggle state 74 | /// of the NUM LOCK and SCROLL LOCK keys is ignored. 75 | /// 76 | /// 77 | /// [out] Pointer to the buffer that receives the translated character or characters. 78 | /// 79 | /// 80 | /// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. 81 | /// 82 | /// 83 | /// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values. 84 | /// Value Meaning 85 | /// 0 The specified virtual key has no translation for the current state of the keyboard. 86 | /// 1 One character was copied to the buffer. 87 | /// 2 Two characters were copied to the buffer. This usually happens when a dead-key character 88 | /// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified 89 | /// virtual key to form a single character. 90 | /// 91 | /// 92 | /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp 93 | /// 94 | [DllImport("user32")] 95 | public static extern int ToAscii( 96 | int uVirtKey, 97 | int uScanCode, 98 | byte[] lpbKeyState, 99 | byte[] lpwTransKey, 100 | int fuState); 101 | 102 | /* int WINAPI ToAscii( 103 | _In_ UINT uVirtKey, 104 | _In_ UINT uScanCode, 105 | _In_opt_ const BYTE *lpKeyState, 106 | _Out_ LPWORD lpChar, 107 | _In_ UINT uFlags 108 | );*/ 109 | [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] 110 | public static extern int ToUnicode( 111 | int uVirtKey, 112 | int uScanCode, 113 | byte[] lpbKeyState, 114 | [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, 115 | int cchBuff, 116 | uint wFlags); 117 | /* 118 | int WINAPI ToUnicode( 119 | _In_ UINT wVirtKey, 120 | _In_ UINT wScanCode, 121 | _In_opt_ const BYTE *lpKeyState, 122 | _Out_ LPWSTR pwszBuff, 123 | _In_ int cchBuff, 124 | _In_ UINT wFlags 125 | );*/ 126 | 127 | 128 | /// 129 | /// The GetKeyboardState function copies the status of the 256 virtual keys to the 130 | /// specified buffer. 131 | /// 132 | /// 133 | /// [in] Pointer to a 256-byte array that contains keyboard key states. 134 | /// 135 | /// 136 | /// If the function succeeds, the return value is nonzero. 137 | /// If the function fails, the return value is zero. To get extended error information, call GetLastError. 138 | /// 139 | /// 140 | /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp 141 | /// 142 | [DllImport("user32")] 143 | public static extern int GetKeyboardState(byte[] pbKeyState); 144 | 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /KeyNStroke/KeystrokeEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Input; 7 | 8 | namespace KeyNStroke 9 | { 10 | public enum KeystrokeType 11 | { 12 | Undefined, 13 | Shortcut, 14 | Text, 15 | Modifiers 16 | } 17 | 18 | public class KeystrokeEventArgs 19 | { 20 | public string TextModeString; // for use in TextMode 21 | public string ShortcutString; // will always contain a shortcut 22 | 23 | public KeyboardRawEventArgs raw; 24 | 25 | public bool Shift { get { return raw.Shift; } set { raw.Shift = value; } } 26 | public bool LShift { get { return raw.LShift; } set { raw.LShift = value; } } 27 | public bool RShift { get { return raw.RShift; } set { raw.RShift = value; } } 28 | public bool Ctrl { get { return raw.Ctrl; } set { raw.Ctrl = value; } } 29 | public bool LCtrl { get { return raw.LCtrl; } set { raw.LCtrl = value; } } 30 | public bool RCtrl { get { return raw.RCtrl; } set { raw.RCtrl = value; } } 31 | public bool Caps { get { return raw.Caps; } set { raw.Caps = value; } } 32 | public bool LWin { get { return raw.LWin; } set { raw.LWin = value; } } 33 | public bool RWin { get { return raw.RWin; } set { raw.RWin = value; } } 34 | public bool Alt { get { return raw.Alt; } set { raw.Alt = value; } } 35 | public bool LAlt { get { return raw.LAlt; } set { raw.LAlt = value; } } 36 | public bool RAlt { get { return raw.RAlt; } set { raw.RAlt = value; } } // Alt Gr 37 | public bool Numlock { get { return raw.Numlock; } set { raw.Numlock = value; } } 38 | public bool Scrollock { get { return raw.Scrollock; } set { raw.Scrollock = value; } } 39 | 40 | public Key Key { get { return raw.Key; } } 41 | public KeyUpDown Method { get { return raw.Method; } } 42 | 43 | public bool Uppercase { get { return raw.Uppercase; } } 44 | public bool OnlyShiftOrCaps { get { return raw.OnlyShiftOrCaps; } } 45 | public bool NoModifiers { get { return raw.NoModifiers; } } 46 | public bool Win { get { return raw.Win; } } 47 | 48 | public bool OrigShift; 49 | public bool OrigLShift; 50 | public bool OrigRShift; 51 | public bool OrigCaps; 52 | 53 | public bool IsAlpha; 54 | public bool IsNumericFromNumpad; 55 | public bool IsNumericFromNumbers; 56 | public bool IsFunctionKey; 57 | public bool IsNoUnicodekey; 58 | public bool ModifierToggledEvent; 59 | 60 | public KeystrokeType StrokeType = KeystrokeType.Undefined; 61 | public bool ShouldBeDisplayed; 62 | public bool RequiresNewLine; 63 | public bool RequiresNewLineAfterwards; 64 | public bool Deletable = false; 65 | 66 | public bool IsNumeric { get { return IsNumericFromNumbers || IsNumericFromNumpad; } } 67 | 68 | public override string ToString() 69 | { 70 | return ToString(true); 71 | } 72 | 73 | public string ToString(bool textMode) 74 | { 75 | if (ShortcutString != null && (StrokeType == KeystrokeType.Shortcut || !textMode)) 76 | { 77 | return ShortcutString; 78 | } 79 | else if (StrokeType == KeystrokeType.Text && textMode) 80 | { 81 | return TextModeString; 82 | } 83 | return "BUG2"; 84 | } 85 | 86 | public string ToString(bool textMode, bool DoubleAmpersand) 87 | { 88 | return DoubleAmpersand ? ToString(textMode).Replace("&", "&&") : ToString(textMode); 89 | } 90 | 91 | public string AsShortcutString() 92 | { 93 | List output = ShortcutModifiersToList(); 94 | if (StrokeType == KeystrokeType.Text) 95 | { 96 | output.Add(TextModeString.ToUpper()); 97 | } 98 | else 99 | { 100 | output.Add(TextModeString); 101 | } 102 | return string.Join(" + ", output); 103 | } 104 | 105 | public List ShortcutModifiersToList() 106 | { 107 | List Modifiers = new List(); 108 | if (OrigShift) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftShift)); 109 | if (Ctrl) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftCtrl)); 110 | if (Alt) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftAlt)); 111 | if (Win) Modifiers.Add(SpecialkeysParser.ToString(Key.LWin)); 112 | return Modifiers; 113 | } 114 | 115 | public string ShortcutIdentifier() 116 | { 117 | if (StrokeType == KeystrokeType.Text) 118 | { 119 | return null; 120 | } 121 | else if(StrokeType == KeystrokeType.Shortcut) 122 | { 123 | 124 | List output = new List(); 125 | 126 | if (this.LCtrl) 127 | { 128 | output.Add("LeftCtrl"); 129 | } 130 | if (this.RCtrl) 131 | { 132 | output.Add("RightCtrl"); 133 | } 134 | // else if (this.Ctrl) 135 | // { 136 | // output.Add("Ctrl"); 137 | // } 138 | 139 | if (this.LWin) 140 | { 141 | output.Add("LeftWin"); 142 | } 143 | if (this.RWin) 144 | { 145 | output.Add("RightWin"); 146 | } 147 | 148 | if (this.LAlt) 149 | { 150 | output.Add("LeftAlt"); 151 | } 152 | if (this.RAlt) 153 | { 154 | output.Add("RightAlt"); 155 | } 156 | // else if (this.Alt) 157 | // { 158 | // output.Add("Alt"); 159 | // } 160 | 161 | if (this.LShift) 162 | { 163 | output.Add("LeftShift"); 164 | } 165 | if (this.RShift) 166 | { 167 | output.Add("RightShift"); 168 | } 169 | // else if (this.Shift) 170 | // { 171 | // output.Add("Shift"); 172 | // } 173 | string trimmed = TextModeString.Trim(); 174 | if (trimmed.Length == 0) // The Space key 175 | { 176 | output.Add(TextModeString); 177 | } 178 | else 179 | { 180 | output.Add(trimmed); 181 | } 182 | return string.Join(" + ", output); 183 | } 184 | return null; 185 | } 186 | 187 | 188 | public KeystrokeEventArgs(KeyboardRawEventArgs e) 189 | { 190 | this.raw = e; 191 | this.OrigShift = e.Shift; 192 | this.OrigCaps = e.Caps; 193 | this.OrigLShift = e.LShift; 194 | this.OrigRShift = e.RShift; 195 | } 196 | } 197 | 198 | public delegate void KeystrokeEventHandler(KeystrokeEventArgs e); 199 | 200 | public interface IKeystrokeEventProvider 201 | { 202 | event KeystrokeEventHandler KeystrokeEvent; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /KeyNStroke/AnnotateLine.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Interop; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Shapes; 16 | using static KeyNStroke.NativeMethodsMouse; 17 | 18 | namespace KeyNStroke 19 | { 20 | /// 21 | /// Interaktionslogik für AnnotateLine.xaml 22 | /// 23 | public partial class AnnotateLine : Window 24 | { 25 | 26 | IMouseRawEventProvider m; 27 | IKeystrokeEventProvider k; 28 | SettingsStore s; 29 | IntPtr windowHandle; 30 | bool isDown; 31 | POINT startCursorPosition = new POINT(0, 0); 32 | POINT endCursorPosition = new POINT(0, 0); 33 | bool nextClickDraws = false; 34 | bool nextClickHides = false; 35 | 36 | public AnnotateLine(IMouseRawEventProvider m, IKeystrokeEventProvider k, SettingsStore s) 37 | { 38 | InitializeComponent(); 39 | 40 | this.m = m; 41 | this.s = s; 42 | this.k = k; 43 | 44 | s.PropertyChanged += settingChanged; 45 | } 46 | 47 | private void Window_Loaded(object sender, RoutedEventArgs e) 48 | { 49 | s.CallPropertyChangedForAllProperties(); 50 | m.MouseEvent += m_MouseEvent; 51 | this.k.KeystrokeEvent += m_KeystrokeEvent; 52 | windowHandle = new WindowInteropHelper(this).Handle; 53 | SetFormStyles(); 54 | } 55 | 56 | #region Shortcut 57 | 58 | public string AnnotateLineShortcut; 59 | 60 | void m_KeystrokeEvent(KeystrokeEventArgs e) 61 | { 62 | if (s == null) return; 63 | string pressed = e.ShortcutIdentifier(); 64 | e.raw.preventDefault = e.raw.preventDefault || CheckForTrigger(pressed); 65 | } 66 | 67 | private bool CheckForTrigger(string pressed) 68 | { 69 | if (AnnotateLineShortcut != null && pressed == AnnotateLineShortcut) 70 | { 71 | nextClickDraws = true; 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | public void SetAnnotateLineShortcut(string shortcut) 78 | { 79 | if (KeystrokeDisplay.ValidateShortcutSetting(shortcut)) 80 | { 81 | AnnotateLineShortcut = shortcut; 82 | } 83 | else 84 | { 85 | AnnotateLineShortcut = s.AnnotateLineShortcutDefault; 86 | } 87 | } 88 | 89 | #endregion 90 | 91 | private void m_MouseEvent(MouseRawEventArgs raw_e) 92 | { 93 | if (s == null) return; 94 | if (!isDown && nextClickHides && raw_e.Action == MouseAction.Down) 95 | { 96 | raw_e.preventDefault = true; 97 | nextClickHides = false; 98 | this.Hide(); 99 | } 100 | 101 | if (isDown && raw_e.Action == MouseAction.Move) 102 | { 103 | endCursorPosition = raw_e.Position; 104 | UpdatePositionAndSize(); 105 | } 106 | else if (!isDown && raw_e.Action == MouseAction.Down && nextClickDraws) 107 | { 108 | isDown = true; 109 | raw_e.preventDefault = true; 110 | nextClickDraws = false; 111 | startCursorPosition = raw_e.Position; 112 | endCursorPosition = raw_e.Position; 113 | } 114 | else if (isDown && raw_e.Action == MouseAction.Up) 115 | { 116 | isDown = false; 117 | nextClickHides = true; 118 | } 119 | } 120 | 121 | private void settingChanged(object sender, PropertyChangedEventArgs e) 122 | { 123 | this.Dispatcher.BeginInvoke((Action)(() => 124 | { 125 | if (s == null) return; 126 | switch (e.PropertyName) 127 | { 128 | case "AnnotateLineShortcutTrigger": 129 | nextClickDraws = true; 130 | break; 131 | case "AnnotateLineColor": 132 | UpdateColor(); 133 | break; 134 | case "AnnotateLineShortcut": 135 | SetAnnotateLineShortcut(s.AnnotateLineShortcut); 136 | break; 137 | } 138 | })); 139 | } 140 | 141 | void SetFormStyles() 142 | { 143 | Log.e("CI", $"WindowHandle={windowHandle}"); 144 | NativeMethodsGWL.ClickThrough(windowHandle); 145 | NativeMethodsGWL.HideFromAltTab(windowHandle); 146 | 147 | UpdatePositionAndSize(); 148 | } 149 | 150 | void UpdateColor() 151 | { 152 | line.Fill = new SolidColorBrush(UIHelper.ToMediaColor(s.AnnotateLineColor)); 153 | } 154 | 155 | void UpdatePositionAndSize() 156 | { 157 | if (isDown) 158 | { 159 | this.Show(); 160 | Int32 xmin = Math.Min(startCursorPosition.X, endCursorPosition.X); 161 | Int32 ymin = Math.Min(startCursorPosition.Y, endCursorPosition.Y); 162 | Int32 h = Math.Abs(startCursorPosition.Y - endCursorPosition.Y); 163 | Int32 w = Math.Abs(startCursorPosition.X - endCursorPosition.X); 164 | bool horizontal = w >= h; 165 | 166 | IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(startCursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST); 167 | uint adpiX = 0, adpiY = 0; 168 | NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY); 169 | Log.e("AL", $"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={startCursorPosition.X} cy={startCursorPosition.Y}"); 170 | 171 | 172 | if (horizontal) 173 | { 174 | this.Width = w / (double)adpiX * 96.0; 175 | this.Height = 4; 176 | NativeMethodsWindow.SetWindowPosition(windowHandle, (int)xmin, (int)startCursorPosition.Y - 2); 177 | } 178 | else 179 | { 180 | this.Width = 4; 181 | this.Height = h / (double)adpiY * 96.0; 182 | NativeMethodsWindow.SetWindowPosition(windowHandle, (int)startCursorPosition.X - 2, ymin); 183 | } 184 | 185 | } 186 | else 187 | { 188 | this.Hide(); 189 | } 190 | 191 | } 192 | 193 | private void Window_Closed(object sender, EventArgs e) 194 | { 195 | if (m != null) 196 | { 197 | m.MouseEvent -= m_MouseEvent; 198 | } 199 | if (k != null) 200 | { 201 | k.KeystrokeEvent -= m_KeystrokeEvent; 202 | } 203 | if (s != null) 204 | { 205 | s.PropertyChanged -= settingChanged; 206 | } 207 | m = null; 208 | s = null; 209 | k = null; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /KeyNStroke/NativeMethodsWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace KeyNStroke 9 | { 10 | public class NativeMethodsWindow 11 | { 12 | public const UInt32 S_OK = 0x00000000; 13 | 14 | /// 15 | /// Sets the Topmost property for the window Handle 16 | /// 17 | /// The Forms myform.Handle 18 | public static void SetWindowTopMost(IntPtr Handle) { 19 | SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 20 | } 21 | 22 | /// 23 | /// Sets the Position property for the window Handle 24 | /// 25 | /// The Forms myform.Handle 26 | public static void SetWindowPosition(IntPtr Handle, int x, int y) 27 | { 28 | SetWindowPos(Handle, HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE); 29 | } 30 | 31 | /// 32 | /// Sets the Size property for the window Handle 33 | /// 34 | /// The Forms myform.Handle 35 | public static void SetWindowSize(IntPtr Handle, int width, int height) 36 | { 37 | SetWindowPos(Handle, HWND_TOPMOST, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER); 38 | } 39 | 40 | public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); 41 | public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); 42 | public static readonly IntPtr HWND_TOP = new IntPtr(0); 43 | public static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 44 | public const UInt32 SWP_NOSIZE = 0x0001; 45 | public const UInt32 SWP_NOMOVE = 0x0002; 46 | public const UInt32 SWP_NOACTIVATE = 0x0010; 47 | public const UInt32 SWP_NOOWNERZORDER = 0x0200; 48 | 49 | [DllImport("user32.dll")] 50 | [return: MarshalAs(UnmanagedType.Bool)] 51 | public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); 52 | 53 | public enum MonitorOptions : uint 54 | { 55 | MONITOR_DEFAULTTONULL = 0x00000000, 56 | MONITOR_DEFAULTTOPRIMARY = 0x00000001, 57 | MONITOR_DEFAULTTONEAREST = 0x00000002 58 | } 59 | 60 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 61 | public static extern IntPtr MonitorFromPoint(NativeMethodsMouse.POINT pt, MonitorOptions dwFlags); 62 | 63 | public enum DpiType : uint 64 | { 65 | MDT_EFFECTIVE_DPI = 0, 66 | MDT_ANGULAR_DPI = 1, 67 | MDT_RAW_DPI = 2, 68 | MDT_DEFAULT = 3 69 | } 70 | 71 | [DllImport("Shcore.dll", CharSet = CharSet.Auto, SetLastError = true)] 72 | public static extern uint GetDpiForMonitor(IntPtr hmonitor, DpiType dpiType, ref uint dpiX, ref uint dpiY); 73 | 74 | public enum ProcessDpiAwareness : uint 75 | { 76 | PROCESS_DPI_UNAWARE = 0, 77 | PROCESS_SYSTEM_DPI_AWARE = 1, 78 | PROCESS_PER_MONITOR_DPI_AWARE = 2 79 | }; 80 | 81 | [DllImport("Shcore.dll", CharSet = CharSet.Auto, SetLastError = true)] 82 | public static extern uint GetProcessDpiAwareness(IntPtr hprocess, ref ProcessDpiAwareness value); 83 | 84 | /*#define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1) 85 | #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2) 86 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3) 87 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) 88 | #define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)*/ 89 | 90 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 91 | public static extern IntPtr GetThreadDpiAwarenessContext(); 92 | 93 | public enum DpiAwareness : uint 94 | { 95 | DPI_AWARENESS_INVALID, 96 | DPI_AWARENESS_UNAWARE, 97 | DPI_AWARENESS_SYSTEM_AWARE, 98 | DPI_AWARENESS_PER_MONITOR_AWARE 99 | }; 100 | 101 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 102 | public static extern DpiAwareness GetAwarenessFromDpiAwarenessContext(IntPtr hDpiAwarenessContext); 103 | 104 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 105 | public static extern uint GetDpiFromDpiAwarenessContext(IntPtr hDpiAwarenessContext); 106 | 107 | 108 | 109 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 110 | [return: MarshalAs(UnmanagedType.Bool)] 111 | static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData); 112 | 113 | 114 | delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData); 115 | 116 | [StructLayout(LayoutKind.Sequential)] 117 | public struct Rect 118 | { 119 | public int left; 120 | public int top; 121 | public int right; 122 | public int bottom; 123 | } 124 | 125 | static public List GetAllUsedDpis() 126 | { 127 | List dpis = new List(); 128 | 129 | EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, 130 | delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData) 131 | { 132 | uint adpiX = 0, adpiY = 0; 133 | uint result = GetDpiForMonitor(hMonitor, DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY); 134 | if (result == S_OK) 135 | { 136 | dpis.Add(adpiX); 137 | } 138 | return true; // continue enumeration 139 | }, IntPtr.Zero); 140 | return dpis; 141 | } 142 | 143 | static public void PrintDpiAwarenessInfo() 144 | { 145 | NativeMethodsMouse.POINT cursorPosition = new NativeMethodsMouse.POINT(0, 0); 146 | NativeMethodsMouse.GetCursorPos(ref cursorPosition); 147 | IntPtr monitor = MonitorFromPoint(cursorPosition, MonitorOptions.MONITOR_DEFAULTTONEAREST); 148 | 149 | // Angular DPI seems to work best 150 | uint edpiX = 0, edpiY = 0; 151 | NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref edpiX, ref edpiY); 152 | uint adpiX = 0, adpiY = 0; 153 | NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY); 154 | uint rdpiX = 0, rdpiY = 0; 155 | NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_RAW_DPI, ref rdpiX, ref rdpiY); 156 | 157 | 158 | ProcessDpiAwareness dpiawareness = 0; 159 | GetProcessDpiAwareness(IntPtr.Zero, ref dpiawareness); 160 | 161 | IntPtr hContext = GetThreadDpiAwarenessContext(); 162 | DpiAwareness threaddpiawareness = GetAwarenessFromDpiAwarenessContext(hContext); 163 | uint threadDpi = GetDpiFromDpiAwarenessContext(hContext); 164 | 165 | Log.e("DPI", $"DPI: {edpiX},{edpiY}/{adpiX},{adpiY}/{rdpiX},{rdpiY}, Monitor: {(int)monitor}, Awareness: {dpiawareness}/{threaddpiawareness}/{threadDpi}"); 166 | 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /KeyNStroke/LabeledSlider.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | 18 | namespace KeyNStroke 19 | { 20 | 21 | /// 22 | /// Interaktionslogik für LabeledSlider.xaml 23 | /// 24 | public partial class LabeledSlider : UserControl 25 | { 26 | 27 | private bool interalValueUpdate = false; 28 | 29 | public LabeledSlider() 30 | { 31 | InitializeComponent(); 32 | Log.e("BIN", "Set Data context in labeled slider"); 33 | layout_root.DataContext = this; 34 | 35 | var dpd = DependencyPropertyDescriptor.FromProperty(ValueProperty, typeof(LabeledSlider)); 36 | dpd.AddValueChanged(this, (object sender, EventArgs e) => { 37 | Log.e("SLIDER", $"{Title}: Value={Value}"); 38 | ValueToSlider(); 39 | }); 40 | 41 | var dpd1 = DependencyPropertyDescriptor.FromProperty(MinimumProperty, typeof(LabeledSlider)); 42 | dpd1.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); }); 43 | 44 | var dpd2 = DependencyPropertyDescriptor.FromProperty(MaximumProperty, typeof(LabeledSlider)); 45 | dpd2.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); }); 46 | 47 | var dpd3 = DependencyPropertyDescriptor.FromProperty(HalfWayProperty, typeof(LabeledSlider)); 48 | dpd3.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); }); 49 | 50 | var dpd4 = DependencyPropertyDescriptor.FromProperty(LogarithmicProperty, typeof(LabeledSlider)); 51 | dpd4.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); }); 52 | 53 | } 54 | 55 | 56 | [Browsable(true)] 57 | public double HalfWay 58 | { 59 | get { return (double)GetValue(HalfWayProperty); } 60 | set { SetValue(HalfWayProperty, value); } 61 | } 62 | public static readonly DependencyProperty HalfWayProperty = 63 | DependencyProperty.Register( 64 | "HalfWay", typeof(double), 65 | typeof(LabeledSlider) 66 | ); 67 | 68 | [Browsable(true)] 69 | public bool Logarithmic 70 | { 71 | get { return (bool)GetValue(LogarithmicProperty); } 72 | set { SetValue(LogarithmicProperty, value); } 73 | } 74 | public static readonly DependencyProperty LogarithmicProperty = 75 | DependencyProperty.Register( 76 | "Logarithmic", typeof(bool), 77 | typeof(LabeledSlider) 78 | ); 79 | 80 | [Browsable(true)] 81 | public String Title 82 | { 83 | get { return (String) GetValue(TitleProperty); } 84 | set { SetValue(TitleProperty, value); } 85 | } 86 | public static readonly DependencyProperty TitleProperty = 87 | DependencyProperty.Register( 88 | "Title", typeof(String), 89 | typeof(LabeledSlider) 90 | ); 91 | 92 | [Browsable(true)] 93 | public double Minimum 94 | { 95 | get { return (double) GetValue(MinimumProperty); } 96 | set { SetValue(MinimumProperty, value); } 97 | } 98 | public static readonly DependencyProperty MinimumProperty = 99 | DependencyProperty.Register("Minimum", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0)); 100 | 101 | 102 | [Browsable(true)] 103 | public double Maximum 104 | { 105 | get { return (double) GetValue(MaximumProperty); } 106 | set { SetValue(MaximumProperty, value); } 107 | } 108 | public static readonly DependencyProperty MaximumProperty = 109 | DependencyProperty.Register("Maximum", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0)); 110 | 111 | 112 | [Browsable(true)] 113 | public double Value 114 | { 115 | get { return (double) GetValue(ValueProperty); } 116 | set { SetValue(ValueProperty, value); } 117 | } 118 | public static readonly DependencyProperty ValueProperty = 119 | DependencyProperty.Register("Value", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0)); 120 | 121 | 122 | 123 | 124 | private double pq(double p, double q) { 125 | double r1 = -(p / 2); 126 | double r2 = Math.Sqrt(Math.Pow(p / 2, 2) - q); 127 | return r1 + r2; 128 | } 129 | 130 | private double a; 131 | private double b; 132 | private double c; 133 | 134 | double m; 135 | 136 | private void recalcParams() 137 | { 138 | double ymin = Minimum; 139 | double ymax = Maximum; 140 | double ysper = Math.Min(1.0, Math.Max(0.0, HalfWay)); 141 | double x1 = slider.Maximum; 142 | 143 | if (ymax == ymin) 144 | { 145 | ymax = 10 + ymin; 146 | } 147 | 148 | double ysmin = ymin * 1.01; 149 | double ysmax = ((ymin + ymax) / 2) * 0.99; 150 | double ys = ysmin + ysper * (ysmax - ysmin); 151 | double d = (ymin - ymax) / (ys - ymin); 152 | 153 | double v1 = pq(d, -1 - d); 154 | c = 2 * Math.Log(v1) / x1; 155 | b = (ys - ymin) / (Math.Exp(c * x1 / 2) - 1); 156 | a = ymin - b; 157 | 158 | // linear 159 | m = (ymax - ymin) / x1; 160 | Log.e("SLIDER", $"{Title}: ymax={ymax} ymin={ymin} x1={x1} m={m}"); 161 | if (m == 0) 162 | { 163 | m = 0.1; 164 | } 165 | } 166 | 167 | private void ValueToSlider() 168 | { 169 | if (!interalValueUpdate) 170 | { 171 | interalValueUpdate = true; 172 | double y = Math.Min(Maximum, Math.Max(Minimum, Value)); 173 | if (Logarithmic) 174 | { 175 | slider.Value = Math.Log((y - a) / b) / c; 176 | } 177 | else 178 | { 179 | slider.Value = (y - Minimum) / m; 180 | } 181 | interalValueUpdate = false; 182 | } 183 | } 184 | 185 | private void SliderToValue() 186 | { 187 | if (!interalValueUpdate) 188 | { 189 | interalValueUpdate = true; 190 | if (Logarithmic) 191 | { 192 | Value = a + b * Math.Exp(c * slider.Value); 193 | } 194 | else 195 | { 196 | Value = Minimum + m * slider.Value; 197 | Log.e("SLIDER", $"{Title}: {Value} = {Minimum} {m} * {slider.Value}"); 198 | } 199 | interalValueUpdate = false; 200 | } 201 | } 202 | 203 | public event RoutedPropertyChangedEventHandler ValueChanged; 204 | 205 | private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 206 | { 207 | SliderToValue(); 208 | 209 | RoutedPropertyChangedEventArgs eint = null; 210 | if (e != null) 211 | { 212 | eint = new RoutedPropertyChangedEventArgs(e.OldValue, e.NewValue, e.RoutedEvent); 213 | } 214 | if (ValueChanged != null) 215 | { 216 | ValueChanged.Invoke(sender, eint); 217 | } 218 | } 219 | 220 | 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /KeyNStroke/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of (.NET Core) runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 23 | 24 | 25 | 26 | 27 | A list of (.NET Core) runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 28 | 29 | 30 | 31 | 32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 38 | 39 | 40 | 41 | 42 | The order of preloaded assemblies, delimited with line breaks. 43 | 44 | 45 | 46 | 47 | 48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 49 | 50 | 51 | 52 | 53 | Controls if .pdbs for reference assemblies are also embedded. 54 | 55 | 56 | 57 | 58 | Controls if (.NET Core) runtime assemblies are also embedded. 59 | 60 | 61 | 62 | 63 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 64 | 65 | 66 | 67 | 68 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 69 | 70 | 71 | 72 | 73 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 74 | 75 | 76 | 77 | 78 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 79 | 80 | 81 | 82 | 83 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 84 | 85 | 86 | 87 | 88 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 89 | 90 | 91 | 92 | 93 | A list of (.NET Core) runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 94 | 95 | 96 | 97 | 98 | A list of (.NET Core) runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. 99 | 100 | 101 | 102 | 103 | A list of unmanaged 32 bit assembly names to include, delimited with |. 104 | 105 | 106 | 107 | 108 | A list of unmanaged 64 bit assembly names to include, delimited with |. 109 | 110 | 111 | 112 | 113 | The order of preloaded assemblies, delimited with |. 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 122 | 123 | 124 | 125 | 126 | A comma-separated list of error codes that can be safely ignored in assembly verification. 127 | 128 | 129 | 130 | 131 | 'false' to turn off automatic generation of the XML Schema file. 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /KeyNStroke/Updater/Admininstration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Security.Cryptography; 5 | using System.Xml; 6 | 7 | namespace KeyNStroke.Updater 8 | { 9 | class Admininstration 10 | { 11 | // Files for key creation and testing (all in current working directory) 12 | const string PATH_PUB_KEY = "updateKey.pub.xml"; 13 | const string PATH_PRIV_KEY = "updateKey.priv.xml"; 14 | const string PATH_UPDATE_MANIFEST = "updateManifest.xml"; 15 | 16 | /// 17 | /// Checks the command line and executes administrative functions if the parameter is given. 18 | /// Returns true if the program should exit. 19 | /// 20 | /// 21 | static public bool HandleArgs(string[] args) 22 | { 23 | if (args.Length > 0) 24 | { 25 | if (args[0] == "--create-signing-keypair") 26 | { 27 | CreateSigningKeyPair(); 28 | return true; 29 | } 30 | if (args[0] == "--create-update-manifest") 31 | { 32 | CreateUpdateManifest(); 33 | return true; 34 | } 35 | if (args[0] == "--sign-update-manifest") 36 | { 37 | SignUpdateManifest(); 38 | return true; 39 | } 40 | if (args[0] == "--test-update") 41 | { 42 | DownloadAndVerifyManifestAndUpdate(); 43 | return true; 44 | } 45 | if (args[0] == "--test-signing") 46 | { 47 | TestSigning(); 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | 55 | /// 56 | /// Creates a new RSA key and saves it next to the executable. 57 | /// Put the public key into the Resources folder and bake it into the executable. 58 | /// Store the private key somewhere and never give it away. 59 | /// 60 | static void CreateSigningKeyPair() 61 | { 62 | 63 | RSA rsaKey = RSA.Create(); 64 | string pub = rsaKey.ToXmlString(false); 65 | string priv = rsaKey.ToXmlString(true); 66 | 67 | using (StreamWriter outputFile = new StreamWriter(PATH_PUB_KEY)) 68 | { 69 | outputFile.Write(pub); 70 | } 71 | 72 | using (StreamWriter outputFile = new StreamWriter(PATH_PRIV_KEY)) 73 | { 74 | outputFile.Write(priv); 75 | } 76 | } 77 | 78 | /// 79 | /// Load the private key from the current directory. 80 | /// 81 | /// The private key. 82 | static RSA GetPrivateKey() 83 | { 84 | RSA priv = RSA.Create(); 85 | using (var sr = new StreamReader(PATH_PRIV_KEY)) 86 | { 87 | priv.FromXmlString(sr.ReadToEnd()); 88 | } 89 | return priv; 90 | } 91 | 92 | /// 93 | /// Verifies that the embedded public key can be used to verify documents 94 | /// signed with the private key found in the working directory. 95 | /// The test result is written to the Colsole. 96 | /// 97 | static void TestSigning() 98 | { 99 | String dirOfExecutable = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); 100 | String tmpDocumentPath = Path.Combine(dirOfExecutable, "test.xml"); 101 | 102 | // Sign a test document with the private key and serialize the document to the disk 103 | { 104 | RSA priv = GetPrivateKey(); 105 | string xmlmessage = @" 106 | 107 | 108 | 19834209 109 | 02/02/2002 110 | 111 | "; 112 | XmlDocument xmlDoc = new XmlDocument 113 | { 114 | PreserveWhitespace = true 115 | }; 116 | xmlDoc.LoadXml(xmlmessage); 117 | Utils.SignXml(xmlDoc, priv); 118 | // xmlDoc.GetElementsByTagName("number")[0].InnerText = "123123"; 119 | // xmlDoc.GetElementsByTagName("root")[0].AppendChild(xmlDoc.CreateNode("element", "test", "")); 120 | xmlDoc.Save(tmpDocumentPath); 121 | } 122 | 123 | // Load the test document from the disk and verify the signature with the embedded public key 124 | { 125 | XmlDocument xmlDoc = new XmlDocument 126 | { 127 | PreserveWhitespace = true 128 | }; 129 | xmlDoc.Load(tmpDocumentPath); 130 | try 131 | { 132 | Utils.VerifyXml(xmlDoc, Utils.GetEmbeddedPubKey()); 133 | Console.WriteLine("The XML signature is valid."); 134 | Console.WriteLine(xmlDoc.OuterXml); 135 | } 136 | catch (Exception e) 137 | { 138 | Console.WriteLine("The XML signature is not valid."); 139 | Console.WriteLine(e.Message); 140 | Console.WriteLine(xmlDoc.OuterXml); 141 | } 142 | } 143 | } 144 | 145 | /// 146 | /// Creates a manifest that can be used to update to the current executed assembly. 147 | /// The manifest is saved to the current working directory. 148 | /// The manifest is not signed so the user can set the info etc. 149 | /// 150 | static void CreateUpdateManifest() 151 | { 152 | string executable = System.Reflection.Assembly.GetEntryAssembly().Location; 153 | string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3); 154 | string hash = Utils.SHA256OfFile(executable); 155 | 156 | string manifest = $@" 157 | 158 | {version} 159 | {DateTime.UtcNow:s} 160 | New release with bug fixes and new features 161 | https://github.com/Phaiax/Key-n-Stroke/raw/master/Releases/{version}/{Path.GetFileName(executable)} 162 | {hash} 163 | 164 | "; 165 | 166 | XmlDocument xmlDoc = new XmlDocument 167 | { 168 | PreserveWhitespace = true 169 | }; 170 | xmlDoc.LoadXml(manifest); 171 | xmlDoc.Save(PATH_UPDATE_MANIFEST); 172 | } 173 | 174 | /// 175 | /// Signs the manifest in the current working directory. 176 | /// 177 | static void SignUpdateManifest() 178 | { 179 | XmlDocument xmlDoc = new XmlDocument 180 | { 181 | PreserveWhitespace = true 182 | }; 183 | xmlDoc.Load(PATH_UPDATE_MANIFEST); 184 | Utils.SignXml(xmlDoc, GetPrivateKey()); 185 | xmlDoc.Save(PATH_UPDATE_MANIFEST); 186 | Console.WriteLine($"scp {PATH_UPDATE_MANIFEST} $SSHSERVER:html/key-n-stroke/"); 187 | 188 | string executable = System.Reflection.Assembly.GetEntryAssembly().Location; 189 | Console.WriteLine($"scp /{executable.Replace("\\", "/").Replace(":", "")} $SSHSERVER:html/key-n-stroke/"); 190 | } 191 | 192 | /// 193 | /// Download and verify the manifest and the executable. 194 | /// 195 | static void DownloadAndVerifyManifestAndUpdate() 196 | { 197 | try 198 | { 199 | Console.WriteLine("Downloading manifest ..."); 200 | 201 | XmlDocument manifest = Utils.DownloadAndVerifyManifest(); 202 | Console.WriteLine("Downloading update ..."); 203 | byte[] data = Utils.DownloadExecutableAndVerifyHash(manifest, (e) => 204 | { 205 | Console.Write($"\r {e}%"); 206 | }, new TimeSpan(0, 0, 10)); 207 | Console.WriteLine(); 208 | Console.WriteLine($"Manifest and update ({data.Length} bytes) are ok."); 209 | } 210 | catch (Exception e) 211 | { 212 | Console.WriteLine("ERROR:"); 213 | Console.WriteLine(e.ToString()); 214 | } 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /KeyNStroke/KeyboardLayoutParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using KeyNStroke; 7 | 8 | namespace KeyNStroke 9 | { 10 | public class KeyboardLayoutParser 11 | { 12 | public static string Parse(KeyboardRawEventArgs e) 13 | { 14 | StringBuilder sb = new StringBuilder(128); 15 | int lParam = 0; 16 | // Bits in lParam 17 | // 16-23 Scan code. 18 | // 24 Extended-key flag. Distinguishes some keys on an enhanced keyboard. 19 | // 25 "Do not care" bit. The application calling this function sets this 20 | // bit to indicate that the function should not distinguish between left 21 | // and right CTRL and SHIFT keys, for example. 22 | 23 | lParam = e.Kbdllhookstruct.scanCode << 16; 24 | 25 | int result = NativeMethodsKeyboard.GetKeyNameText(lParam, sb, 128); 26 | return sb.ToString(); 27 | } 28 | 29 | public static string ParseViaMapKeycode(KeyboardRawEventArgs e) 30 | { 31 | uint r = NativeMethodsKeyboard.MapVirtualKey((uint)e.vkCode, 32 | NativeMethodsKeyboard.MAPVK_VK_TO_CHAR); 33 | return ((char)r).ToString(); 34 | } 35 | 36 | 37 | public static string ParseViaToAscii(KeyboardRawEventArgs e) 38 | { 39 | byte[] inBuffer = new byte[2]; 40 | int buffertype = NativeMethodsKeyboard.ToAscii(e.vkCode, 41 | e.Kbdllhookstruct.scanCode, 42 | e.keyState, 43 | inBuffer, 44 | e.Alt ? 1 : 0); 45 | 46 | if (buffertype < 0) // deadkey 47 | { 48 | 49 | } 50 | else if (buffertype == 1) // one char in inBuffer[0] 51 | { 52 | char key = (char)inBuffer[0]; 53 | return key.ToString(); 54 | } 55 | else if (buffertype == 2) // two chars in inBuffer 56 | { 57 | char key = (char)inBuffer[0]; 58 | char key2 = (char)inBuffer[1]; 59 | return key.ToString() + key2.ToString(); 60 | } 61 | else if (buffertype == 0) 62 | { 63 | // no translation 64 | } 65 | return ""; 66 | } 67 | 68 | 69 | public static string ParseViaToUnicode(KeyboardRawEventArgs e) 70 | { 71 | StringBuilder inBuffer = new StringBuilder(128); 72 | int buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode, 73 | e.Kbdllhookstruct.scanCode, 74 | e.keyState, 75 | inBuffer, 76 | 128, 77 | 0); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */ 78 | Log.e("KP", 79 | String.Format(" ParseViaToUnicode(): First call to ToUnicode: returned={0} translated='{1}' alt={2} ctrl={3} vk={4}", buffertype, 80 | inBuffer.ToString(), e.Alt, e.Ctrl, e.vkCode)); 81 | string keystate = ""; 82 | for (int i = 0; i < e.keyState.Length; i++ ) 83 | { 84 | if(e.keyState[i] != 0) 85 | { 86 | keystate += " " + ((WindowsVirtualKey) i).ToString() + ":" + e.keyState[i]; 87 | 88 | } 89 | } 90 | 91 | Log.e("KP", " ParseViaToUnicode(): Key state: " + keystate); 92 | 93 | // call ToUnicode again, otherwise it will destoy the dead key for the rest of the system 94 | int buffertype2 = NativeMethodsKeyboard.ToUnicode(e.vkCode, 95 | e.Kbdllhookstruct.scanCode, 96 | e.keyState, 97 | inBuffer, 98 | 128, 99 | 0); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */ 100 | 101 | Log.e("KP", 102 | String.Format(" ParseViaToUnicode(): Secnd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype2, 103 | inBuffer.ToString(), e.Alt, e.vkCode)); 104 | 105 | if (buffertype < 0) // deadkey 106 | { 107 | // return DEADKEY, so the next key can try to assemble the deadkey 108 | //return "DEADKEY"; 109 | return buffertype2 >= 1 ? inBuffer.ToString(0, 1) : ""; 110 | } 111 | else if(buffertype2 < 0) // type two dead keys in a row 112 | { 113 | Log.e("KP", " ParseViaToUnicode(): TwoDeadKeysInARow " + buffertype2.ToString() + " & deadkey"); 114 | return buffertype >= 1 ? inBuffer.ToString(0, 1) : ""; 115 | } 116 | else if (buffertype2 >= 1) // buffertype chars in inBuffer[0..buffertype] 117 | { 118 | string out_ = inBuffer.ToString(0, buffertype2); 119 | if (out_ == "\b") // Backspace is no text 120 | { 121 | return ""; 122 | } 123 | return out_; 124 | } 125 | else if (buffertype2 == 0) 126 | { 127 | // no translation 128 | } 129 | return ""; 130 | } 131 | 132 | public static string ProcessDeadkeyWithNextKey(KeyboardRawEventArgs dead, KeyboardRawEventArgs e) 133 | { 134 | Log.e("KP", " ProcessDeadkeyWithNextKey()"); 135 | StringBuilder inBuffer = new StringBuilder(128); 136 | int buffertype = NativeMethodsKeyboard.ToUnicode(dead.vkCode, 137 | dead.Kbdllhookstruct.scanCode, 138 | dead.keyState, 139 | inBuffer, 140 | 128, 141 | 4); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */ 142 | 143 | Log.e("KP", 144 | String.Format(" ProcessDeadkeyWithNextKey(): First call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype, 145 | inBuffer.ToString(), e.Alt, e.vkCode)); 146 | buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode, 147 | e.Kbdllhookstruct.scanCode, 148 | e.keyState, 149 | inBuffer, 150 | 128, 151 | 4); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */ 152 | Log.e("KP", 153 | String.Format(" ProcessDeadkeyWithNextKey(): Sednd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype, 154 | inBuffer.ToString(), e.Alt, e.vkCode)); 155 | 156 | if (buffertype >= 1) // buffertype chars in inBuffer[0..buffertype] 157 | { 158 | return inBuffer.ToString(0, buffertype); 159 | } 160 | else if (buffertype == 0) 161 | { 162 | // no translation 163 | } 164 | return ""; 165 | } 166 | 167 | /* 168 | int convertVirtualKeyToWChar(int virtualKey, PWCHAR outputChar, PWCHAR deadChar) 169 | { 170 | int i = 0; 171 | short state = 0; 172 | int capsLock; 173 | int shift = -1; 174 | int mod = 0; 175 | int charCount = 0; 176 | WCHAR baseChar; 177 | WCHAR diacritic; 178 | *outputChar = 0; 179 | capsLock = (GetKeyState(VK_CAPITAL) & 0x1); 180 | do 181 | { 182 | state = GetAsyncKeyState(pgCharModifiers->pVkToBit[i].Vk); 183 | if (pgCharModifiers->pVkToBit[i].Vk == VK_SHIFT) 184 | shift = i + 1; // Get modification number for Shift key 185 | if (state & ~SHRT_MAX) 186 | { 187 | if (mod == 0) 188 | mod = i + 1; 189 | else 190 | mod = 0; // Two modifiers at the same time! 191 | } 192 | i++; 193 | } 194 | while (pgCharModifiers->pVkToBit[i].Vk != 0); 195 | 196 | SEARCH_VK_IN_CONVERSION_TABLE(1) 197 | SEARCH_VK_IN_CONVERSION_TABLE(2) 198 | SEARCH_VK_IN_CONVERSION_TABLE(3) 199 | SEARCH_VK_IN_CONVERSION_TABLE(4) 200 | SEARCH_VK_IN_CONVERSION_TABLE(5) 201 | SEARCH_VK_IN_CONVERSION_TABLE(6) 202 | SEARCH_VK_IN_CONVERSION_TABLE(7) 203 | SEARCH_VK_IN_CONVERSION_TABLE(8) 204 | SEARCH_VK_IN_CONVERSION_TABLE(9) 205 | SEARCH_VK_IN_CONVERSION_TABLE(10) 206 | 207 | if (*deadChar != 0) // I see dead characters... 208 | { 209 | i = 0; 210 | do 211 | { 212 | baseChar = (WCHAR) pgDeadKey[i].dwBoth; 213 | diacritic = (WCHAR) (pgDeadKey[i].dwBoth >> 16); 214 | if ((baseChar == *outputChar) && (diacritic == *deadChar)) 215 | { 216 | *deadChar = 0; 217 | *outputChar = (WCHAR) pgDeadKey[i].wchComposed; 218 | } 219 | i++; 220 | } 221 | while (pgDeadKey[i].dwBoth != 0); 222 | } 223 | return charCount; 224 | }*/ 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /KeyNStroke/Resources/mouse_test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 32 | 36 | 37 | 47 | 48 | 67 | 69 | 70 | 72 | image/svg+xml 73 | 75 | 76 | 77 | 78 | 79 | 84 | 90 | 94 | 98 | 102 | 108 | 111 | 117 | 123 | 124 | 130 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /KeyNStroke/MouseHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using KeyNStroke; 8 | 9 | namespace KeyNStroke 10 | { 11 | 12 | /// 13 | /// Low level mouse event interception class 14 | /// 15 | public class MouseHook : IDisposable, IMouseRawEventProvider 16 | { 17 | 18 | #region Initializion 19 | 20 | int doubleClickTime; 21 | SettingsStore s; 22 | 23 | /// 24 | /// Set up the mouse hook 25 | /// 26 | public MouseHook(SettingsStore s) 27 | { 28 | this.s = s; 29 | doubleClickTime = NativeMethodsMouse.GetDoubleClickTime(); 30 | Log.e("ME", "doubleClickTime " + doubleClickTime.ToString()); 31 | RegisterMouseHook(); 32 | } 33 | 34 | #endregion 35 | 36 | #region Hook Add/Remove 37 | 38 | 39 | //Variables used in the call to SetWindowsHookEx 40 | private NativeMethodsMouse.HookHandlerDelegate windowsHookExProc; 41 | private IntPtr windowsHookExID = IntPtr.Zero; 42 | 43 | //Variables used in the call to SetWinEventHook 44 | private NativeMethodsEvents.WinEventDelegate setWinEventHookProc; 45 | private IntPtr setWinEventHookID = IntPtr.Zero; 46 | 47 | 48 | /// 49 | /// Registers the function WindowsHookExCallback for the global mouse events winapi. 50 | /// Also registers a WinEventHook to check if the cursor is hidden. 51 | /// 52 | private void RegisterMouseHook() 53 | { 54 | // Register WindowsHookExCallback 55 | if (windowsHookExID == IntPtr.Zero) { 56 | windowsHookExProc = new NativeMethodsMouse.HookHandlerDelegate(WindowsHookExCallback); 57 | using (Process curProcess = Process.GetCurrentProcess()) 58 | { 59 | using (ProcessModule curModule = curProcess.MainModule) 60 | { 61 | windowsHookExID = NativeMethodsMouse.SetWindowsHookEx(NativeMethodsMouse.WH_MOUSE_LL, windowsHookExProc, 62 | NativeMethodsKeyboard.GetModuleHandle(curModule.ModuleName), 0); 63 | } 64 | } 65 | } 66 | 67 | // Register WinEventCallback 68 | // https://devblogs.microsoft.com/oldnewthing/20151116-00/?p=92091 69 | if (setWinEventHookID == IntPtr.Zero) { 70 | setWinEventHookProc = new NativeMethodsEvents.WinEventDelegate(WinEventCallback); 71 | setWinEventHookID = NativeMethodsEvents.SetWinEventHook( 72 | NativeMethodsEvents.WinEvents.EVENT_OBJECT_SHOW, 73 | NativeMethodsEvents.WinEvents.EVENT_OBJECT_NAMECHANGE, 74 | IntPtr.Zero, 75 | setWinEventHookProc, 76 | 0, // ProcessId = 0 and ThreadId = 0 -> global events 77 | 0, 78 | NativeMethodsEvents.WinEventFlags.WINEVENT_SKIPOWNPROCESS 79 | ); 80 | } 81 | } 82 | 83 | /// 84 | /// Unregisters the winapi hook for global mouse events 85 | /// 86 | private void UnregisterMouseHook() 87 | { 88 | if (windowsHookExID != IntPtr.Zero) 89 | { 90 | NativeMethodsMouse.UnhookWindowsHookEx(windowsHookExID); 91 | windowsHookExID = IntPtr.Zero; 92 | } 93 | if (setWinEventHookID != IntPtr.Zero) 94 | { 95 | NativeMethodsEvents.UnhookWinEvent(setWinEventHookID); 96 | setWinEventHookID = IntPtr.Zero; 97 | } 98 | } 99 | 100 | #endregion 101 | 102 | 103 | #region Event Handling 104 | 105 | MouseRawEventArgs lastDownEvent; 106 | 107 | /// 108 | /// Processes the key event captured by the hook. 109 | /// 110 | private IntPtr WindowsHookExCallback(int nCode, 111 | UIntPtr wParam, 112 | ref NativeMethodsMouse.MSLLHOOKSTRUCT lParam) 113 | { 114 | if (nCode < 0) 115 | { 116 | return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam); 117 | } 118 | else 119 | { 120 | if (nCode == NativeMethodsMouse.HC_ACTION) 121 | { 122 | MouseRawEventArgs args = new MouseRawEventArgs(lParam); 123 | args.ParseWparam(wParam); 124 | CheckDoubleClick(args); 125 | 126 | Log.e("ME", String.Format("MOUSE: Button:{0} Action:{1} Orig:{2}", 127 | args.Button.ToString(), args.Action.ToString(), 128 | args.Event.ToString())); 129 | 130 | OnMouseEvent(args); 131 | if (args.preventDefault) 132 | { 133 | return IntPtr.Add(IntPtr.Zero, 1); 134 | } 135 | else 136 | { 137 | return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam); 138 | } 139 | } 140 | else 141 | { 142 | return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam); 143 | } 144 | } 145 | } 146 | 147 | private void CheckDoubleClick(MouseRawEventArgs args) 148 | { 149 | if (lastDownEvent != null && args.Action == MouseAction.Down) 150 | { 151 | if (args.Button == lastDownEvent.Button 152 | && args.Msllhookstruct.time <= lastDownEvent.Msllhookstruct.time + doubleClickTime 153 | && args.Msllhookstruct.pt.Equals(lastDownEvent.Msllhookstruct.pt)) 154 | { 155 | args.Action = MouseAction.DblClk; 156 | Log.e("ME", "DBLCLK"); 157 | } 158 | } 159 | 160 | if (args.Action == MouseAction.Down) 161 | lastDownEvent = args; 162 | } 163 | 164 | private void WinEventCallback(IntPtr hWinEventHook, NativeMethodsEvents.WinEvents eventType, IntPtr hwnd, uint idObject, uint idChild, uint dwEventThread, uint dwmsEventTime) 165 | { 166 | if (hwnd == IntPtr.Zero && 167 | idObject == (uint)NativeMethodsEvents.ObjIds.OBJID_CURSOR && 168 | idChild == NativeMethodsEvents.CHILDID_SELF) 169 | { 170 | switch (eventType) 171 | { 172 | case NativeMethodsEvents.WinEvents.EVENT_OBJECT_HIDE: 173 | OnCursorEvent(false); 174 | break; 175 | case NativeMethodsEvents.WinEvents.EVENT_OBJECT_SHOW: 176 | OnCursorEvent(true); 177 | break; 178 | case NativeMethodsEvents.WinEvents.EVENT_OBJECT_NAMECHANGE: 179 | if (s.CursorIndicatorHideIfCustomCursor) 180 | { 181 | NativeMethodsMouse.CURSORINFO info = NativeMethodsMouse.GetCursorInfoWrapper(); 182 | Log.e("CURSOR", $"EVENT_OBJECT_NAMECHANGE flags={info.flags} hCursor={info.hCursor}"); 183 | if (info.flags == NativeMethodsMouse.CURSOR_HIDDEN || ((uint)info.hCursor) >= 0x100000) /* limit guessed */ 184 | { 185 | OnCursorEvent(false); 186 | } 187 | else 188 | { 189 | OnCursorEvent(true); 190 | } 191 | } 192 | break; 193 | } 194 | } 195 | } 196 | 197 | #endregion 198 | 199 | #region Event Forwarding 200 | 201 | /// 202 | /// Fires if mouse is moved or clicked. 203 | /// 204 | public event MouseRawEventHandler MouseEvent; 205 | 206 | /// 207 | /// Raises the MouseEvent event. 208 | /// 209 | /// An instance of MouseRawEventArgs 210 | public void OnMouseEvent(MouseRawEventArgs e) 211 | { 212 | if (MouseEvent != null) 213 | MouseEvent(e); 214 | } 215 | 216 | /// 217 | /// Fires if cursor is shown or hidden. 218 | /// 219 | public event CursorEventHandler CursorEvent; 220 | 221 | /// 222 | /// Raises the CursorEvent event. 223 | /// 224 | /// True if the cursor is visible 225 | public void OnCursorEvent(bool visible) 226 | { 227 | if (CursorEvent != null) 228 | CursorEvent(visible); 229 | } 230 | #endregion 231 | 232 | 233 | #region Finalizing and Disposing 234 | 235 | ~MouseHook() 236 | { 237 | Log.e("HOOK", "~MouseHook"); 238 | UnregisterMouseHook(); 239 | } 240 | 241 | /// 242 | /// Releases the mouse hook. 243 | /// 244 | public void Dispose() 245 | { 246 | Log.e("HOOK", "Dispose MouseHook"); 247 | UnregisterMouseHook(); 248 | } 249 | #endregion 250 | } 251 | } 252 | --------------------------------------------------------------------------------