├── 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 |
14 |
15 |
--------------------------------------------------------------------------------
/KeyNStroke/ButtonIndicator2.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/KeyNStroke/Themes/Generic.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
19 |
20 |
--------------------------------------------------------------------------------
/KeyNStroke/CursorIndicator1.xaml:
--------------------------------------------------------------------------------
1 |
19 |
21 |
22 |
--------------------------------------------------------------------------------
/KeyNStroke/ReadShortcut.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | Press shortcut for
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/KeyNStroke/AnnotateLine.xaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/KeyNStroke/UrlOpener.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 |
8 | namespace KeyNStroke
9 | {
10 | public class UrlOpener
11 | {
12 | public static void OpenGithub()
13 | {
14 | string url = "https://github.com/Phaiax/Key-n-Stroke/";
15 | ProcessStartInfo si = new ProcessStartInfo(url);
16 | Process.Start(si);
17 | }
18 |
19 | public static void OpenGithubREADME()
20 | {
21 | string url = "https://github.com/Phaiax/Key-n-Stroke/blob/master/README.md";
22 | ProcessStartInfo si = new ProcessStartInfo(url);
23 | Process.Start(si);
24 | }
25 |
26 | public static void OpenGithubIssues()
27 | {
28 | string url = "https://github.com/Phaiax/Key-n-Stroke/issues";
29 | ProcessStartInfo si = new ProcessStartInfo(url);
30 | Process.Start(si);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/KeyNStroke/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace KeyNStroke
8 | {
9 | public class Log
10 | {
11 | static string TagFilter = "";
12 | static string[] allowed = new string[]{};
13 | public static void SetTagFilter(string tagfilter)
14 | {
15 | TagFilter = tagfilter;
16 | allowed = TagFilter.Split(new char[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries);
17 | }
18 |
19 | static bool filter(string tag)
20 | {
21 | return tag == TagFilter || TagFilter == "" || allowed.Contains(tag);
22 | }
23 |
24 | public static void e(string tag, string msg)
25 | {
26 | if(filter(tag))
27 | {
28 | Console.WriteLine(tag.ToUpper() + " " + msg);
29 | }
30 | }
31 |
32 | public static void e(string msg)
33 | {
34 | e("ERR", msg);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/KeyNStroke/LabeledSlider.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/KeyNStroke.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30011.22
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyNStroke", "KeyNStroke\KeyNStroke.csproj", "{BA117557-FEBC-45C5-8895-4017D05C3DCD}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {BA117557-FEBC-45C5-8895-4017D05C3DCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {BA117557-FEBC-45C5-8895-4017D05C3DCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {BA117557-FEBC-45C5-8895-4017D05C3DCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {BA117557-FEBC-45C5-8895-4017D05C3DCD}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {72B0FF35-BC69-4A53-ACA7-FFFD74A21114}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/KeyNStroke/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace KeyNStroke.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/KeyNStroke/ReadShortcut.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.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Shapes;
14 |
15 | namespace KeyNStroke
16 | {
17 | ///
18 | /// Interaktionslogik für ReadShortcut.xaml
19 | ///
20 | public partial class ReadShortcut : Window
21 | {
22 | IKeystrokeEventProvider k;
23 |
24 | string shortcut;
25 | public string Shortcut
26 | {
27 | get { return shortcut; }
28 | }
29 |
30 | public ReadShortcut(IKeystrokeEventProvider k, string purpose)
31 | {
32 | InitializeComponent();
33 | this.k = k;
34 | this.run_purpose.Text = purpose;
35 | }
36 |
37 | private void Button_Click(object sender, RoutedEventArgs e)
38 | {
39 | Close();
40 | }
41 |
42 | private void Window_Loaded(object sender, RoutedEventArgs e)
43 | {
44 | this.k.KeystrokeEvent += k_KeystrokeEvent;
45 |
46 | }
47 |
48 | void k_KeystrokeEvent(KeystrokeEventArgs e)
49 | {
50 | string idf = e.ShortcutIdentifier();
51 | if (idf != null)
52 | {
53 | shortcut = idf;
54 | Close();
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/KeyNStroke/ButtonIndicator1.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace KeyNStroke
2 | {
3 | partial class ButtonIndicator1
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.SuspendLayout();
32 | //
33 | // ButtonIndicator
34 | //
35 | this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 24F);
36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
37 | this.ClientSize = new System.Drawing.Size(521, 482);
38 | this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6);
39 | this.Name = "ButtonIndicator";
40 | this.ShowInTaskbar = false;
41 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
42 | this.Text = "CursorIndicator";
43 | this.Load += new System.EventHandler(this.ButtonIndicator_Load);
44 | this.ResumeLayout(false);
45 |
46 | }
47 |
48 | #endregion
49 |
50 |
51 |
52 | }
53 | }
--------------------------------------------------------------------------------
/KeyNStroke/EnumBindingSourceExtention.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Controls;
4 | using System.Windows.Documents;
5 | using System.Windows.Markup;
6 |
7 | namespace KeyNStroke
8 | {
9 | public class EnumBindingSourceExtention : MarkupExtension
10 | {
11 | public Type EnumType { get; private set; }
12 |
13 | public EnumBindingSourceExtention(Type enumType)
14 | {
15 | if (enumType == null || !enumType.IsEnum)
16 | throw new Exception("enumType must be of type enum");
17 | this.EnumType = enumType;
18 | }
19 |
20 | public static T GetAttributeOfType(Enum enumVal) where T : System.Attribute
21 | {
22 | var type = enumVal.GetType();
23 | var memInfo = type.GetMember(Enum.GetName(type, enumVal)); // type.GetMember(enumVal.ToString());
24 | var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
25 | return (attributes.Length > 0) ? (T)attributes[0] : null;
26 | }
27 |
28 | public static string GetAttributeDescription(Enum enumValue)
29 | {
30 | var attribute = GetAttributeOfType(enumValue);
31 | return attribute == null ? String.Empty : attribute.Description;
32 | }
33 |
34 | public override object ProvideValue(IServiceProvider serviceProvider)
35 | {
36 | Array variants = Enum.GetValues(EnumType);
37 | List b = new List(variants.Length);
38 | foreach (var a in variants)
39 | {
40 | ComboBoxItem i = new ComboBoxItem();
41 | i.Content = GetAttributeDescription((Enum)a); // new TextBlock(new Run("Hi"));
42 | i.Tag = a;
43 | b.Add(i);
44 | }
45 | return b;
46 | }
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) Microsoft Corporation.
2 | # Licensed under the MIT license.
3 |
4 | # https://github.com/microsoft/github-actions-for-desktop-apps
5 |
6 | # This continuous integration pipeline is triggered anytime a user pushes code to the repo.
7 |
8 | # This pipeline builds the project, runs unit tests, then saves the build artifact.
9 |
10 | name: Key-n-Stroke Continuous Integration
11 |
12 | # Trigger on every master branch push and pull request
13 | on:
14 | push:
15 | branches: [ master ]
16 | pull_request:
17 | branches: [ master ]
18 |
19 | jobs:
20 |
21 | build:
22 | runs-on: windows-latest
23 |
24 | env:
25 | Solution_Path: KeyNStroke.sln
26 | #Test_Project_Path: KeyNStroke.Tests\KeyNStroke.Tests.csproj
27 | App_Project_Path: KeyNStroke\KeyNStroke.csproj
28 | App_Output_Directory: KeyNStroke\bin
29 | App_Assembly: Key-n-Stroke.exe
30 | Actions_Allow_Unsecure_Commands: true # Allows AddPAth and SetEnv commands
31 |
32 | steps:
33 | - name: Checkout
34 | uses: actions/checkout@v2
35 | with:
36 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
37 |
38 | # Versioning
39 | - name: Use Nerdbank.GitVersioning to set version variables
40 | uses: dotnet/nbgv@master
41 | with:
42 | setAllVars: true
43 |
44 | # Install the .NET Core workload
45 | - name: Install .NET
46 | uses: actions/setup-dotnet@v1
47 | with:
48 | dotnet-version: '5.0.x'
49 |
50 | # Add MsBuild to the PATH: https://github.com/microsoft/setup-msbuild
51 | - name: Setup MSBuild.exe
52 | uses: microsoft/setup-msbuild@v1.0.1
53 |
54 | #- name: Execute Unit Tests
55 | # run: dotnet test $env:Test_Project_Path
56 |
57 | # Restore the application
58 | - name: Restore the application to populate the obj and packages folder
59 | run: msbuild $env:Solution_Path /t:Restore /p:RestorePackagesConfig=true /p:Configuration=$env:Configuration
60 | env:
61 | Configuration: Release
62 |
63 |
64 | # Actual build
65 | - name: Build the application
66 | run: msbuild $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="Any CPU"
67 |
68 | env:
69 | Configuration: Release
70 |
71 |
72 | # Signing
73 | # https://github.com/dlemstra/code-sign-action
74 | # https://github.com/GabrielAcostaEngler/signtool-code-sign
75 | # https://archi-lab.net/code-signing-assemblies-with-github-actions/
76 |
77 | - name: Upload build artifacts
78 | uses: actions/upload-artifact@v1
79 | with:
80 | name: KeyNStroke-${{env.NBGV_SemVer2}}
81 | path: ${{ env.App_Output_Directory }}\Release\
82 |
83 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/KeyNStroke/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // Allgemeine Informationen über eine Assembly werden über die folgenden
8 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
9 | // die einer Assembly zugeordnet sind.
10 | [assembly: AssemblyTitle("Key'n'Stroke")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Key'n'Stroke")]
15 | [assembly: AssemblyCopyright("Copyright © 2021")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
20 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
21 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
22 | [assembly: ComVisible(false)]
23 |
24 | //Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
25 | //ImCodeVerwendeteKultur in der .csproj-Datei
26 | //in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
27 | //(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
28 | //des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
29 | //sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
36 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
37 | // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
38 | ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
39 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
40 | // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
41 | )]
42 |
43 |
44 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
45 | //
46 | // Hauptversion
47 | // Nebenversion
48 | // Buildnummer
49 | // Revision
50 | //
51 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
52 | // übernehmen, indem Sie "*" eingeben:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | //[assembly: AssemblyVersion("1.2.0.0")]
55 | //[assembly: AssemblyFileVersion("1.2.0.0")]
56 |
--------------------------------------------------------------------------------
/KeyNStroke/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace KeyNStroke.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
17 | ///
18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
19 | // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
21 | // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KeyNStroke.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
51 | /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/KeyNStroke/UIHelper.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.Media;
8 | using MColor = System.Windows.Media.Color;
9 | using DColor = System.Drawing.Color;
10 |
11 |
12 | namespace KeyNStroke
13 | {
14 | class UIHelper
15 | {
16 | #region Helper to convert Drawing.Color to Windows.Media.Color
17 | public static MColor ToMediaColor(DColor color)
18 | {
19 | return MColor.FromArgb(color.A, color.R, color.G, color.B);
20 | }
21 |
22 | public static DColor ToDrawingColor(MColor color)
23 | {
24 | return DColor.FromArgb(color.A, color.R, color.G, color.B);
25 | }
26 | #endregion
27 |
28 | ///
29 | /// Finds a Child of a given item in the visual tree.
30 | ///
31 | /// A direct parent of the queried item.
32 | /// The type of the queried item.
33 | /// x:Name or Name of child.
34 | /// The first parent item that matches the submitted type parameter.
35 | /// If not matching item can be found,
36 | /// a null parent is being returned.
37 | public static T FindChild(DependencyObject parent, string childName)
38 | where T : DependencyObject
39 | {
40 | // Confirm parent and childName are valid.
41 | if (parent == null) return null;
42 |
43 | if (parent is FrameworkElement)
44 | ((FrameworkElement)parent).ApplyTemplate();
45 |
46 | T foundChild = null;
47 |
48 | int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
49 | for (int i = 0; i < childrenCount; i++)
50 | {
51 | var child = VisualTreeHelper.GetChild(parent, i);
52 | // If the child is not of the request child type child
53 | T childType = child as T;
54 | if (childType == null)
55 | {
56 | // recursively drill down the tree
57 | foundChild = FindChild(child, childName);
58 |
59 | // If the child is found, break so we do not overwrite the found child.
60 | if (foundChild != null) break;
61 | }
62 | else if (!string.IsNullOrEmpty(childName))
63 | {
64 | var frameworkElement = child as FrameworkElement;
65 | // If the child's name is set for search
66 | if (frameworkElement != null && frameworkElement.Name == childName)
67 | {
68 | // if the child's name is of the request name
69 | foundChild = (T)child;
70 | break;
71 | }
72 | }
73 | else
74 | {
75 | // child element found.
76 | foundChild = (T)child;
77 | break;
78 | }
79 | }
80 |
81 | return foundChild;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/KeyNStroke/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 | PerMonitor
55 | true
56 |
57 |
58 |
59 |
60 |
61 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | */Thumbs.db
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.sln.docstates
10 |
11 | # Build results
12 | [Dd]ebug/
13 | [Dd]ebugPublic/
14 | [Rr]elease/
15 | x64/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | ## TODO: Comment the next line if you want to checkin your
129 | ## web deploy settings but do note that will include unencrypted
130 | ## passwords
131 | #*.pubxml
132 |
133 | # NuGet Packages Directory
134 | packages/*
135 | ## TODO: If the tool you use requires repositories.config
136 | ## uncomment the next line
137 | #!packages/repositories.config
138 |
139 | # Enable "build/" folder in the NuGet Packages folder since
140 | # NuGet packages use it for MSBuild targets.
141 | # This line needs to be after the ignore of the build folder
142 | # (and the packages folder if the line above has been uncommented)
143 | !packages/build/
144 |
145 | # Windows Azure Build Output
146 | csx/
147 | *.build.csdef
148 |
149 | # Windows Store app package directory
150 | AppPackages/
151 |
152 | # Others
153 | sql/
154 | *.Cache
155 | ClientBin/
156 | [Ss]tyle[Cc]op.*
157 | ~$*
158 | *~
159 | *.dbmdl
160 | *.dbproj.schemaview
161 | *.pfx
162 | *.publishsettings
163 | node_modules/
164 |
165 | # RIA/Silverlight projects
166 | Generated_Code/
167 |
168 | # Backup & report files from converting an old project file
169 | # to a newer Visual Studio version. Backup files are not needed,
170 | # because we have git ;-)
171 | _UpgradeReport_Files/
172 | Backup*/
173 | UpgradeLog*.XML
174 | UpgradeLog*.htm
175 |
176 | # SQL Server files
177 | *.mdf
178 | *.ldf
179 |
180 | # Business Intelligence projects
181 | *.rdl.data
182 | *.bim.layout
183 | *.bim_*.settings
184 |
185 | # Microsoft Fakes
186 | FakesAssemblies/
187 |
188 | # LightSwitch generated files
189 | GeneratedArtifacts/
190 | _Pvt_Extensions/
191 | ModelManifest.xml
192 |
193 | .vs
194 |
195 | # keys
196 | *.priv.xml
197 |
--------------------------------------------------------------------------------
/KeyNStroke/Welcome.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using System.Threading;
5 | using System.Windows;
6 | using System.Windows.Documents;
7 |
8 | namespace KeyNStroke
9 | {
10 | ///
11 | /// Interaktionslogik für Welcome.xaml
12 | ///
13 | public partial class Welcome : Window
14 | {
15 | readonly SettingsStore settings;
16 |
17 | public Welcome(SettingsStore s)
18 | {
19 | InitializeComponent();
20 | settings = s;
21 | layout_root.DataContext = settings;
22 |
23 | Updater.Updater.Instance.OnUiUpdate += Updater_onUIUpdate;
24 | Updater.Updater.Instance.OnShutdownRequest += Updater_onShutdownRequest;
25 | ButtonCheckForUpdates.Click += Updater.Updater.Instance.UiButton_Click;
26 |
27 | Version current = Assembly.GetExecutingAssembly().GetName().Version;
28 | VersionInfo.Text = current.ToString(3);
29 |
30 | }
31 |
32 | protected override void OnClosed(EventArgs e)
33 | {
34 | base.OnClosed(e);
35 | ((App)Application.Current).onWelcomeWindowClosed();
36 | }
37 |
38 | private void Hyperlink_RequestNavigate_README(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
39 | {
40 | UrlOpener.OpenGithubREADME();
41 | }
42 |
43 |
44 | private void Hyperlink_RequestNavigate_Issues(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
45 | {
46 | UrlOpener.OpenGithubIssues();
47 | }
48 |
49 |
50 | private void ButtonSettings_Click(object sender, RoutedEventArgs e)
51 | {
52 | ((App)Application.Current).showSettingsWindow();
53 | }
54 |
55 | #region Updater
56 |
57 | List updateDetails;
58 |
59 | private void Updater_onUIUpdate(object sender, Updater.Statemachine.UiUpdateEventArgs e)
60 | {
61 | this.Dispatcher.InvokeAsync(() =>
62 | {
63 | ButtonCheckForUpdates.Content = e.buttonText;
64 | ButtonCheckForUpdates.IsEnabled = e.buttonEnabled;
65 | TextUpdateStatus.Inlines.Clear();
66 | if (e.details != null && e.details.Count > 0)
67 | {
68 | Hyperlink hl = new Hyperlink(new Run(e.info))
69 | {
70 | NavigateUri = new Uri("none://dummy")
71 | };
72 | hl.RequestNavigate += Hyperlink_RequestNavigate_UpdateDetails;
73 | TextUpdateStatus.Inlines.Add(hl);
74 | updateDetails = e.details;
75 | } else
76 | {
77 | TextUpdateStatus.Inlines.Add(new Run(e.info));
78 | updateDetails = null;
79 | }
80 | });
81 | }
82 |
83 | private void Hyperlink_RequestNavigate_UpdateDetails(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
84 | {
85 | if (updateDetails != null)
86 | {
87 | MessageBox.Show(String.Join("\n", updateDetails), "Updater Info", MessageBoxButton.OK);
88 | }
89 | }
90 |
91 |
92 |
93 | private void Updater_onShutdownRequest(object sender)
94 | {
95 | this.Dispatcher.InvokeAsync(() =>
96 | {
97 | Application.Current.Shutdown();
98 | });
99 | }
100 |
101 | #endregion
102 |
103 | private void ButtonHideThisWindow_Click(object sender, RoutedEventArgs e)
104 | {
105 | Close();
106 | }
107 |
108 | private void ButtonExitApplication_Click(object sender, RoutedEventArgs e)
109 | {
110 | System.Windows.Application.Current.Shutdown();
111 | }
112 |
113 | private void Hyperlink_RequestNavigate_settings(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
114 | {
115 | ((App)Application.Current).showSettingsWindow();
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/KeyNStroke/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/KeyNStroke/Welcome.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 | settings.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
56 |
58 | Documentation
60 |
61 | Report Bugs in GitHub
63 |
71 |
79 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/KeyNStroke/KeystrokeDisplay.xaml:
--------------------------------------------------------------------------------
1 |
20 |
23 |
24 |
31 |
32 |
44 |
54 |
64 |
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 |
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 |
--------------------------------------------------------------------------------