Full Code of Phaiax/Key-n-Stroke for AI

master be9d567d2a65 cached
71 files
489.1 KB
106.8k tokens
506 symbols
1 requests
Download .txt
Showing preview only (514K chars total). Download the full file or copy to clipboard to get everything.
Repository: Phaiax/Key-n-Stroke
Branch: master
Commit: be9d567d2a65
Files: 71
Total size: 489.1 KB

Directory structure:
gitextract_xryqbs4m/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── DEVELOP.md
├── Directory.Build.props
├── KeyNStroke/
│   ├── AnnotateLine.xaml
│   ├── AnnotateLine.xaml.cs
│   ├── App.config
│   ├── App.xaml
│   ├── App.xaml.cs
│   ├── ButtonIndicator1.Designer.cs
│   ├── ButtonIndicator1.cs
│   ├── ButtonIndicator1.resx
│   ├── ButtonIndicator2.xaml
│   ├── ButtonIndicator2.xaml.cs
│   ├── CursorIndicator1.xaml
│   ├── CursorIndicator1.xaml.cs
│   ├── EnumBindingSourceExtention.cs
│   ├── FodyWeavers.xml
│   ├── FodyWeavers.xsd
│   ├── ImageResources.cs
│   ├── KeyNStroke.csproj
│   ├── KeyboardHook.cs
│   ├── KeyboardLayoutParser.cs
│   ├── KeyboardRawEvent.cs
│   ├── KeystrokeDisplay.xaml
│   ├── KeystrokeDisplay.xaml.cs
│   ├── KeystrokeEvent.cs
│   ├── KeystrokeParser.cs
│   ├── LabeledSlider.py
│   ├── LabeledSlider.xaml
│   ├── LabeledSlider.xaml.cs
│   ├── Log.cs
│   ├── MouseHook.cs
│   ├── MouseRawEvent.cs
│   ├── NativeMethodsDC.cs
│   ├── NativeMethodsEvents.cs
│   ├── NativeMethodsGWL.cs
│   ├── NativeMethodsKeyboard.cs
│   ├── NativeMethodsMouse.cs
│   ├── NativeMethodsWindow.cs
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   ├── Resources.Designer.cs
│   │   ├── Resources.resx
│   │   ├── Settings.Designer.cs
│   │   └── Settings.settings
│   ├── ReadShortcut.xaml
│   ├── ReadShortcut.xaml.cs
│   ├── ResizeButton.xaml
│   ├── ResizeButton.xaml.cs
│   ├── Resources/
│   │   └── updateKey.pub.xml
│   ├── Settings1.xaml
│   ├── Settings1.xaml.cs
│   ├── SettingsStore.cs
│   ├── SpecialkeysParser.cs
│   ├── Themes/
│   │   └── Generic.xaml
│   ├── TweenLabel.cs
│   ├── UIHelper.cs
│   ├── Updater/
│   │   ├── Admininstration.cs
│   │   ├── Statemachine.cs
│   │   ├── Updater.cs
│   │   └── Utils.cs
│   ├── UrlOpener.cs
│   ├── Welcome.xaml
│   ├── Welcome.xaml.cs
│   ├── app.manifest
│   └── packages.config
├── KeyNStroke.sln
├── LICENSE
├── README.md
└── version.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/workflows/ci.yml
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

# https://github.com/microsoft/github-actions-for-desktop-apps

# This continuous integration pipeline is triggered anytime a user pushes code to the repo.

# This pipeline builds the project, runs unit tests, then saves the build artifact.

name:  Key-n-Stroke Continuous Integration

# Trigger on every master branch push and pull request
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    runs-on: windows-latest

    env:
      Solution_Path: KeyNStroke.sln
      #Test_Project_Path: KeyNStroke.Tests\KeyNStroke.Tests.csproj
      App_Project_Path: KeyNStroke\KeyNStroke.csproj
      App_Output_Directory: KeyNStroke\bin
      App_Assembly: Key-n-Stroke.exe
      Actions_Allow_Unsecure_Commands: true # Allows AddPAth and SetEnv commands

    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        fetch-depth: 0 # avoid shallow clone so nbgv can do its work.

    # Versioning
    - name: Use Nerdbank.GitVersioning to set version variables
      uses: dotnet/nbgv@master
      with:
        setAllVars: true

    # Install the .NET Core workload
    - name: Install .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '5.0.x'

    # Add  MsBuild to the PATH: https://github.com/microsoft/setup-msbuild
    - name: Setup MSBuild.exe
      uses: microsoft/setup-msbuild@v1.0.1

    #- name: Execute Unit Tests
    #  run: dotnet test $env:Test_Project_Path

    # Restore the application
    - name:  Restore the application to populate the obj and packages folder
      run: msbuild $env:Solution_Path /t:Restore /p:RestorePackagesConfig=true /p:Configuration=$env:Configuration
      env:
        Configuration: Release


    # Actual build
    - name:  Build the application
      run: msbuild $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="Any CPU"

      env:
        Configuration: Release

    
    # Signing
    # https://github.com/dlemstra/code-sign-action
    # https://github.com/GabrielAcostaEngler/signtool-code-sign
    # https://archi-lab.net/code-signing-assemblies-with-github-actions/

    - name: Upload build artifacts
      uses: actions/upload-artifact@v1
      with:
        name: KeyNStroke-${{env.NBGV_SemVer2}}
        path: ${{ env.App_Output_Directory }}\Release\



================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

*/Thumbs.db

# User-specific files
*.suo
*.user
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
x64/
build/
bld/
[Bb]in/
[Oo]bj/

# Roslyn cache directories
*.ide/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

#NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile

# Visual Studio profiler
*.psess
*.vsp
*.vspx

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding addin-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml

# NuGet Packages Directory
packages/*
## TODO: If the tool you use requires repositories.config
## uncomment the next line
#!packages/repositories.config

# Enable "build/" folder in the NuGet Packages folder since
# NuGet packages use it for MSBuild targets.
# This line needs to be after the ignore of the build folder
# (and the packages folder if the line above has been uncommented)
!packages/build/

# Windows Azure Build Output
csx/
*.build.csdef

# Windows Store app package directory
AppPackages/

# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml

.vs

# keys
*.priv.xml


================================================
FILE: DEVELOP.md
================================================
# Key'n'Stroke

(previously PxKeystrokesForScreencasts)

This is a little documentation about how the source code is organized and works

## How to make a new release

 1. Change Version: 
   - Twice in AssemblyInfo.cs
   - In project settings under "Publish"
 2. Build the App in Release mode
 3. Sign the file using the certum certificate
    - cmd.exe: `signtool.exe sign /n Open /t http://time.certum.pl/ /fd sha256 /v Key-n-Stroke.exe`
 4. Verify the signature:
    - cmd.exe: `signtool.exe verify /pa Key-n-Stroke.exe`
 4. `./Key-n-Stroke.exe --create-update-manifest`
 5. Change manifest: Update description
 6. `./Key-n-Stroke.exe --sign-update-manifest`
 7. Copy executable into release folder
 8. Commit and push
 7. Upload manifest `scp updateManifest.xml $SSHSERVER:html/key-n-stroke/`

## How to prepare the development environment

 - Install Visual Studio 2019
 - Download dependencies:
   - Clean the folder `packages` in the repository root
   - Search a msbuild.exe and run it like this:
   - 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`
 - Then run the application using Visual Studio

## Main (Program.cs)

The main function is found in Program.cs.

It initializes the classes decribed below.
 * new KeyboardHook()
 * new KeystrokeParser()
 * new SettingsStore()
 * new KeystrokeDisplay()

The keypress information is intercepted in KeyboardHook, passed to KeystrokeParser and then passed to KeystrokeDisplay.


## How key events are intercepted from the system

In Windows, you can register a callback (also known as hook) for certain process messages like [keyboard][LowLevelKeyboardProc] and mouse events.

Callbacks are registered using the function [SetWindowsHookEx(event type, callback function, ...)][SetWindowsHookEx].
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.

Sometimes special data structures and constants are needed for these system functions (for example [KBDLLHOOKSTRUCT][KBDLLHOOKSTRUCT]).They are also defined in these files.


### System calls (KeyboardHook.cs KeyboardRawEvents.cs)

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().

KeyboardHook exposes the C# event <code>KeyEvent</code> that takes methods of delegate type <code>KeyboardRawEventHandler</code>. (via <code>interface IKeyboardRawEventProvider</code> in KeyboardRawEvent.cs).

The idea is, that you just do this nice pure C# thing

```	void hook_KeyEvent(KeyboardRawEventArgs raw_e)
	{
		// process hook
	}
	IKeyboardRawEventProvider myKeyboardHook = new KeyboardHook();
	hook.KeyEvent += hook_KeyEvent;
```

... instead of dealing with the raw system library calls.

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 <code>raw_e</code>.

### Key event processing (KeystrokeParser.cs KeystrokeEvent.cs)

Next, the <code>KeyboardRawEventArgs raw_e</code> are converted into Keystrokes. This is happening in a similar interface/event pattern.

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.

Input: The KeystrokeParser registers itself on the <code>KeyEvent</code> of the KeyboardHook in the constructor.

Output: The KeystrokeParser exposes the C# event <code>KeystrokeEvent</code> that takes methods of delegate type KeystrokeEventHandler. (via interface IKeystrokeEventProvider in KeystrokeEvent.cs).

During Calculation, the KeystrokeParser uses the static methods in <code>KeyboardLayoutParser</code> and <code>SpecialkeysParser</code> (can be found in the two .cs files) to do some of the conversion.

The <code>KeyboardLayoutParser</code> 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).

The <code>SpecialkeysParser</code> 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.


The output of the KeystrokeParser is used by the KeystrokesDisplay.

[SetWindowsHookEx]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx "SetWindowsHookEx function"
[LowLevelKeyboardProc]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985%28v=vs.85%29.aspx "LowLevelKeyboardProc callback function"
[TranslateMessage]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955%28v=vs.85%29.aspx "TranslateMessage function"
[KBDLLHOOKSTRUCT]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx "KBDLLHOOKSTRUCT structure"

## Displaying Keystrokes (KeystrokesDisplay.cs code (F7))

 * displays new keys as wished by the information in KeystrokeEventArgs (<code>k_KeystrokeEvent()</code>)
 * Checks if resizing mode is activated (<code>CheckForSettingsMode()</code>)
 * allows resizing and moving of window
 * keeps track of the History of keystrokes (<code>List&lt;TweenLabel&gt; tweenLabels</code>)

### TweenLabel (TweenLabel.cs code (F7))

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.

## Settings

Are stored in the C# Application.UserAppDataRegistry using the wrapper functions in SettingsStore.cs.

The Settings are changed in the UI window Settings.cs

## NativeMethodsSWP.cs

A wrapper for calling the system library function SetWindowPos.
The class NativeMethodsSWP provides a method to pin the UI on top of everything.

## NativeMethodsGWL.cs

Some wrappers for calling system library functions.
They provide methods to make the UI through clickable and clickable again.

## UrlOpener.cs

Open a browser.

================================================
FILE: Directory.Build.props
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
      <Version>3.4.244</Version>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
</Project>

================================================
FILE: KeyNStroke/AnnotateLine.xaml
================================================
<Window x:Class="KeyNStroke.AnnotateLine"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:KeyNStroke"
        mc:Ignorable="d"
        Title="AnnotateLine" Height="8" Width="800"
        AllowsTransparency="True"
        Topmost="True"
        ShowInTaskbar="False"
        WindowStyle="None"
        ResizeMode="NoResize"
        Background="Transparent" Loaded="Window_Loaded" Closed="Window_Closed">
    <Grid>
        <Rectangle x:Name="line" Fill="#FF7EEF84" Margin="1">
            <Rectangle.Effect>
                <BlurEffect Radius="2" KernelType="Box"/>
            </Rectangle.Effect>
        </Rectangle>
    </Grid>
</Window>


================================================
FILE: KeyNStroke/AnnotateLine.xaml.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using static KeyNStroke.NativeMethodsMouse;

namespace KeyNStroke
{
    /// <summary>
    /// Interaktionslogik für AnnotateLine.xaml
    /// </summary>
    public partial class AnnotateLine : Window
    {

        IMouseRawEventProvider m;
        IKeystrokeEventProvider k;
        SettingsStore s;
        IntPtr windowHandle;
        bool isDown;
        POINT startCursorPosition = new POINT(0, 0);
        POINT endCursorPosition = new POINT(0, 0);
        bool nextClickDraws = false;
        bool nextClickHides = false;

        public AnnotateLine(IMouseRawEventProvider m, IKeystrokeEventProvider k, SettingsStore s)
        {
            InitializeComponent();

            this.m = m;
            this.s = s;
            this.k = k;

            s.PropertyChanged += settingChanged;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            s.CallPropertyChangedForAllProperties();
            m.MouseEvent += m_MouseEvent;
            this.k.KeystrokeEvent += m_KeystrokeEvent;
            windowHandle = new WindowInteropHelper(this).Handle;
            SetFormStyles();
        }

        #region Shortcut

        public string AnnotateLineShortcut;

        void m_KeystrokeEvent(KeystrokeEventArgs e)
        {
            if (s == null) return;
            string pressed = e.ShortcutIdentifier();
            e.raw.preventDefault = e.raw.preventDefault || CheckForTrigger(pressed);
        }

        private bool CheckForTrigger(string pressed)
        {
            if (AnnotateLineShortcut != null && pressed == AnnotateLineShortcut)
            {
                nextClickDraws = true;
                return true;
            }
            return false;
        }

        public void SetAnnotateLineShortcut(string shortcut)
        {
            if (KeystrokeDisplay.ValidateShortcutSetting(shortcut))
            {
                AnnotateLineShortcut = shortcut;
            }
            else
            {
                AnnotateLineShortcut = s.AnnotateLineShortcutDefault;
            }
        }

        #endregion

        private void m_MouseEvent(MouseRawEventArgs raw_e)
        {
            if (s == null) return;
            if (!isDown && nextClickHides && raw_e.Action == MouseAction.Down)
            {
                raw_e.preventDefault = true;
                nextClickHides = false;
                this.Hide();
            }

            if (isDown && raw_e.Action == MouseAction.Move)
            {
                endCursorPosition = raw_e.Position;
                UpdatePositionAndSize();
            }
            else if (!isDown && raw_e.Action == MouseAction.Down && nextClickDraws)
            {
                isDown = true;
                raw_e.preventDefault = true;
                nextClickDraws = false;
                startCursorPosition = raw_e.Position;
                endCursorPosition = raw_e.Position;
            }
            else if (isDown && raw_e.Action == MouseAction.Up)
            {
                isDown = false;
                nextClickHides = true;
            }
        }

        private void settingChanged(object sender, PropertyChangedEventArgs e)
        {
            this.Dispatcher.BeginInvoke((Action)(() =>
            {
                if (s == null) return;
                switch (e.PropertyName)
                {
                    case "AnnotateLineShortcutTrigger":
                        nextClickDraws = true;
                        break;
                    case "AnnotateLineColor":
                        UpdateColor();
                        break;
                    case "AnnotateLineShortcut":
                        SetAnnotateLineShortcut(s.AnnotateLineShortcut);
                        break;
                }
            }));
        }

        void SetFormStyles()
        {
            Log.e("CI", $"WindowHandle={windowHandle}");
            NativeMethodsGWL.ClickThrough(windowHandle);
            NativeMethodsGWL.HideFromAltTab(windowHandle);

            UpdatePositionAndSize();
        }

        void UpdateColor()
        {
            line.Fill = new SolidColorBrush(UIHelper.ToMediaColor(s.AnnotateLineColor));
        }

        void UpdatePositionAndSize()
        {
            if (isDown)
            {
                this.Show();
                Int32 xmin = Math.Min(startCursorPosition.X, endCursorPosition.X);
                Int32 ymin = Math.Min(startCursorPosition.Y, endCursorPosition.Y);
                Int32 h = Math.Abs(startCursorPosition.Y - endCursorPosition.Y);
                Int32 w = Math.Abs(startCursorPosition.X - endCursorPosition.X);
                bool horizontal = w >= h;

                IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(startCursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);
                uint adpiX = 0, adpiY = 0;
                NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);
                Log.e("AL", $"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={startCursorPosition.X} cy={startCursorPosition.Y}");


                if (horizontal)
                {
                    this.Width = w / (double)adpiX * 96.0;
                    this.Height = 4;
                    NativeMethodsWindow.SetWindowPosition(windowHandle, (int)xmin, (int)startCursorPosition.Y - 2);
                }
                else
                {
                    this.Width = 4;
                    this.Height = h / (double)adpiY * 96.0;
                    NativeMethodsWindow.SetWindowPosition(windowHandle, (int)startCursorPosition.X - 2, ymin);
                }

            }
            else
            {
                this.Hide();
            }
            
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            if (m != null)
            {
                m.MouseEvent -= m_MouseEvent;
            }
            if (k != null)
            {
                k.KeystrokeEvent -= m_KeystrokeEvent;
            }
            if (s != null)
            {
                s.PropertyChanged -= settingChanged;
            }
            m = null;
            s = null;
            k = null;
        }
    }
}


================================================
FILE: KeyNStroke/App.config
================================================
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    </startup>
    <System.Windows.Forms.ApplicationConfigurationSection>
      <add key="DpiAwareness" value="PerMonitorV2" />
    </System.Windows.Forms.ApplicationConfigurationSection>
</configuration>

================================================
FILE: KeyNStroke/App.xaml
================================================
<Application x:Class="KeyNStroke.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:KeyNStroke" ShutdownMode="OnExplicitShutdown">
    <Application.Resources>
         
    </Application.Resources>
</Application>


================================================
FILE: KeyNStroke/App.xaml.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing.Printing;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using KeyNStroke;

namespace KeyNStroke
{
    /// <summary>
    /// Interaktionslogik für "App.xaml"
    /// </summary>
    public partial class App : Application
    {

        #region Main()

        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public static void Main()
        {
            App app = new App();
            app.InitializeComponent();
            app.Run();
        }

        #endregion

        #region Init

        SettingsStore mySettings;
        Window welcomeWindow;
        Settings1 settingsWindow;

        protected override void OnStartup(StartupEventArgs e)
        {
            Log.SetTagFilter("UPDATE");

            if (Updater.Updater.HandleArgs(e.Args))
            {
                Shutdown();
            }

            InitSettings();
            ImageResources.Init(mySettings.ButtonIndicatorCustomIconsFolder);
            InitKeyboardInterception();

            mySettings.PropertyChanged += OnSettingChanged;
            myKeystrokeConverter.KeystrokeEvent += m_KeystrokeEvent;

            //myUi.FormClosed += OnUiClosed;
            mySettings.CallPropertyChangedForAllProperties();

            makeNotifyIcon();

            if (mySettings.WelcomeOnStartup)
            {
                showWelcomeWindow();
            }
        }

        protected override void OnActivated(EventArgs e)
        {

        }


        private void InitSettings()
        {
            mySettings = new SettingsStore();

            mySettings.WindowLocationDefault = new Point(
                System.Windows.SystemParameters.PrimaryScreenWidth - mySettings.WindowSizeDefault.Width - 20,
                System.Windows.SystemParameters.PrimaryScreenHeight - mySettings.WindowSizeDefault.Height - 40);

            //mySettings.ResetAll(); // test defaults
            mySettings.LoadAll();
        }

        IKeyboardRawEventProvider myKeyboardHook;
        IKeystrokeEventProvider myKeystrokeConverter;

        private void InitKeyboardInterception()
        {
            myKeyboardHook = new KeyboardHook();
            myKeystrokeConverter = new KeystrokeParser(myKeyboardHook);
        }

        #endregion

        #region Closing/Exiting

        private void OnUiClosed(object sender, EventArgs e)
        {
            DisableCursorIndicator();
            DisableButtonIndicator();
            DisableKeystrokeHistory();
        }

        protected override void OnExit(ExitEventArgs e)
        {
            OnUiClosed(this, e);
            this.notifyIcon_main.Visible = false;
        }

        #endregion

        #region Tray Icon

        private System.Windows.Forms.NotifyIcon notifyIcon_main;
        void makeNotifyIcon()
        {
            var _assembly = System.Reflection.Assembly.GetExecutingAssembly();
            
            Stream iconStream = Application.GetResourceStream(new Uri("pack://application:,,,/Key-n-Stroke;component/Resources/app.ico")).Stream;
            var icon = new System.Drawing.Icon(iconStream);

            this.notifyIcon_main = new System.Windows.Forms.NotifyIcon();
            this.notifyIcon_main.BalloonTipText = "xfxfn";
            this.notifyIcon_main.BalloonTipTitle = "xfgn";
            this.notifyIcon_main.Click += new EventHandler(notifyIcon_Click);
            this.notifyIcon_main.Icon = icon;
            this.notifyIcon_main.Visible = true;

        }

        void notifyIcon_Click(object sender, EventArgs e)
        {
            showSettingsWindow();
        }

        #endregion

        #region Settings Window

        public void showSettingsWindow()
        {
            if (settingsWindow == null)
            {
                settingsWindow = new Settings1(mySettings, myKeystrokeConverter);
                settingsWindow.Show();
            } else
            {
                settingsWindow.Activate();
            }
        }

        public void onSettingsWindowClosed()
        {
            settingsWindow = null;
        }

        #endregion

        #region Welcome Window

        public void showWelcomeWindow()
        {
            if (welcomeWindow == null)
            {
                welcomeWindow = new Welcome(mySettings);
                welcomeWindow.Show();
            }
            else
            {
                welcomeWindow.Activate();
            }
        }

        public void onWelcomeWindowClosed()
        {
            welcomeWindow = null;
        }

        #endregion

        #region Shortcut

        public string StandbyShortcut;

        void m_KeystrokeEvent(KeystrokeEventArgs e)
        {
            string pressed = e.ShortcutIdentifier();
            e.raw.preventDefault = e.raw.preventDefault || CheckForTrigger(pressed);
        }

        private bool CheckForTrigger(string pressed)
        {
            if (StandbyShortcut != null && pressed == StandbyShortcut)
            {
                mySettings.Standby = !mySettings.Standby;
                return true;
            }
            return false;
        }

        public void SetStandbyShortcut(string shortcut)
        {
            if (KeystrokeDisplay.ValidateShortcutSetting(shortcut))
            {
                StandbyShortcut = shortcut;
            }
            else
            {
                StandbyShortcut = mySettings.StandbyShortcutDefault;
            }
        }

        #endregion

        #region OnSettingChanged

        private void OnSettingChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "EnableCursorIndicator":
                    OnCursorIndicatorSettingChanged();
                    break;
                case "ButtonIndicator":
                    OnButtonIndicatorSettingChanged();
                    break;
                case "EnableKeystrokeHistory":
                    OnKeystrokeHistorySettingChanged();
                    break;
                case "EnableAnnotateLine":
                    OnAnnotateLineSettingChanged();
                    break;
                case "ButtonIndicatorCustomIconsFolder":
                case "ButtonIndicatorUseCustomIcons":
                    if (mySettings.ButtonIndicatorUseCustomIcons)
                    {
                        ImageResources.ReloadRessources(mySettings.ButtonIndicatorCustomIconsFolder);
                    } else
                    {
                        ImageResources.ReloadRessources(null);
                    }
                    break;
                case "StandbyShortcut":
                    SetStandbyShortcut(mySettings.StandbyShortcut);
                    break;
                case "Standby":
                    OnCursorIndicatorSettingChanged();
                    OnButtonIndicatorSettingChanged();
                    OnKeystrokeHistorySettingChanged();
                    OnAnnotateLineSettingChanged();
                    break;
            }
        }

        #endregion

        #region Keystroke History

        KeystrokeDisplay KeystrokeHistoryWindow;
        bool KeystrokeHistoryVisible;

        private void OnKeystrokeHistorySettingChanged()
        {
            if (mySettings.EnableKeystrokeHistory && !mySettings.Standby)
            {
                EnableKeystrokeHistory();
            }
            else
            {
                DisableKeystrokeHistory();
            }
        }

        private void EnableKeystrokeHistory()
        {
            if (KeystrokeHistoryVisible || KeystrokeHistoryWindow != null)
                return;
            KeystrokeHistoryVisible = true; // Prevent Recursive call
            KeystrokeHistoryWindow = new KeystrokeDisplay(myKeystrokeConverter, mySettings);
            KeystrokeHistoryWindow.Show();
        }

        private void DisableKeystrokeHistory()
        {
            KeystrokeHistoryVisible = false;
            if (KeystrokeHistoryWindow == null)
                return;
            KeystrokeHistoryWindow.Close();
            KeystrokeHistoryWindow = null;
        }

        #endregion

        #region Button Indicator

        ButtonIndicator1 ButtonIndicatorWindow = null;

        private void OnButtonIndicatorSettingChanged()
        {
            if (mySettings.ButtonIndicator != ButtonIndicatorType.Disabled && !mySettings.Standby)
            {
                EnableButtonIndicator();
            }
            else
            {
                DisableButtonIndicator();
            }
        }


        private void EnableButtonIndicator()
        {
            if (ButtonIndicatorWindow != null)
                return;
            Log.e("BI", "EnableButtonIndicator");
            EnableMouseHook();
            ButtonIndicatorWindow = new ButtonIndicator1(myMouseHook, myKeystrokeConverter, mySettings);
            ButtonIndicatorWindow.Show();
        }


        private void DisableButtonIndicator()
        {
            if (ButtonIndicatorWindow == null)
                return;
            ButtonIndicatorWindow.Close();
            ButtonIndicatorWindow = null;
            DisableMouseHookIfNotNeeded();
            Log.e("BI", "DisableButtonIndicator");
        }

        #endregion

        #region Cursor Indicator

        CursorIndicator1 CursorIndicatorWindow = null;

        private void OnCursorIndicatorSettingChanged()
        {
            if (mySettings.EnableCursorIndicator && !mySettings.Standby)
            {
                EnableCursorIndicator();
            }
            else
            {
                DisableCursorIndicator();
            }
        }

        
        private void EnableCursorIndicator()
        {
            if (CursorIndicatorWindow != null)
                return;
            Log.e("CI", "EnableCursorIndicator");
            EnableMouseHook();
            CursorIndicatorWindow = new CursorIndicator1(myMouseHook, mySettings);
            CursorIndicatorWindow.Show();
        }



        private void DisableCursorIndicator()
        {
            if (CursorIndicatorWindow == null)
                return;
            CursorIndicatorWindow.Close();
            CursorIndicatorWindow = null;
            DisableMouseHookIfNotNeeded();
            Log.e("CI", "DisableCursorIndicator");
        }


        #endregion

        #region Annotate Line

        AnnotateLine AnnotateLineWindow;


        private void OnAnnotateLineSettingChanged()
        {
            if (mySettings.EnableAnnotateLine && !mySettings.Standby)
            {
                EnableAnnotateLine();
            }
            else
            {
                DisableAnnotateLine();
            }
        }

        private void EnableAnnotateLine()
        {
            if (AnnotateLineWindow != null)
                return;
            Log.e("AL", "EnableAnnotateLineWindow");
            EnableMouseHook();
            AnnotateLineWindow = new AnnotateLine(myMouseHook, myKeystrokeConverter, mySettings);
            AnnotateLineWindow.Show();
        }



        private void DisableAnnotateLine()
        {
            if (AnnotateLineWindow == null)
                return;
            AnnotateLineWindow.Close();
            AnnotateLineWindow = null;
            DisableMouseHookIfNotNeeded();
            Log.e("AL", "DisableAnnotateLineWindow");
        }

        #endregion

        #region Mouse Hook

        IMouseRawEventProvider myMouseHook = null;

        private void EnableMouseHook()
        {
            if (myMouseHook != null)
                return;
            myMouseHook = new MouseHook(mySettings);
        }

        private void DisableMouseHookIfNotNeeded()
        {
            if (CursorIndicatorWindow == null && ButtonIndicatorWindow == null && AnnotateLineWindow == null)
                DisableMouseHook();
        }

        private void DisableMouseHook()
        {
            if (myMouseHook == null)
                return;
            myMouseHook.Dispose();
            myMouseHook = null;
        }

        #endregion
    }
}


================================================
FILE: KeyNStroke/ButtonIndicator1.Designer.cs
================================================
namespace KeyNStroke
{
    partial class ButtonIndicator1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // ButtonIndicator
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 24F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(521, 482);
            this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6);
            this.Name = "ButtonIndicator";
            this.ShowInTaskbar = false;
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "CursorIndicator";
            this.Load += new System.EventHandler(this.ButtonIndicator_Load);
            this.ResumeLayout(false);

        }

        #endregion



    }
}

================================================
FILE: KeyNStroke/ButtonIndicator1.cs
================================================
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using KeyNStroke;

namespace KeyNStroke
{
    public partial class ButtonIndicator1 : Form
    {
        IMouseRawEventProvider m;
        IKeystrokeEventProvider k;
        SettingsStore s;
        ImageResources.ComposeOptions c;
        Size bitmapsize;

        public ButtonIndicator1(IMouseRawEventProvider m, IKeystrokeEventProvider k, SettingsStore s)
        {
            InitializeComponent();
            StartImageResizeThread();

            this.m = m;
            this.s = s;
            this.k = k;
            FormClosed += ButtonIndicator_FormClosed;

            HideMouseIfNoButtonPressed();

            c.dpi = 96;
            UpdatePosition(NativeMethodsMouse.CursorPosition);

            NativeMethodsWindow.SetWindowTopMost(this.Handle);
            SetFormStyles();

            m.MouseEvent += m_MouseEvent;
            k.KeystrokeEvent += k_KeystrokeEvent;
            s.PropertyChanged += settingChanged;
            DoubleClickIconTimer.Tick += leftDoubleClickIconTimeout_Tick;
            DoubleClickIconTimer.Interval = 750;

            WheelIconTimer.Interval = 750;
            WheelIconTimer.Tick += WheelIconTimer_Tick;

            UpdateSize(); // will call Redraw() after 200ms and scaling the images
            //Redraw();

        }

        private void Redraw()
        {
            Bitmap scaledAndComposedBitmap = ImageResources.Compose(c);
            this.bitmapsize = scaledAndComposedBitmap.Size;
            IntPtr handle;
            try
            {
                handle = this.Handle; // May be already disposed if leftDoubleClickIconTimeout triggers
            }
            catch (System.ObjectDisposedException)
            {
                return; // throw;
            }

            NativeMethodsDC.SetBitmapForWindow(handle,
                                               this.Location,
                                               scaledAndComposedBitmap,
                                               1.0f);   // opacity
            NativeMethodsWindow.PrintDpiAwarenessInfo();
        }

        private void ButtonIndicator_Load(object sender, EventArgs e)
        {
            Log.e("SOME", "ButtonIndicator => Load");

            RecalcOffset();
            UpdateSize();
        }

        private void k_KeystrokeEvent(KeystrokeEventArgs e)
        {
            if (s == null) return;
            bool changed = false;
            if (s.ButtonIndicatorShowModifiers)
            {
                if (e.StrokeType == KeystrokeType.Modifiers)
                {
                    if (e.Shift != c.addMShift) changed = true;
                    c.addMShift = e.Shift;
                    if (e.Ctrl != c.addMCtrl) changed = true;
                    c.addMCtrl = e.Ctrl;
                    if (e.Alt != c.addMAlt) changed = true;
                    c.addMAlt = e.Alt;
                    if (e.Win != c.addMWin) changed = true;
                    c.addMWin = e.Win;
                }
            }
            else
            {
                if (c.addMShift) changed = true;
                c.addMShift = false;
                if (c.addMCtrl) changed = true;
                c.addMCtrl = false;
                if (c.addMAlt) changed = true;
                c.addMAlt = false;
                if (c.addMWin) changed = true;
                c.addMWin = false;
            }
            if (changed)
            {
                Redraw();
            }
        }

        MouseRawEventArgs lastDblClk;

        void m_MouseEvent(MouseRawEventArgs raw_e)
        {
            if (s == null) return;
            switch (raw_e.Action)
            {
                case MouseAction.Up:
                    HideButton(raw_e);
                    break;
                case MouseAction.Down:
                    ShowButton(raw_e.Button);
                    break;
                case MouseAction.DblClk:
                    lastDblClk = raw_e;
                    IndicateDoubleClick(raw_e.Button);
                    break;
                case MouseAction.Move:
                    UpdatePosition(raw_e.Position);
                    break;
                case MouseAction.Wheel:
                    IndicateWheel(raw_e);
                    break;
                default:
                    break;
            }
        }


        System.Windows.Forms.Timer WheelIconTimer = new System.Windows.Forms.Timer();

        private void IndicateWheel(MouseRawEventArgs raw_e)
        {
            c.addBMouse = true;
            Log.e("WHEEL", "Display " + raw_e.wheelDelta.ToString());
            WheelIconTimer.Stop();
            WheelIconTimer.Start();
            if (raw_e.wheelDelta < 0)
            {
                c.addBWheelDown = true;
                c.addBWheelUp = false;
            }
            else if (raw_e.wheelDelta > 0)
            {
                c.addBWheelUp = true;
                c.addBWheelDown = false;
            }
            Redraw();
        }

        void WheelIconTimer_Tick(object sender, EventArgs e)
        {
            WheelIconTimer.Stop();
            Log.e("WHEEL", "Hide ");
            c.addBWheelDown = false;
            c.addBWheelUp = false;
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        private void IndicateDoubleClick(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.LButton:
                    c.addBMouse = true;
                    c.addBLeft = false;
                    c.addBLeftDouble = true;
                    doubleClickReleased = false;
                    Redraw();
                    break;
                case MouseButton.RButton:
                    break;
                default:
                    break;
            }
        }

        void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)
        {
            ((System.Windows.Forms.Timer)sender).Stop();
            c.addBLeftDouble = false;
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        private void ShowButton(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.LButton:
                    c.addBMouse = true;
                    c.addBLeft = true;
                    c.addBLeftDouble = false;
                    //UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.RButton:
                    c.addBMouse = true;
                    c.addBRight = true;
                    //UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.MButton:
                    c.addBMouse = true;
                    c.addBMiddle = true;
                    //UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.XButton:
                    break;
                case MouseButton.None:
                    break;
                default:
                    break;
            }
        }

        System.Windows.Forms.Timer DoubleClickIconTimer = new System.Windows.Forms.Timer();
        bool doubleClickReleased = true;

        private void HideButton(MouseRawEventArgs raw_e)
        {
            switch (raw_e.Button)
            {
                case MouseButton.LButton:
                    c.addBLeft = false;
                    doubleClickReleased = true;
                    if (c.addBLeftDouble)
                    {
                        if (raw_e.Msllhookstruct.time - lastDblClk.Msllhookstruct.time > DoubleClickIconTimer.Interval)
                        {
                            c.addBLeftDouble = false;
                        }
                        else
                        {
                            DoubleClickIconTimer.Stop();
                            DoubleClickIconTimer.Start();
                        }
                    }
                    break;
                case MouseButton.RButton:
                    c.addBRight = false;
                    break;
                case MouseButton.MButton:
                    c.addBMiddle = false;
                    break;
                case MouseButton.XButton:
                    break;
                case MouseButton.None:
                    break;
                default:
                    break;
            }
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        void HideMouseIfNoButtonPressed()
        {
            if (   !c.addBLeft
                && !c.addBRight
                && !c.addBMiddle
                && !c.addBLeftDouble
                && !c.addBRightDouble
                && !c.addBWheelDown
                && !c.addBWheelUp)
            {
                c.addBMouse = false;
            }
        }

        void ButtonIndicator_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (m != null)
                m.MouseEvent -= m_MouseEvent;
            if (s != null)
                s.PropertyChanged -= settingChanged;
            if (k != null)
                k.KeystrokeEvent -= k_KeystrokeEvent;
            if (newScalingFactors != null)
                newScalingFactors.CompleteAdding();
            m = null;
            s = null;
        }

        void SetFormStyles()
        {
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            //this.Opacity = 0.8;
            NativeMethodsGWL.ClickThrough(this.Handle); // This also makes the window style WS_EX_LAYERED
            NativeMethodsGWL.HideFromAltTab(this.Handle);

            UpdateSize();
            UpdatePosition(NativeMethodsMouse.CursorPosition);
        }


        Thread imagesResizeThread;
        BlockingCollection<double> newScalingFactors = new BlockingCollection<double>(new ConcurrentQueue<double>());

        private void StartImageResizeThread()
        {
            imagesResizeThread = new Thread(new ThreadStart(BackgroundResizeImages));
            imagesResizeThread.Start();
        }


        void BackgroundResizeImages()
        {
            while (true)
            {
                try
                {
                    double scalingFactor = newScalingFactors.Take(); // Blocks until at least one new Item is available
                    Thread.Sleep(200); // wait for more data, so we don't do too eager calculations
                    // skip all elements except for the last
                    while (newScalingFactors.Count > 0)
                    {
                        scalingFactor = newScalingFactors.Take();
                    }
                    Log.e("BI", $"size change applied: {scalingFactor}");
                    ImageResources.ApplyScalingFactor(scalingFactor);
                    this.Invoke((Action) (() =>
                    {
                        Redraw();
                        UpdatePosition(NativeMethodsMouse.CursorPosition);
                    }));
                }
                catch (InvalidOperationException)
                {
                    // Window closed
                    Log.e("BI", "BackgrounResizeThread stopped");
                    return;
                }
            }
        }


        void UpdateSize()
        {
            newScalingFactors.Add(s.ButtonIndicatorScaling);
        }

        Size offset = new Size(0, 0);

        void RecalcOffset()
        {
            offset.Width = (int)(s.ButtonIndicatorPositionDistance * Math.Sin(s.ButtonIndicatorPositionAngle / 10.0f));
            offset.Height = (int)(s.ButtonIndicatorPositionDistance * Math.Cos(s.ButtonIndicatorPositionAngle / 10.0f));
        }

        void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)
        {
            if (OnlyDblClkIconVisible() && doubleClickReleased)
                return;
            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);
            uint adpiX = 0, adpiY = 0;
            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);
            c.dpi = adpiX;
            NativeMethodsWindow.SetWindowPosition(this.Handle, cursorPosition.X - this.bitmapsize.Width/2 + offset.Width, cursorPosition.Y - this.bitmapsize.Height/2 + offset.Height);

        }

        private bool OnlyDblClkIconVisible()
        {
            return !c.addBLeft
                && !c.addBRight
                && !c.addBMiddle
                && !c.addBWheelUp
                && !c.addBWheelDown
                && (c.addBLeftDouble
               || c.addBRightDouble);
        }

        private void settingChanged(object sender, PropertyChangedEventArgs e)
        {
            if (s == null) return;
            // Log.e("BI", $"ButtonIndicator => settingChanged {e.PropertyName}");
            switch (e.PropertyName)
            {
                case "ButtonIndicator":
                    break;
                case "ButtonIndicatorPositionAngle":
                    RecalcOffset();
                    UpdatePosition(NativeMethodsMouse.CursorPosition);
                    break;
                case "ButtonIndicatorPositionDistance":
                    RecalcOffset();
                    UpdatePosition(NativeMethodsMouse.CursorPosition);
                    break;
                case "ButtonIndicatorScaling":
                    UpdateSize();
                    break;
            }
        }


    }
}


================================================
FILE: KeyNStroke/ButtonIndicator1.resx
================================================
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 
    
    Version 2.0
    
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    
    Example:
    
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
                
    There are any number of "resheader" rows that contain simple 
    name/value pairs.
    
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    
    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>

================================================
FILE: KeyNStroke/ButtonIndicator2.xaml
================================================
<Window x:Class="KeyNStroke.ButtonIndicator2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:KeyNStroke"
        mc:Ignorable="d"
        Title="ButtonIndicator" Height="100" Width="100"
        AllowsTransparency="True" 
        WindowStyle='None' 
        Background="Transparent" ResizeMode="NoResize" ShowInTaskbar="False" IsEnabled="False">
    <Ellipse Width="100" Height="100" Fill="Red" Opacity="0.5"/>
</Window>


================================================
FILE: KeyNStroke/ButtonIndicator2.xaml.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace KeyNStroke
{
    /// <summary>
    /// Interaktionslogik für ButtonIndicator2.xaml
    /// </summary>
    public partial class ButtonIndicator2 : Window
    {

        IMouseRawEventProvider m;
        SettingsStore s;
        ImageResources.ComposeOptions c;
        IntPtr handle;

        public ButtonIndicator2(IMouseRawEventProvider m, SettingsStore s)
        {
            InitializeComponent();
            this.m = m;
            this.s = s;
            this.Closed += this.Window_Closed;
            this.Loaded += Window_Loaded;
            

        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.handle = new WindowInteropHelper(this).Handle;
            HideMouseIfNoButtonPressed();

            c.dpi = 96;
            UpdatePosition();

            NativeMethodsWindow.SetWindowTopMost(handle);
            SetFormStyles();

            m.MouseEvent += m_MouseEvent;
            s.PropertyChanged += settingChanged;
            DoubleClickIconTimer.Tick += leftDoubleClickIconTimeout_Tick;
            DoubleClickIconTimer.Interval = TimeSpan.FromMilliseconds(750.0);

            WheelIconTimer.Interval = TimeSpan.FromMilliseconds(750.0);
            WheelIconTimer.Tick += WheelIconTimer_Tick;


            Redraw();
        }


        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
        }

        private void Redraw()
        {
            Bitmap scaledAndComposedBitmap = ImageResources.Compose(c);
            Log.e("DPI", $"DPI at Redraw: {c.dpi}");
            NativeMethodsDC.SetBitmapForWindow(this.handle,
                                               new System.Drawing.Point((int)this.Left, (int)this.Top),
                                               scaledAndComposedBitmap,
                                               1.0f);   // opacity
        }

        private void ButtonIndicator_Load(object sender, EventArgs e)
        {
            Log.e("SOME", "ButtonIndicator => Load");

            RecalcOffset();
            UpdateSize();
        }

        //System.Drawing.Point cursorPosition;
        MouseRawEventArgs lastDblClk;

        void m_MouseEvent(MouseRawEventArgs raw_e)
        {
            // cursorPosition = raw_e.Position; this position does not work nice with multiple monitor/dpi setups
            switch (raw_e.Action)
            {
                case MouseAction.Up:
                    HideButton(raw_e);
                    break;
                case MouseAction.Down:
                    ShowButton(raw_e.Button);
                    break;
                case MouseAction.DblClk:
                    lastDblClk = raw_e;
                    IndicateDoubleClick(raw_e.Button);
                    break;
                case MouseAction.Move:
                    UpdatePosition();
                    break;
                case MouseAction.Wheel:
                    IndicateWheel(raw_e);
                    break;
                default:
                    break;
            }
        }

        DispatcherTimer WheelIconTimer = new DispatcherTimer();

        private void IndicateWheel(MouseRawEventArgs raw_e)
        {
            c.addBMouse = true;
            Log.e("WHEEL", "Display " + raw_e.wheelDelta.ToString());
            WheelIconTimer.Stop();
            WheelIconTimer.Start();
            if (raw_e.wheelDelta < 0)
            {
                c.addBWheelDown = true;
                c.addBWheelUp = false;
            }
            else if (raw_e.wheelDelta > 0)
            {
                c.addBWheelUp = true;
                c.addBWheelDown = false;
            }
            Redraw();
        }

        void WheelIconTimer_Tick(object sender, EventArgs e)
        {
            WheelIconTimer.Stop();
            Log.e("WHEEL", "Hide ");
            c.addBWheelDown = false;
            c.addBWheelUp = false;
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        private void IndicateDoubleClick(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.LButton:
                    c.addBMouse = true;
                    c.addBLeft = false;
                    c.addBLeftDouble = true;
                    doubleClickReleased = false;
                    Redraw();
                    break;
                case MouseButton.RButton:
                    break;
                default:
                    break;
            }
        }

        void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)
        {
            ((DispatcherTimer)sender).Stop();
            c.addBLeftDouble = false;
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        private void ShowButton(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.LButton:
                    c.addBMouse = true;
                    c.addBLeft = true;
                    c.addBLeftDouble = false;
                    UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.RButton:
                    c.addBMouse = true;
                    c.addBRight = true;
                    UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.MButton:
                    c.addBMouse = true;
                    c.addBMiddle = true;
                    UpdatePosition();
                    Redraw();
                    break;
                case MouseButton.XButton:
                    break;
                case MouseButton.None:
                    break;
                default:
                    break;
            }
        }

        DispatcherTimer DoubleClickIconTimer = new DispatcherTimer();
        bool doubleClickReleased = true;

        private void HideButton(MouseRawEventArgs raw_e)
        {
            switch (raw_e.Button)
            {
                case MouseButton.LButton:
                    c.addBLeft = false;
                    doubleClickReleased = true;
                    if (c.addBLeftDouble)
                    {
                        if (raw_e.Msllhookstruct.time - lastDblClk.Msllhookstruct.time > DoubleClickIconTimer.Interval.TotalMilliseconds)
                        {
                            c.addBLeftDouble = false;
                        }
                        else
                        {
                            DoubleClickIconTimer.Stop();
                            DoubleClickIconTimer.Start();
                        }
                    }
                    break;
                case MouseButton.RButton:
                    c.addBRight = false;
                    break;
                case MouseButton.MButton:
                    c.addBMiddle = false;
                    break;
                case MouseButton.XButton:
                    break;
                case MouseButton.None:
                    break;
                default:
                    break;
            }
            HideMouseIfNoButtonPressed();
            Redraw();
        }

        void HideMouseIfNoButtonPressed()
        {
            if (!c.addBLeft
                && !c.addBRight
                && !c.addBMiddle
                && !c.addBLeftDouble
                && !c.addBRightDouble
                && !c.addBWheelDown
                && !c.addBWheelUp)
            {
                c.addBMouse = false;
            }
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            if (m != null)
                m.MouseEvent -= m_MouseEvent;
            if (s != null)
                s.PropertyChanged -= settingChanged;
            m = null;
            s = null;
        }

        void SetFormStyles()
        {
            //this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            //this.Opacity = 0.8;
            NativeMethodsGWL.ClickThrough(this.handle); // This also makes the window style WS_EX_LAYERED
            NativeMethodsGWL.HideFromAltTab(this.handle);

            UpdateSize();
            UpdatePosition();
        }



        void UpdateSize()
        {
            ImageResources.ApplyScalingFactor(s.ButtonIndicatorScalingPercentage / 100.0f);
            Log.e("BI", "size change");
            Redraw();
        }

        System.Drawing.Size offset = new System.Drawing.Size(0, 0); // dpi independend

        void RecalcOffset()
        {
            offset.Width = (int)(s.ButtonIndicatorPositionDistance * Math.Sin(s.ButtonIndicatorPositionAngle / 10.0f));
            offset.Height = (int)(s.ButtonIndicatorPositionDistance * Math.Cos(s.ButtonIndicatorPositionAngle / 10.0f));
        }

        void UpdatePosition()
        {
            if (OnlyDblClkIconVisible() && doubleClickReleased)
                return;
            NativeMethodsMouse.POINT cursorPosition = new NativeMethodsMouse.POINT(0, 0);
            NativeMethodsWindow.GetCursorPos(ref cursorPosition);
            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);

            // Angular DPI seems to work best
            //uint edpiX = 0, edpiY = 0;
            //NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref edpiX, ref edpiY);
            uint adpiX = 0, adpiY = 0;
            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY);
            //uint rdpiX = 0, rdpiY = 0;
            //NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_RAW_DPI, ref rdpiX, ref rdpiY);

            c.dpi = adpiX;


            NativeMethodsWindow.SetWindowPosition(this.handle, cursorPosition.X, cursorPosition.Y);

        }

        private bool OnlyDblClkIconVisible()
        {
            return !c.addBLeft
                && !c.addBRight
                && !c.addBMiddle
                && !c.addBWheelUp
                && !c.addBWheelDown
                && (c.addBLeftDouble
               || c.addBRightDouble);
        }

        private void settingChanged(object sender, PropertyChangedEventArgs e)
        {
            Log.e("SOME", "ButtonIndicator => settingChanged");
            switch (e.PropertyName)
            {
                case "ButtonIndicator":
                    break;
                case "ButtonIndicatorPositionAngle":
                    RecalcOffset();
                    UpdatePosition();
                    break;
                case "ButtonIndicatorPositionDistance":
                    RecalcOffset();
                    UpdatePosition();
                    break;
                case "ButtonIndicatorScalingPercentage":
                    UpdateSize();
                    UpdatePosition();
                    break;
            }
        }

    }
}


================================================
FILE: KeyNStroke/CursorIndicator1.xaml
================================================
<Window x:Class="KeyNStroke.CursorIndicator1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:KeyNStroke"
        mc:Ignorable="d"
        Title="CursorIndicator1"
        Height="450"
        Width="800"
        Loaded="Window_Loaded"
        Closed="Window_Closed"
        AllowsTransparency="True"
        Topmost="True"
        ShowInTaskbar="False"
        WindowStyle="None"
        ResizeMode="NoResize"
        Background="Transparent">
    <Ellipse x:Name="circle"
             Fill="#7FEF7E7E"></Ellipse>
</Window>


================================================
FILE: KeyNStroke/CursorIndicator1.xaml.cs
================================================
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using static KeyNStroke.NativeMethodsMouse;

namespace KeyNStroke
{
    /// <summary>
    /// Interaktionslogik für CursorIndicator1.xaml
    /// </summary>
    public partial class CursorIndicator1 : Window
    {
        IMouseRawEventProvider m;
        SettingsStore s;
        IntPtr windowHandle;
        bool isHidden;
        bool isDown;

        public CursorIndicator1(IMouseRawEventProvider m, SettingsStore s)
        {
            InitializeComponent();

            this.m = m;
            this.s = s;

            s.PropertyChanged += settingChanged;
            this.isHidden = false;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            m.MouseEvent += m_MouseEvent;
            m.CursorEvent += m_CursorEvent;
            windowHandle = new WindowInteropHelper(this).Handle;
            SetFormStyles();
        }

        void m_MouseEvent(MouseRawEventArgs raw_e)
        {
            if (raw_e.Action == MouseAction.Move)
            {
                UpdatePosition(raw_e.Position);
            }
            else if (raw_e.Action == MouseAction.Down || raw_e.Action == MouseAction.DblClk)
            {
                isDown = true;
                UpdateColor();
            }
            else if (raw_e.Action == MouseAction.Up)
            {
                isDown = false;
                UpdateColor();
            }
        }

        void m_CursorEvent(bool visible)
        {
            if (visible)
            {
                if (isHidden)
                {
                    this.Show();
                    isHidden = false;
                }
            }
            else
            {
                if (!isHidden)
                {
                    this.Hide();
                    isHidden = true;
                }
            }
        }

        void SetFormStyles()
        {
            //this.Opacity = s.CursorIndicatorOpacity;
            Log.e("CI", $"WindowHandle={windowHandle}");
            NativeMethodsGWL.ClickThrough(windowHandle);
            NativeMethodsGWL.HideFromAltTab(windowHandle);

            UpdateSize();
            UpdateColor();
            UpdatePosition(NativeMethodsMouse.CursorPosition);
        }

        void UpdateSize()
        {
            this.Width = s.CursorIndicatorSize;
            this.Height = s.CursorIndicatorSize;
        }

        void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)
        {
            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);
            uint adpiX = 0, adpiY = 0;
            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);
            Log.e("CI", $"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={cursorPosition.X} cy={cursorPosition.Y}");
            NativeMethodsWindow.SetWindowPosition(windowHandle, 
                    (int)(cursorPosition.X - (this.ActualWidth / 2) * (double)adpiX / 96.0),
                    (int)(cursorPosition.Y - (this.ActualHeight / 2) * (double)adpiY / 96.0));
        }

        private void settingChanged(object sender, PropertyChangedEventArgs e)
        {
            this.Dispatcher.BeginInvoke((Action) (() =>
            {
                if (s == null) return;
                switch (e.PropertyName)
                {
                    case "EnableCursorIndicator":
                        break;
                    case "CursorIndicatorOpacity":
                        UpdateColor();
                        break;
                    case "CursorIndicatorSize":
                        UpdateSize();
                        break;
                    case "CursorIndicatorColor":
                        UpdateColor();
                        break;
                    case "CursorIndicatorEdgeColor":
                        UpdateColor();
                        break;
                    case "CursorIndicatorEdgeStrokeThickness":
                        UpdateColor();
                        break;
                    case "CursorIndicatorDrawEdge":
                        UpdateColor();
                        break;
                }
            }));
        }

        private void UpdateColor()
        {
            Color c;
            if (isDown && s.CursorIndicatorFlashOnClick)
            {
                c = UIHelper.ToMediaColor(s.CursorIndicatorClickColor);
            }
            else
            {
                c = UIHelper.ToMediaColor(s.CursorIndicatorColor);
            }
            circle.Fill = new SolidColorBrush(Color.FromArgb((byte)(255 * (1 - s.CursorIndicatorOpacity)), c.R, c.G, c.B));
            if (s.CursorIndicatorDrawEdge)
            {
                circle.Stroke = new SolidColorBrush(UIHelper.ToMediaColor(s.CursorIndicatorEdgeColor));
                circle.StrokeThickness = s.CursorIndicatorEdgeStrokeThickness;
            }
            else
            {
                circle.Stroke = null;
                circle.StrokeThickness = 0;
            }
        }


        private void Window_Closed(object sender, EventArgs e)
        {
            if (m != null)
            {
                m.MouseEvent -= m_MouseEvent;
                m.CursorEvent -= m_CursorEvent;
            }
            if (s != null)
                s.PropertyChanged -= settingChanged;
            m = null;
            s = null;
        }
    }
}


================================================
FILE: KeyNStroke/EnumBindingSourceExtention.cs
================================================
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;

namespace KeyNStroke
{
    public class EnumBindingSourceExtention : MarkupExtension
    {
        public Type EnumType { get; private set; }

        public EnumBindingSourceExtention(Type enumType)
        {
            if (enumType == null || !enumType.IsEnum)
                throw new Exception("enumType must be of type enum");
            this.EnumType = enumType;
        }

        public static T GetAttributeOfType<T>(Enum enumVal) where T : System.Attribute
        {
            var type = enumVal.GetType();
            var memInfo = type.GetMember(Enum.GetName(type, enumVal)); // type.GetMember(enumVal.ToString());
            var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
            return (attributes.Length > 0) ? (T)attributes[0] : null;
        }

        public static string GetAttributeDescription(Enum enumValue)
        {
            var attribute = GetAttributeOfType<System.ComponentModel.DescriptionAttribute>(enumValue);
            return attribute == null ? String.Empty : attribute.Description;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            Array variants = Enum.GetValues(EnumType);
            List<ComboBoxItem> b = new List<ComboBoxItem>(variants.Length);
            foreach (var a in variants)
            {
                ComboBoxItem i = new ComboBoxItem();
                i.Content = GetAttributeDescription((Enum)a); // new TextBlock(new Run("Hi"));
                i.Tag = a;
                b.Add(i);
            }
            return b;
        }

    }
}


================================================
FILE: KeyNStroke/FodyWeavers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Costura />
</Weavers>

================================================
FILE: KeyNStroke/FodyWeavers.xsd
================================================
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
  <xs:element name="Weavers">
    <xs:complexType>
      <xs:all>
        <xs:element name="Costura" minOccurs="0" maxOccurs="1">
          <xs:complexType>
            <xs:all>
              <xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of (.NET Core) runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of (.NET Core) runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
                <xs:annotation>
                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
            </xs:all>
            <xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>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.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Controls if (.NET Core) runtime assemblies are also embedded.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="DisableCompression" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="DisableCleanup" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="LoadAtModuleInit" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>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.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>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.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="ExcludeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of (.NET Core) runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of (.NET Core) runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="Unmanaged32Assemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="Unmanaged64Assemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="PreloadOrder" type="xs:string">
              <xs:annotation>
                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
          </xs:complexType>
        </xs:element>
      </xs:all>
      <xs:attribute name="VerifyAssembly" type="xs:boolean">
        <xs:annotation>
          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="VerifyIgnoreCodes" type="xs:string">
        <xs:annotation>
          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="GenerateXsd" type="xs:boolean">
        <xs:annotation>
          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
    </xs:complexType>
  </xs:element>
</xs:schema>

================================================
FILE: KeyNStroke/ImageResources.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using KeyNStroke;
using System.Windows.Forms.VisualStyles;
using System.Windows;

namespace KeyNStroke
{
    public class ImageResources
    {
        static Assembly _assembly;

        private struct BitmapCollection
        {
            public Bitmap BMouse;
            public Bitmap BLeft;
            public Bitmap BRight;
            public Bitmap BMiddle;
            public Bitmap BLeftDouble;
            public Bitmap BRightDouble;
            public Bitmap BWheelUp;
            public Bitmap BWheelDown;
            public Bitmap MCtrl;
            public Bitmap MWin;
            public Bitmap MAlt;
            public Bitmap MShift;
        }

        static Dictionary<uint, BitmapCollection> ScaledByDpi;
        static BitmapCollection Orig; // original size

        public static void Init(string customIconFolder)
        {
            try
            {
                _assembly = Assembly.GetExecutingAssembly();

                foreach (string i in _assembly.GetManifestResourceNames())
                {
                    Log.e("RES", i);
                }

                ReloadRessources(customIconFolder);
            }
            catch
            {
                Log.e("RES", "Error accessing resources!");
            }

            ApplyScalingFactor(1.0f);
        }

        public static void ExportBuiltinRessources(string exportFolder)
        {
            string[] filenames = { "mouse.svg",
                                   "mouse.png",
                                   "mouse_left.png",
                                   "mouse_right.png",
                                   "mouse_middle.png",
                                   "mouse_left_double.png",
                                   "mouse_right_double.png",
                                   "mouse_wheel_up.png",
                                   "mouse_wheel_down.png",
                                   "mouse_modifier_ctrl.png",
                                   "mouse_modifier_win.png",
                                   "mouse_modifier_alt.png",
                                   "mouse_modifier_shift.png" };
            foreach (string png_name in filenames)
            {
                try
                {

                    string target = Path.Combine(exportFolder, png_name);
                    if (File.Exists(target))
                    {
                        var result = MessageBox.Show($"Overwrite {png_name}?", $"File already exists", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
                        if (result == MessageBoxResult.Cancel)
                        {
                            return;
                        }
                        else if (result == MessageBoxResult.No)
                        {
                            continue;
                        }
                        else
                        {
                            // Yes -> Overwrite
                        }
                    }
                    using (var fs = new FileStream(target, FileMode.Create, FileAccess.Write))
                    {
                        _assembly.GetManifestResourceStream($"KeyNStroke.Resources.{png_name}").CopyTo(fs);
                    }
                }
                catch (Exception)
                {

                }
            }
        }


        public static void ReloadRessources(string customIconFolder)
        {
            // FIXME: This happens three times on startup. 

            Orig.BMouse = LoadRessource(customIconFolder, "mouse.png");
            Orig.BLeft = LoadRessource(customIconFolder, "mouse_left.png");
            Orig.BRight = LoadRessource(customIconFolder, "mouse_right.png");
            Orig.BMiddle = LoadRessource(customIconFolder, "mouse_middle.png");
            Orig.BLeftDouble = LoadRessource(customIconFolder, "mouse_left_double.png");
            Orig.BRightDouble = LoadRessource(customIconFolder, "mouse_right_double.png");
            Orig.BWheelUp = LoadRessource(customIconFolder, "mouse_wheel_up.png");
            Orig.BWheelDown = LoadRessource(customIconFolder, "mouse_wheel_down.png");
            Orig.MCtrl = LoadRessource(customIconFolder, "mouse_modifier_ctrl.png");
            Orig.MWin = LoadRessource(customIconFolder, "mouse_modifier_win.png");
            Orig.MAlt = LoadRessource(customIconFolder, "mouse_modifier_alt.png");
            Orig.MShift = LoadRessource(customIconFolder, "mouse_modifier_shift.png");

            RefreshScalingCache();
        }

        private static Bitmap LoadRessource(string customIconFolder, string png_name)
        {
            if (customIconFolder != null)
            {
                try
                {
                    string customIconPath = Path.Combine(customIconFolder, png_name);
                    if (File.Exists(customIconPath))
                    {
                        using (var fs = new FileStream(customIconPath, FileMode.Open, FileAccess.Read))
                        {
                            return new Bitmap(fs);
                        }
                    }
                }
                catch (Exception)
                {

                }
            }
            return new Bitmap(_assembly.GetManifestResourceStream($"KeyNStroke.Resources.{png_name}"));
        }


        static double appliedScalingFactor = -1.0;

        public static void ApplyScalingFactor(double scalingfactor)
        {
            scalingfactor = Math.Min(2f, Math.Max(0.1f, scalingfactor));

            if (appliedScalingFactor != scalingfactor)
            {
                appliedScalingFactor = scalingfactor;
                RefreshScalingCache();
            }
        }

        private static void RefreshScalingCache()
        {
            if (appliedScalingFactor == -1.0)
                return;

            var newByDpi = new Dictionary<uint, BitmapCollection>();

            List<uint> dpis = NativeMethodsWindow.GetAllUsedDpis();

            foreach (uint dpi in dpis)
            {
                if (!newByDpi.ContainsKey(dpi)) { 
                    newByDpi.Add(dpi, CreateScaledBitmapCollection((float)appliedScalingFactor, dpi));
                }
            }

            ScaledByDpi = newByDpi;
            lastComposedBitmap = null; // Force regeneration of Image
        }


        private static BitmapCollection CreateScaledBitmapCollection(float scalingFactor, uint dpi)
        {
            BitmapCollection scaled = new BitmapCollection
            {
                BMouse = Scale(scalingFactor, Orig.BMouse, dpi),
                BLeft = Scale(scalingFactor, Orig.BLeft, dpi),
                BRight = Scale(scalingFactor, Orig.BRight, dpi),
                BMiddle = Scale(scalingFactor, Orig.BMiddle, dpi),
                BLeftDouble = Scale(scalingFactor, Orig.BLeftDouble, dpi),
                BRightDouble = Scale(scalingFactor, Orig.BRightDouble, dpi),
                BWheelUp = Scale(scalingFactor, Orig.BWheelUp, dpi),
                BWheelDown = Scale(scalingFactor, Orig.BWheelDown, dpi),
                MCtrl = Scale(scalingFactor, Orig.MCtrl, dpi),
                MWin = Scale(scalingFactor, Orig.MWin, dpi),
                MAlt = Scale(scalingFactor, Orig.MAlt, dpi),
                MShift = Scale(scalingFactor, Orig.MShift, dpi)
            };

            return scaled;
        }

        private static Bitmap Scale(float scalingFactor, Bitmap original, uint dpi)
        {
            var dpiScale = (float)dpi / 96.0f;
            scalingFactor *= dpiScale;
            var scaledWidth = (int)(original.Width * scalingFactor);
            Log.e("DPI", $"Scale: DPI: {dpi}, dpiScaleFactor:{dpiScale}, scaledWidth:{scaledWidth}, totalScalingFactor:{scalingFactor}");
            var scaledHeight = (int)(original.Height * scalingFactor);
            var scaledBitmap = new Bitmap(scaledWidth, scaledHeight, PixelFormat.Format32bppArgb);
            // Draw original image onto new bitmap and interpolate
            Graphics graph = Graphics.FromImage(scaledBitmap);
            graph.InterpolationMode = InterpolationMode.High;
            graph.CompositingQuality = CompositingQuality.HighQuality;
            graph.SmoothingMode = SmoothingMode.AntiAlias;
            graph.DrawImage(original, new Rectangle(0, 0, scaledWidth, scaledHeight));
            return scaledBitmap;
        }

        public struct ComposeOptions
        {
            public uint dpi;
            public bool addBMouse;
            public bool addBLeft;
            public bool addBRight;
            public bool addBMiddle;
            public bool addBLeftDouble;
            public bool addBRightDouble;
            public bool addBWheelUp;
            public bool addBWheelDown;
            public bool addMCtrl;
            public bool addMAlt;
            public bool addMWin;
            public bool addMShift;

            public static bool operator ==(ComposeOptions first, ComposeOptions second)
            {
                return Equals(first, second);
            }

            public static bool operator !=(ComposeOptions first, ComposeOptions second)
            {
                return !(first == second);
            }

            public override string ToString()
            {
                String s = "";
                if (addBMouse) s += "Mouse ";
                if (addBLeft) s += "Left ";
                if (addBRight) s += "Right ";
                if (addBMiddle) s += "Middle ";
                if (addBLeftDouble) s += "LeftDouble ";
                if (addBRightDouble) s += "RightDouble ";
                if (addBWheelUp) s += "WheelUp ";
                if (addBWheelDown) s += "WheelDown ";
                if (addMCtrl) s += "Ctrl ";
                if (addMAlt) s += "Alt ";
                if (addMWin) s += "Win ";
                if (addMShift) s += "Shift ";
                return s;
            }

            public override bool Equals(object obj)
            {
                return base.Equals(obj);
            }

            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }

        static ComposeOptions lastComposeOptions;
        static Bitmap lastComposedBitmap;


        public static Bitmap Compose(ComposeOptions c)
        {

            if (lastComposedBitmap != null && (c == lastComposeOptions))
            {
                return lastComposedBitmap;
            }



            var byDpi = ScaledByDpi; // take reference to prevent datarace with update/replace logic on scaling factor change
            if (!byDpi.ContainsKey(c.dpi))
            {
                byDpi.Add(c.dpi, CreateScaledBitmapCollection((float)appliedScalingFactor, c.dpi));
            }

            BitmapCollection scaled = byDpi[c.dpi];
            Log.e("DPI", $"COMPOSE! {c.dpi}, {c}, {scaled.BMouse.Size.Width}");

            var targetBitmap = new Bitmap(scaled.BMouse.Size.Width, scaled.BMouse.Size.Height, PixelFormat.Format32bppArgb);
            Graphics graph = Graphics.FromImage(targetBitmap);

            if (c.addBMouse) graph.DrawImageUnscaled(scaled.BMouse, 0, 0);
            if (c.addBLeft) graph.DrawImageUnscaled(scaled.BLeft, 0, 0);
            if (c.addBRight) graph.DrawImageUnscaled(scaled.BRight, 0, 0);
            if (c.addBMiddle) graph.DrawImageUnscaled(scaled.BMiddle, 0, 0);
            if (c.addBLeftDouble) graph.DrawImageUnscaled(scaled.BLeftDouble, 0, 0);
            if (c.addBRightDouble) graph.DrawImageUnscaled(scaled.BRightDouble, 0, 0);
            if (c.addBWheelUp) graph.DrawImageUnscaled(scaled.BWheelUp, 0, 0);
            if (c.addBWheelDown) graph.DrawImageUnscaled(scaled.BWheelDown, 0, 0);
            if (c.addMCtrl) graph.DrawImageUnscaled(scaled.MCtrl, 0, 0);
            if (c.addMWin) graph.DrawImageUnscaled(scaled.MWin, 0, 0);
            if (c.addMAlt) graph.DrawImageUnscaled(scaled.MAlt, 0, 0);
            if (c.addMShift) graph.DrawImageUnscaled(scaled.MShift, 0, 0);

            lastComposeOptions = c;
            lastComposedBitmap = targetBitmap;
            return targetBitmap;
        }
    }
}


================================================
FILE: KeyNStroke/KeyNStroke.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\packages\Costura.Fody.5.0.2\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.5.0.2\build\Costura.Fody.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{BA117557-FEBC-45C5-8895-4017D05C3DCD}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <RootNamespace>KeyNStroke</RootNamespace>
    <AssemblyName>Key-n-Stroke</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WarningLevel>4</WarningLevel>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
    <IsWebBootstrapper>false</IsWebBootstrapper>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <PublishUrl>publish\</PublishUrl>
    <Install>true</Install>
    <InstallFrom>Disk</InstallFrom>
    <UpdateEnabled>false</UpdateEnabled>
    <UpdateMode>Foreground</UpdateMode>
    <UpdateInterval>7</UpdateInterval>
    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
    <UpdatePeriodically>false</UpdatePeriodically>
    <UpdateRequired>false</UpdateRequired>
    <MapFileExtensions>true</MapFileExtensions>
    <ApplicationRevision>0</ApplicationRevision>
    <ApplicationVersion>1.2.0.%2a</ApplicationVersion>
    <UseApplicationTrust>false</UseApplicationTrust>
    <BootstrapperEnabled>true</BootstrapperEnabled>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup>
    <StartupObject>KeyNStroke.App</StartupObject>
  </PropertyGroup>
  <PropertyGroup>
    <ApplicationManifest>app.manifest</ApplicationManifest>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Costura, Version=5.0.2.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\packages\Costura.Fody.5.0.2\lib\netstandard1.0\Costura.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.ComponentModel.Composition" />
    <Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
    </Reference>
    <Reference Include="System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Drawing" />
    <Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
      <HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.IO.Compression.FileSystem" />
    <Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
      <HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Net" />
    <Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Numerics" />
    <Reference Include="System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Runtime.Serialization" />
    <Reference Include="System.Security" />
    <Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="System.Web" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Xaml">
      <RequiredTargetFramework>4.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
      <Private>True</Private>
      <Private>True</Private>
    </Reference>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="WpfColorFontDialog, Version=1.0.7.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\packages\WpfColorFontDialog.1.0.7\lib\net40\WpfColorFontDialog.dll</HintPath>
      <EmbedInteropTypes>False</EmbedInteropTypes>
    </Reference>
    <Reference Include="Xceed.Wpf.Toolkit, Version=3.8.0.0, Culture=neutral, PublicKeyToken=3e4669d2f30244f4, processorArchitecture=MSIL">
      <HintPath>..\packages\Extended.Wpf.Toolkit.3.8.1\lib\net40\Xceed.Wpf.Toolkit.dll</HintPath>
      <EmbedInteropTypes>False</EmbedInteropTypes>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AnnotateLine.xaml.cs">
      <DependentUpon>AnnotateLine.xaml</DependentUpon>
    </Compile>
    <Compile Include="CursorIndicator1.xaml.cs">
      <DependentUpon>CursorIndicator1.xaml</DependentUpon>
    </Compile>
    <Compile Include="EnumBindingSourceExtention.cs" />
    <Compile Include="NativeMethodsEvents.cs" />
    <Compile Include="ReadShortcut.xaml.cs">
      <DependentUpon>ReadShortcut.xaml</DependentUpon>
    </Compile>
    <Compile Include="ResizeButton.xaml.cs">
      <DependentUpon>ResizeButton.xaml</DependentUpon>
    </Compile>
    <Compile Include="Updater\Updater.cs" />
    <Compile Include="Updater\Admininstration.cs" />
    <Compile Include="Updater\Utils.cs" />
    <Compile Include="Updater\Statemachine.cs" />
    <Compile Include="Welcome.xaml.cs">
      <DependentUpon>Welcome.xaml</DependentUpon>
    </Compile>
    <Page Include="AnnotateLine.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="App.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Compile Include="App.xaml.cs">
      <DependentUpon>App.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="ButtonIndicator1.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="ButtonIndicator1.Designer.cs">
      <DependentUpon>ButtonIndicator1.cs</DependentUpon>
    </Compile>
    <Compile Include="ImageResources.cs" />
    <Compile Include="KeyboardHook.cs" />
    <Compile Include="KeyboardLayoutParser.cs" />
    <Compile Include="KeyboardRawEvent.cs" />
    <Compile Include="KeystrokeDisplay.xaml.cs">
      <DependentUpon>KeystrokeDisplay.xaml</DependentUpon>
    </Compile>
    <Compile Include="KeystrokeEvent.cs" />
    <Compile Include="KeystrokeParser.cs" />
    <Compile Include="LabeledSlider.xaml.cs">
      <DependentUpon>LabeledSlider.xaml</DependentUpon>
    </Compile>
    <Compile Include="Log.cs" />
    <Compile Include="MouseHook.cs" />
    <Compile Include="MouseRawEvent.cs" />
    <Compile Include="NativeMethodsDC.cs" />
    <Compile Include="NativeMethodsGWL.cs" />
    <Compile Include="NativeMethodsKeyboard.cs" />
    <Compile Include="NativeMethodsMouse.cs" />
    <Compile Include="NativeMethodsWindow.cs" />
    <Compile Include="Settings1.xaml.cs">
      <DependentUpon>Settings1.xaml</DependentUpon>
    </Compile>
    <Compile Include="SettingsStore.cs" />
    <Compile Include="SpecialkeysParser.cs" />
    <Compile Include="TweenLabel.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="UIHelper.cs" />
    <Compile Include="UrlOpener.cs" />
    <Page Include="CursorIndicator1.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="ReadShortcut.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Themes\Generic.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="ResizeButton.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Welcome.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
    <EmbeddedResource Include="ButtonIndicator1.resx">
      <DependentUpon>ButtonIndicator1.cs</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="app.manifest" />
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <Page Include="KeystrokeDisplay.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="LabeledSlider.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Settings1.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </ItemGroup>
  <ItemGroup>
    <BootstrapperPackage Include=".NETFramework,Version=v4.8">
      <Visible>False</Visible>
      <ProductName>Microsoft .NET Framework 4.8 %28x86 and x64%29</ProductName>
      <Install>true</Install>
    </BootstrapperPackage>
    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
      <Visible>False</Visible>
      <ProductName>.NET Framework 3.5 SP1</ProductName>
      <Install>false</Install>
    </BootstrapperPackage>
  </ItemGroup>
  <ItemGroup>
    <Resource Include="app.ico" />
  </ItemGroup>
  <ItemGroup>
    <WCFMetadata Include="Connected Services\" />
  </ItemGroup>
  <ItemGroup>
    <Resource Include="Resources\updateKey.pub.xml" />
    <Resource Include="Resources\app.ico" />
    <EmbeddedResource Include="Resources\mouse.png" />
    <EmbeddedResource Include="Resources\mouse.svg" />
    <EmbeddedResource Include="Resources\mouse_left.png" />
    <EmbeddedResource Include="Resources\mouse_left_double.png" />
    <EmbeddedResource Include="Resources\mouse_middle.png" />
    <EmbeddedResource Include="Resources\mouse_right.png" />
    <EmbeddedResource Include="Resources\mouse_right_double.png" />
    <EmbeddedResource Include="Resources\mouse_modifier_alt.png" />
    <EmbeddedResource Include="Resources\mouse_modifier_ctrl.png" />
    <EmbeddedResource Include="Resources\mouse_modifier_shift.png" />
    <EmbeddedResource Include="Resources\mouse_modifier_win.png" />
    <Content Include="Resources\mouse_test.svg" />
    <EmbeddedResource Include="Resources\mouse_wheel_down.png" />
    <EmbeddedResource Include="Resources\mouse_wheel_up.png" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Costura.Fody">
      <Version>5.0.2</Version>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Fody">
      <Version>6.3.0</Version>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\packages\Fody.6.3.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.3.0\build\Fody.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\packages\Fody.6.3.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.3.0\build\Fody.targets'))" />
    <Error Condition="!Exists('..\packages\Costura.Fody.5.0.2\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.0.2\build\Costura.Fody.props'))" />
  </Target>
</Project>

================================================
FILE: KeyNStroke/KeyboardHook.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
using KeyNStroke;

namespace KeyNStroke
{
    /// Some Parts are from 
    /// KEYBOARD.CS (c) 2006 by Emma Burrows

    /// <summary>
    /// Low-level keyboard intercept class
    /// </summary>
    public class KeyboardHook : IDisposable, IKeyboardRawEventProvider
    {
 
        #region Initializion

        /// <summary>
        /// Sets up a keyboard hook
        /// </summary>
        public KeyboardHook()
        {
            RegisterKeyboardHook();
        }

        #endregion

        #region Hook Add/Remove

        // Keyboard Hook Identifier for WinApi
        private const int WH_KEYBOARD_LL = 13;

        //Variables used in the call to SetWindowsHookEx
        private NativeMethodsKeyboard.HookHandlerDelegate proc;
        private IntPtr hookID = IntPtr.Zero;

        /// <summary>
        /// Registers the function HookCallback for the global key events winapi 
        /// </summary>
        private void RegisterKeyboardHook()
        {
            if (hookID != IntPtr.Zero)
                return;
            proc = new NativeMethodsKeyboard.HookHandlerDelegate(HookCallback);
            using (Process curProcess = Process.GetCurrentProcess())
            {
                using (ProcessModule curModule = curProcess.MainModule)
                {
                    hookID = NativeMethodsKeyboard.SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                        NativeMethodsKeyboard.GetModuleHandle(curModule.ModuleName), 0);
                }
            }
        }

        /// <summary>
        /// Unregisters the winapi hook for global key events
        /// </summary>
        private void UnregisterKeyboardHook()
        {
            if (hookID == IntPtr.Zero)
                return;
            NativeMethodsKeyboard.UnhookWindowsHookEx(hookID);
            hookID = IntPtr.Zero;
        }

        #endregion

        #region EventHandling

        //Keyboard API constants: press or release  
        private const int WM_KEYUP = 0x0101;
        private const int WM_SYSKEYUP = 0x0105;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_SYSKEYDOWN = 0x0104;

        private const int
        VK_SHIFT = 0x10,
        VK_CONTROL = 0x11,
        VK_MENU = 0x12,
        VK_CAPITAL = 0x14,
        VK_LWIN = 0x5B,
        VK_RWIN = 0x5C,
        VK_NUMLOCK = 0x90,
        VK_SCROLL = 0x91,
        VK_LSHIFT = 0xA0,
        VK_RSHIFT = 0xA1,
        VK_LCONTROL = 0xA2,
        VK_RCONTROL = 0xA3,
        VK_LMENU = 0xA4,
        VK_RMENU = 0xA5;

        /// <summary>
        /// Processes the key event captured by the hook.
        /// </summary>
        private IntPtr HookCallback(int nCode, 
                                    IntPtr wParam, 
                                    ref NativeMethodsKeyboard.KBDLLHOOKSTRUCT lParam)
        {
            if (nCode >= 0)
            {
                KeyboardRawEventArgs e = new KeyboardRawEventArgs(lParam);
                e.keyState = new byte[256];
                //NativeMethodsKeyboard.GetKeyboardState(e.keyState); probability of dataraces

                if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
                {
                    e.Method = KeyUpDown.Down;
                }
                else if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
                {
                    e.Method = KeyUpDown.Up;
                }

                if (e.Method != KeyUpDown.Undefined)
                {
                    CheckModifiers(e);
                    FixKeyStateArray(e);
                    Log.e("KE", $"EVENT scanCode={lParam.scanCode} vkCode={e.vkCode} method={e.Method} uppercase={e.Uppercase} ");
                    OnKeyEvent(e);
                }

                /*System.Diagnostics.Debug.WriteLine(
                    String.Format("Key: sc {0} vk {1} ext {2} fl {3}, {4}", lParam.scanCode,
                        lParam.vkCode, lParam.dwExtraInfo, lParam.flags, e.Method));
                */
                if (e.preventDefault)
                {
                    return IntPtr.Add(IntPtr.Zero, 1);
                } else
                {
                    return NativeMethodsKeyboard.CallNextHookEx(hookID, nCode, wParam, ref lParam);
                }
            }
            else
            {
                //Pass key to next application
                return NativeMethodsKeyboard.CallNextHookEx(hookID, nCode, wParam, ref lParam);
            }
        }

        /// <summary>
        /// Checks whether Alt, Shift, Control or CapsLock
        /// is enabled at the same time as another key.
        /// Modify the relevant sections and return type 
        /// depending on what you want to do with modifier keys.
        /// </summary>
        private void CheckModifiers(KeyboardRawEventArgs e)
        {
            e.Shift = CheckModifierDown(VK_SHIFT);
            e.Ctrl = CheckModifierDown(VK_CONTROL);
            e.Alt = CheckModifierDown(VK_MENU);
            e.Caps = CheckModifierToggled(VK_CAPITAL);
            e.LWin = CheckModifierDown(VK_LWIN);
            e.RWin = CheckModifierDown(VK_RWIN);
            e.Numlock = CheckModifierToggled(VK_NUMLOCK);
            e.Scrollock = CheckModifierToggled(VK_SCROLL);
            e.LShift = CheckModifierDown(VK_LSHIFT);
            e.RShift = CheckModifierDown(VK_RSHIFT);
            e.LCtrl = CheckModifierDown(VK_LCONTROL);
            e.RCtrl = CheckModifierDown(VK_RCONTROL);
            e.LAlt = CheckModifierDown(VK_LMENU);
            e.RAlt = CheckModifierDown(VK_RMENU);
        }

        /// <summary>
        /// Uses the winapi to check if the VK key modifiercode is pressed
        /// </summary>
        /// <param name="modifiercode"></param>
        /// <returns></returns>
        private bool CheckModifierDown(int modifiercode)
        {
            // SHORT NativeMethodsKeyboard.GetKeyState(int)
            // The return value specifies the status of the specified virtual key, as follows:

            // - If the high-order bit is 1, the key is down; otherwise, it is up.
            // - If the low-order bit is 1, the key is toggled. A key, such as 
            //   the CAPS LOCK key, is toggled if it is turned on. The key is 
            //   off and untoggled if the low-order bit is 0. A toggle key's 
            //   indicator light (if any) on the keyboard will be on when the 
            //   key is toggled, and off when the key is untoggled.
            return ((NativeMethodsKeyboard.GetKeyState(modifiercode) & 0x8000) != 0);
        }

        /// <summary>
        /// Uses the winapi to check weather the caps/numlock/scrolllock is activated
        /// </summary>
        /// <param name="modifiercode"></param>
        /// <returns></returns>
        private bool CheckModifierToggled(int modifiercode)
        {
            return (NativeMethodsKeyboard.GetKeyState(modifiercode) & 0x0001) != 0;
        }


        private void FixKeyStateArray(KeyboardRawEventArgs e)
        {
            Log.e("KP", "FixKeyStateArray()");
            if (e.Uppercase)
            {
                e.keyState[VK_SHIFT] = 129;
            } else
            {
                e.keyState[VK_SHIFT] = 0;
            }
            //return;
#pragma warning disable CS0162 // Unerreichbarer Code wurde entdeckt.
            e.keyState[VK_CONTROL] = (byte)(e.Ctrl ? 129 : 0);
#pragma warning restore CS0162 // Unerreichbarer Code wurde entdeckt.
            e.keyState[VK_MENU] = (byte)(e.Alt ? 129 : 0);
            e.keyState[VK_CAPITAL] = (byte)(e.Caps ? 129 : 0);
            e.keyState[VK_LWIN] = (byte)(e.LWin ? 129 : 0);
            e.keyState[VK_RWIN] = (byte)(e.RWin ? 129 : 0);
            e.keyState[VK_NUMLOCK] = (byte)(e.Numlock ? 129 : 0);
            e.keyState[VK_SCROLL] = (byte)(e.Scrollock ? 129 : 0);
            e.keyState[VK_LSHIFT] = (byte)(e.LShift ? 129 : 0);
            e.keyState[VK_RSHIFT] = (byte)(e.RShift ? 129 : 0);
            e.keyState[VK_LCONTROL] = (byte)(e.LCtrl ? 129 : 0);
            e.keyState[VK_RCONTROL] = (byte)(e.RCtrl ? 129 : 0);
            e.keyState[VK_LMENU] = (byte)(e.LAlt ? 129 : 0);
            e.keyState[VK_RMENU] = (byte)(e.RAlt ? 129 : 0);
        }

        #endregion

        #region Event Forwarding

        /// <summary>
        /// Fires if key is pressed or released.
        /// </summary>
        public event KeyboardRawEventHandler KeyEvent;
        
        /// <summary>
        /// Raises the KeyEvent event.
        /// </summary>
        /// <param name="e">An instance of KeyboardRawEvent</param>
        public void OnKeyEvent(KeyboardRawEventArgs e)
        {
            if (KeyEvent != null)
                KeyEvent(e);
        }

        #endregion

        #region Finalizing and Disposing

        ~KeyboardHook()
        {
            Log.e("HOOK", "~KeyboardHook");
            UnregisterKeyboardHook();
        }

        /// <summary>
        /// Releases the keyboard hook.
        /// </summary>
        public void Dispose()
        {
            UnregisterKeyboardHook();
        }
        #endregion


    }

}


================================================
FILE: KeyNStroke/KeyboardLayoutParser.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using KeyNStroke;

namespace KeyNStroke
{
    public class KeyboardLayoutParser
    {
        public static string Parse(KeyboardRawEventArgs e)
        {
            StringBuilder sb = new StringBuilder(128);
            int lParam = 0;
            // Bits in lParam
            // 16-23	Scan code.
            // 24	Extended-key flag. Distinguishes some keys on an enhanced keyboard.
            // 25	"Do not care" bit. The application calling this function sets this 
            //      bit to indicate that the function should not distinguish between left 
            //      and right CTRL and SHIFT keys, for example.

            lParam = e.Kbdllhookstruct.scanCode << 16;

            int result = NativeMethodsKeyboard.GetKeyNameText(lParam, sb, 128);
            return sb.ToString();
        }

        public static string ParseViaMapKeycode(KeyboardRawEventArgs e)
        {
            uint r = NativeMethodsKeyboard.MapVirtualKey((uint)e.vkCode, 
                                                    NativeMethodsKeyboard.MAPVK_VK_TO_CHAR);
            return ((char)r).ToString();
        }


        public static string ParseViaToAscii(KeyboardRawEventArgs e)
        {
            byte[] inBuffer = new byte[2];
            int buffertype = NativeMethodsKeyboard.ToAscii(e.vkCode,
                        e.Kbdllhookstruct.scanCode,
                        e.keyState,
                        inBuffer,
                        e.Alt ? 1 : 0);

            if (buffertype < 0) // deadkey
            {

            }
            else if (buffertype == 1) // one char in inBuffer[0]
            {
                char key = (char)inBuffer[0];
                return key.ToString();
            }
            else if (buffertype == 2) // two chars in inBuffer
            {
                char key = (char)inBuffer[0];
                char key2 = (char)inBuffer[1];
                return key.ToString() + key2.ToString();
            }
            else if (buffertype == 0)
            {
                // no translation
            }
            return "";
        }


        public static string ParseViaToUnicode(KeyboardRawEventArgs e)
        {
            StringBuilder inBuffer = new StringBuilder(128);
            int buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode,
                        e.Kbdllhookstruct.scanCode,
                        e.keyState,
                        inBuffer,
                        128,
                        0); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */
            Log.e("KP",
                    String.Format("   ParseViaToUnicode(): First call to ToUnicode: returned={0} translated='{1}' alt={2} ctrl={3} vk={4}", buffertype,
                        inBuffer.ToString(), e.Alt, e.Ctrl, e.vkCode));
            string keystate = "";
            for (int i = 0; i < e.keyState.Length; i++ )
            {
                if(e.keyState[i] != 0)
                {
                    keystate += " " + ((WindowsVirtualKey) i).ToString() + ":" + e.keyState[i];

                }
            }

            Log.e("KP", "   ParseViaToUnicode(): Key state: " + keystate);

            // call ToUnicode again, otherwise it will destoy the dead key for the rest of the system
            int buffertype2 = NativeMethodsKeyboard.ToUnicode(e.vkCode,
                e.Kbdllhookstruct.scanCode,
                e.keyState,
                inBuffer,
                128,
                0); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */

            Log.e("KP",
                    String.Format("   ParseViaToUnicode(): Secnd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype2,
                        inBuffer.ToString(), e.Alt, e.vkCode));

            if (buffertype < 0) // deadkey
            {
                // return DEADKEY, so the next key can try to assemble the deadkey
                //return "DEADKEY";
                return buffertype2 >= 1 ? inBuffer.ToString(0, 1) : "";
            }
            else if(buffertype2 < 0) // type two dead keys in a row
            {
                Log.e("KP", "   ParseViaToUnicode(): TwoDeadKeysInARow " + buffertype2.ToString() + " & deadkey");
                return buffertype >= 1 ? inBuffer.ToString(0, 1) : "";
            }
            else if (buffertype2 >= 1) // buffertype chars in inBuffer[0..buffertype]
            {
                string out_ = inBuffer.ToString(0, buffertype2);
                if (out_ == "\b") // Backspace is no text
                {
                    return "";
                }
                return out_;
            }
            else if (buffertype2 == 0)
            {
                // no translation
            }
            return "";
        }

        public static string ProcessDeadkeyWithNextKey(KeyboardRawEventArgs dead, KeyboardRawEventArgs e)
        {
            Log.e("KP", "   ProcessDeadkeyWithNextKey()");
            StringBuilder inBuffer = new StringBuilder(128);
            int buffertype = NativeMethodsKeyboard.ToUnicode(dead.vkCode,
                        dead.Kbdllhookstruct.scanCode,
                        dead.keyState,
                        inBuffer,
                        128,
                        4); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */

            Log.e("KP",
                    String.Format("   ProcessDeadkeyWithNextKey(): First call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype,
                        inBuffer.ToString(), e.Alt, e.vkCode));
            buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode,
                e.Kbdllhookstruct.scanCode,
                e.keyState,
                inBuffer,
                128,
                4); /* 4 == "don't change keyboard state" (Windows 10 version 1607 and higher) */
            Log.e("KP",
                    String.Format("   ProcessDeadkeyWithNextKey(): Sednd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}", buffertype,
                        inBuffer.ToString(), e.Alt, e.vkCode));

            if (buffertype >= 1) // buffertype chars in inBuffer[0..buffertype]
            {
                return inBuffer.ToString(0, buffertype);
            }
            else if (buffertype == 0)
            {
                // no translation
            }
            return "";
        }

        /*
        int convertVirtualKeyToWChar(int virtualKey, PWCHAR outputChar, PWCHAR deadChar)
        {
            int i = 0;
            short state = 0;
            int capsLock;
            int shift = -1;
            int mod = 0;
            int charCount = 0;
            WCHAR baseChar;
            WCHAR diacritic;
            *outputChar = 0;
            capsLock = (GetKeyState(VK_CAPITAL) & 0x1);
            do
            {
                state = GetAsyncKeyState(pgCharModifiers->pVkToBit[i].Vk);
                if (pgCharModifiers->pVkToBit[i].Vk == VK_SHIFT)
                    shift = i + 1; // Get modification number for Shift key
                if (state & ~SHRT_MAX)
                {
                    if (mod == 0)
                        mod = i + 1;
                    else
                        mod = 0; // Two modifiers at the same time!
                }
                i++;
            }
            while (pgCharModifiers->pVkToBit[i].Vk != 0);

            SEARCH_VK_IN_CONVERSION_TABLE(1)
            SEARCH_VK_IN_CONVERSION_TABLE(2)
            SEARCH_VK_IN_CONVERSION_TABLE(3)
            SEARCH_VK_IN_CONVERSION_TABLE(4)
            SEARCH_VK_IN_CONVERSION_TABLE(5)
            SEARCH_VK_IN_CONVERSION_TABLE(6)
            SEARCH_VK_IN_CONVERSION_TABLE(7)
            SEARCH_VK_IN_CONVERSION_TABLE(8)
            SEARCH_VK_IN_CONVERSION_TABLE(9)
            SEARCH_VK_IN_CONVERSION_TABLE(10)

            if (*deadChar != 0) // I see dead characters...
            {
                i = 0;
                do
                {
                    baseChar = (WCHAR) pgDeadKey[i].dwBoth;
                    diacritic = (WCHAR) (pgDeadKey[i].dwBoth >> 16);
                    if ((baseChar == *outputChar) && (diacritic == *deadChar))
                    {
                        *deadChar = 0;
                        *outputChar = (WCHAR) pgDeadKey[i].wchComposed;
                    }
                    i++;
                }
                while (pgDeadKey[i].dwBoth != 0);
            }
            return charCount;
            }*/
      }
}


================================================
FILE: KeyNStroke/KeyboardRawEvent.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using KeyNStroke;

namespace KeyNStroke
{
    public enum KeyUpDown
    {
        Undefined,
        Up,
        Down
    }

    public class KeyboardRawEventArgs
    {
        public bool Shift;
        public bool LShift;
        public bool RShift;
        public bool Ctrl;
        public bool LCtrl;
        public bool RCtrl;
        public bool Caps;
        public bool LWin;
        public bool RWin;
        public bool Alt;
        public bool LAlt;
        public bool RAlt; // Alt Gr
        public bool Numlock;
        public bool Scrollock;

        public int vkCode { get { return Kbdllhookstruct.vkCode; } }
        private Key key;
        public Key Key { get {  return key; } }
        public KeyUpDown Method = KeyUpDown.Undefined;

        public bool Uppercase { get { return (Shift && !Caps) || (Caps && !Shift);  } }
        public bool OnlyShiftOrCaps { get { return !Ctrl && !LWin && !RWin && !Alt;  } }
        public bool NoModifiers { get { return !Ctrl && !LWin && !RWin && !Alt && !Shift; } }
        public bool Win { get { return LWin || RWin; } }

        public NativeMethodsKeyboard.KBDLLHOOKSTRUCT Kbdllhookstruct;
        public byte[] keyState; // 256 bytes

        public bool preventDefault = false;

        public KeyboardRawEventArgs(NativeMethodsKeyboard.KBDLLHOOKSTRUCT Kbdllhookstruct)
        {
            this.Kbdllhookstruct = Kbdllhookstruct;
            this.key = KeyInterop.KeyFromVirtualKey(this.vkCode); /* cache */
        }
    }

    public delegate void KeyboardRawEventHandler(KeyboardRawEventArgs e);

    public interface IKeyboardRawEventProvider : IDisposable
    {
        event KeyboardRawEventHandler KeyEvent;
    }

    public enum WindowsVirtualKey
    {
        [Description("Left mouse button")]
        VK_LBUTTON = 0x01,

        [Description("Right mouse button")]
        VK_RBUTTON = 0x02,

        [Description("Control-break processing")]
        VK_CANCEL = 0x03,

        [Description("Middle mouse button (three-button mouse)")]
        VK_MBUTTON = 0x04,

        [Description("X1 mouse button")]
        VK_XBUTTON1 = 0x05,

        [Description("X2 mouse button")]
        VK_XBUTTON2 = 0x06,

        [Description("BACKSPACE key")]
        VK_BACK = 0x08,

        [Description("TAB key")]
        VK_TAB = 0x09,

        [Description("CLEAR key")]
        VK_CLEAR = 0x0C,

        [Description("ENTER key")]
        VK_RETURN = 0x0D,

        [Description("SHIFT key")]
        VK_SHIFT = 0x10,

        [Description("CTRL key")]
        VK_CONTROL = 0x11,

        [Description("ALT key")]
        VK_MENU = 0x12,

        [Description("PAUSE key")]
        VK_PAUSE = 0x13,

        [Description("CAPS LOCK key")]
        VK_CAPITAL = 0x14,

        [Description("IME Kana mode")]
        VK_KANA = 0x15,

        [Description("IME Hanguel mode (maintained for compatibility; use VK_HANGUL)")]
        VK_HANGUEL = 0x15,

        [Description("IME Hangul mode")]
        VK_HANGUL = 0x15,

        [Description("IME Junja mode")]
        VK_JUNJA = 0x17,

        [Description("IME final mode")]
        VK_FINAL = 0x18,

        [Description("IME Hanja mode")]
        VK_HANJA = 0x19,

        [Description("IME Kanji mode")]
        VK_KANJI = 0x19,

        [Description("ESC key")]
        VK_ESCAPE = 0x1B,

        [Description("IME convert")]
        VK_CONVERT = 0x1C,

        [Description("IME nonconvert")]
        VK_NONCONVERT = 0x1D,

        [Description("IME accept")]
        VK_ACCEPT = 0x1E,

        [Description("IME mode change request")]
        VK_MODECHANGE = 0x1F,

        [Description("SPACEBAR")]
        VK_SPACE = 0x20,

        [Description("PAGE UP key")]
        VK_PRIOR = 0x21,

        [Description("PAGE DOWN key")]
        VK_NEXT = 0x22,

        [Description("END key")]
        VK_END = 0x23,

        [Description("HOME key")]
        VK_HOME = 0x24,

        [Description("LEFT ARROW key")]
        VK_LEFT = 0x25,

        [Description("UP ARROW key")]
        VK_UP = 0x26,

        [Description("RIGHT ARROW key")]
        VK_RIGHT = 0x27,

        [Description("DOWN ARROW key")]
        VK_DOWN = 0x28,

        [Description("SELECT key")]
        VK_SELECT = 0x29,

        [Description("PRINT key")]
        VK_PRINT = 0x2A,

        [Description("EXECUTE key")]
        VK_EXECUTE = 0x2B,

        [Description("PRINT SCREEN key")]
        VK_SNAPSHOT = 0x2C,

        [Description("INS key")]
        VK_INSERT = 0x2D,

        [Description("DEL key")]
        VK_DELETE = 0x2E,

        [Description("HELP key")]
        VK_HELP = 0x2F,

        [Description("0 key")]
        K_0 = 0x30,

        [Description("1 key")]
        K_1 = 0x31,

        [Description("2 key")]
        K_2 = 0x32,

        [Description("3 key")]
        K_3 = 0x33,

        [Description("4 key")]
        K_4 = 0x34,

        [Description("5 key")]
        K_5 = 0x35,

        [Description("6 key")]
        K_6 = 0x36,

        [Description("7 key")]
        K_7 = 0x37,

        [Description("8 key")]
        K_8 = 0x38,

        [Description("9 key")]
        K_9 = 0x39,

        [Description("A key")]
        K_A = 0x41,

        [Description("B key")]
        K_B = 0x42,

        [Description("C key")]
        K_C = 0x43,

        [Description("D key")]
        K_D = 0x44,

        [Description("E key")]
        K_E = 0x45,

        [Description("F key")]
        K_F = 0x46,

        [Description("G key")]
        K_G = 0x47,

        [Description("H key")]
        K_H = 0x48,

        [Description("I key")]
        K_I = 0x49,

        [Description("J key")]
        K_J = 0x4A,

        [Description("K key")]
        K_K = 0x4B,

        [Description("L key")]
        K_L = 0x4C,

        [Description("M key")]
        K_M = 0x4D,

        [Description("N key")]
        K_N = 0x4E,

        [Description("O key")]
        K_O = 0x4F,

        [Description("P key")]
        K_P = 0x50,

        [Description("Q key")]
        K_Q = 0x51,

        [Description("R key")]
        K_R = 0x52,

        [Description("S key")]
        K_S = 0x53,

        [Description("T key")]
        K_T = 0x54,

        [Description("U key")]
        K_U = 0x55,

        [Description("V key")]
        K_V = 0x56,

        [Description("W key")]
        K_W = 0x57,

        [Description("X key")]
        K_X = 0x58,

        [Description("Y key")]
        K_Y = 0x59,

        [Description("Z key")]
        K_Z = 0x5A,

        [Description("Left Windows key (Natural keyboard)")]
        VK_LWIN = 0x5B,

        [Description("Right Windows key (Natural keyboard)")]
        VK_RWIN = 0x5C,

        [Description("Applications key (Natural keyboard)")]
        VK_APPS = 0x5D,

        [Description("Computer Sleep key")]
        VK_SLEEP = 0x5F,

        [Description("Numeric keypad 0 key")]
        VK_NUMPAD0 = 0x60,

        [Description("Numeric keypad 1 key")]
        VK_NUMPAD1 = 0x61,

        [Description("Numeric keypad 2 key")]
        VK_NUMPAD2 = 0x62,

        [Description("Numeric keypad 3 key")]
        VK_NUMPAD3 = 0x63,

        [Description("Numeric keypad 4 key")]
        VK_NUMPAD4 = 0x64,

        [Description("Numeric keypad 5 key")]
        VK_NUMPAD5 = 0x65,

        [Description("Numeric keypad 6 key")]
        VK_NUMPAD6 = 0x66,

        [Description("Numeric keypad 7 key")]
        VK_NUMPAD7 = 0x67,

        [Description("Numeric keypad 8 key")]
        VK_NUMPAD8 = 0x68,

        [Description("Numeric keypad 9 key")]
        VK_NUMPAD9 = 0x69,

        [Description("Multiply key")]
        VK_MULTIPLY = 0x6A,

        [Description("Add key")]
        VK_ADD = 0x6B,

        [Description("Separator key")]
        VK_SEPARATOR = 0x6C,

        [Description("Subtract key")]
        VK_SUBTRACT = 0x6D,

        [Description("Decimal key")]
        VK_DECIMAL = 0x6E,

        [Description("Divide key")]
        VK_DIVIDE = 0x6F,

        [Description("F1 key")]
        VK_F1 = 0x70,

        [Description("F2 key")]
        VK_F2 = 0x71,

        [Description("F3 key")]
        VK_F3 = 0x72,

        [Description("F4 key")]
        VK_F4 = 0x73,

        [Description("F5 key")]
        VK_F5 = 0x74,

        [Description("F6 key")]
        VK_F6 = 0x75,

        [Description("F7 key")]
        VK_F7 = 0x76,

        [Description("F8 key")]
        VK_F8 = 0x77,

        [Description("F9 key")]
        VK_F9 = 0x78,

        [Description("F10 key")]
        VK_F10 = 0x79,

        [Description("F11 key")]
        VK_F11 = 0x7A,

        [Description("F12 key")]
        VK_F12 = 0x7B,

        [Description("F13 key")]
        VK_F13 = 0x7C,

        [Description("F14 key")]
        VK_F14 = 0x7D,

        [Description("F15 key")]
        VK_F15 = 0x7E,

        [Description("F16 key")]
        VK_F16 = 0x7F,

        [Description("F17 key")]
        VK_F17 = 0x80,

        [Description("F18 key")]
        VK_F18 = 0x81,

        [Description("F19 key")]
        VK_F19 = 0x82,

        [Description("F20 key")]
        VK_F20 = 0x83,

        [Description("F21 key")]
        VK_F21 = 0x84,

        [Description("F22 key")]
        VK_F22 = 0x85,

        [Description("F23 key")]
        VK_F23 = 0x86,

        [Description("F24 key")]
        VK_F24 = 0x87,

        [Description("NUM LOCK key")]
        VK_NUMLOCK = 0x90,

        [Description("SCROLL LOCK key")]
        VK_SCROLL = 0x91,

        [Description("Left SHIFT key")]
        VK_LSHIFT = 0xA0,

        [Description("Right SHIFT key")]
        VK_RSHIFT = 0xA1,

        [Description("Left CONTROL key")]
        VK_LCONTROL = 0xA2,

        [Description("Right CONTROL key")]
        VK_RCONTROL = 0xA3,

        [Description("Left MENU key")]
        VK_LMENU = 0xA4,

        [Description("Right MENU key")]
        VK_RMENU = 0xA5,

        [Description("Browser Back key")]
        VK_BROWSER_BACK = 0xA6,

        [Description("Browser Forward key")]
        VK_BROWSER_FORWARD = 0xA7,

        [Description("Browser Refresh key")]
        VK_BROWSER_REFRESH = 0xA8,

        [Description("Browser Stop key")]
        VK_BROWSER_STOP = 0xA9,

        [Description("Browser Search key")]
        VK_BROWSER_SEARCH = 0xAA,

        [Description("Browser Favorites key")]
        VK_BROWSER_FAVORITES = 0xAB,

        [Description("Browser Start and Home key")]
        VK_BROWSER_HOME = 0xAC,

        [Description("Volume Mute key")]
        VK_VOLUME_MUTE = 0xAD,

        [Description("Volume Down key")]
        VK_VOLUME_DOWN = 0xAE,

        [Description("Volume Up key")]
        VK_VOLUME_UP = 0xAF,

        [Description("Next Track key")]
        VK_MEDIA_NEXT_TRACK = 0xB0,

        [Description("Previous Track key")]
        VK_MEDIA_PREV_TRACK = 0xB1,

        [Description("Stop Media key")]
        VK_MEDIA_STOP = 0xB2,

        [Description("Play/Pause Media key")]
        VK_MEDIA_PLAY_PAUSE = 0xB3,

        [Description("Start Mail key")]
        VK_LAUNCH_MAIL = 0xB4,

        [Description("Select Media key")]
        VK_LAUNCH_MEDIA_SELECT = 0xB5,

        [Description("Start Application 1 key")]
        VK_LAUNCH_APP1 = 0xB6,

        [Description("Start Application 2 key")]
        VK_LAUNCH_APP2 = 0xB7,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key")]
        VK_OEM_1 = 0xBA,

        [Description("For any country/region, the '+' key")]
        VK_OEM_PLUS = 0xBB,

        [Description("For any country/region, the ',' key")]
        VK_OEM_COMMA = 0xBC,

        [Description("For any country/region, the '-' key")]
        VK_OEM_MINUS = 0xBD,

        [Description("For any country/region, the '.' key")]
        VK_OEM_PERIOD = 0xBE,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key")]
        VK_OEM_2 = 0xBF,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key")]
        VK_OEM_3 = 0xC0,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key")]
        VK_OEM_4 = 0xDB,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\\|' key")]
        VK_OEM_5 = 0xDC,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key")]
        VK_OEM_6 = 0xDD,

        [Description("Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key")]
        VK_OEM_7 = 0xDE,

        [Description("Used for miscellaneous characters; it can vary by keyboard.")]
        VK_OEM_8 = 0xDF,


        [Description("Either the angle bracket key or the backslash key on the RT 102-key keyboard")]
        VK_OEM_102 = 0xE2,

        [Description("IME PROCESS key")]
        VK_PROCESSKEY = 0xE5,


        [Description("Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP")]
        VK_PACKET = 0xE7,

        [Description("Attn key")]
        VK_ATTN = 0xF6,

        [Description("CrSel key")]
        VK_CRSEL = 0xF7,

        [Description("ExSel key")]
        VK_EXSEL = 0xF8,

        [Description("Erase EOF key")]
        VK_EREOF = 0xF9,

        [Description("Play key")]
        VK_PLAY = 0xFA,

        [Description("Zoom key")]
        VK_ZOOM = 0xFB,

        [Description("PA1 key")]
        VK_PA1 = 0xFD,

        [Description("Clear key")]
        VK_OEM_CLEAR = 0xFE,

    } 
}


================================================
FILE: KeyNStroke/KeystrokeDisplay.xaml
================================================
<Window x:Class="KeyNStroke.KeystrokeDisplay"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:KeyNStroke"
        mc:Ignorable="d"
        Title="KeystrokeDisplay"
        Height="450"
        Width="800"
        Closing="Window_Closing"
        ShowInTaskbar="False"
        Topmost="True"
        AllowsTransparency="True"
        WindowStyle="None"
        ResizeMode="NoResize"
        MouseUp="Window_MouseUp"
        Loaded="Window_Loaded"
        Background="Transparent">
    <Grid x:Name="backgroundGrid"
          Background="Black"
          MouseDown="BackgroundGrid_MouseDown">

        <local:ResizeButton x:Name="buttonResizeWindow"
                            HorizontalAlignment="Right"
                            Margin="0,0,-1,-1"
                            VerticalAlignment="Bottom"
                            Width="38"
                            Height="38"
                            Panel.ZIndex="10" />

        <Button x:Name="buttonClose"
                Content="×"
                HorizontalAlignment="Right"
                Margin="0,-5,20,0"
                VerticalAlignment="Top"
                Width="45"
                FontSize="24"
                Height="38"
                Background="#FFD44A4A"
                Foreground="White"
                Panel.ZIndex="8"
                Click="ButtonClose_Click" />
        <Button x:Name="buttonSettings"
                Content="⚙"
                HorizontalAlignment="Right"
                Margin="0,-2,80,0"
                VerticalAlignment="Top"
                Width="38"
                FontSize="16"
                Height="38"
                Panel.ZIndex="8"
                Click="ButtonSettings_Click" />
        <Button x:Name="buttonLeaveSettingsMode"
                Content="Finish Move+Resize"
                HorizontalAlignment="Right"
                Margin="0,-2,140,0"
                VerticalAlignment="Top"
                Width="170"
                FontSize="16"
                Height="38"
                Panel.ZIndex="8"
                Click="ButtonLeaveSettingsMode_Click" Foreground="White" Background="#FF5CC358" />
        <Button x:Name="buttonLeavePasswordMode"
                HorizontalAlignment="Center"
                Margin="0,0,0,0"
                VerticalAlignment="Center"
                Width="338"
                FontSize="16"
                Height="119"
                Panel.ZIndex="8"
                Click="ButtonLeavePasswordMode_Click" Foreground="White" Background="#FFC3AA58">
            <TextBlock HorizontalAlignment="Left" TextAlignment="Center">
                Password Protection Mode Enabled
                <LineBreak/>
                <Span FontSize="12">To continue normally, click here or press<LineBreak/>
                <Run x:Name="PasswordProtectionModeShortcut" Text=""/>
                </Span>
            </TextBlock>
        </Button>
        <Grid x:Name="innerPanel"
              HorizontalAlignment="Left"
              Height="185"
              Margin="105,96,0,0"
              VerticalAlignment="Top"
              Width="595"
              Background="#FFA7A7A7"
              MouseDown="InnerPanel_MouseDown"
              MouseMove="InnerPanel_MouseMove"
              MouseUp="InnerPanel_MouseUp">
            <StackPanel x:Name="labelStack"
                        Margin="0"
                        VerticalAlignment="Bottom">
            </StackPanel>
            <local:ResizeButton x:Name="buttonResizeInnerPanel"
                                HorizontalAlignment="Right"
                                Margin="0,0,-1,-1"
                                VerticalAlignment="Bottom"
                                Width="38"
                                Height="38"
                                ResizeTarget="Parent"
                                Panel.ZIndex="5" />
        </Grid>

    </Grid>
</Window>


================================================
FILE: KeyNStroke/KeystrokeDisplay.xaml.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace KeyNStroke
{
    /// <summary>
    /// Interaktionslogik für KeystrokeDisplay.xaml
    /// </summary>
    public partial class KeystrokeDisplay : Window
    {
        readonly SettingsStore settings;
        readonly IKeystrokeEventProvider k;
        IntPtr windowHandle;
        Brush OrigInnerPanelBackgroundColor;

        public KeystrokeDisplay(IKeystrokeEventProvider k, SettingsStore s)
        {
            InitializeComponent();
            InitializeAnimations();

            this.k = k;

            this.settings = s;
            this.settings.EnableSettingsMode = false;
            this.settings.EnablePasswordMode = false;
            this.settings.PropertyChanged += SettingChanged;
            this.settings.CallPropertyChangedForAllProperties();

            this.buttonResizeWindow.Settings = s;
            this.buttonResizeInnerPanel.Settings = s;

            //addWelcomeInfo();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Window handle is available
            InitPeriodicTopmostTimer();
            windowHandle = new WindowInteropHelper(this).Handle;

            this.k.KeystrokeEvent += KeystrokeEvent;

            OrigInnerPanelBackgroundColor = innerPanel.Background;
            ActivateDisplayOnlyMode(true);
            DeactivatePasswordProtectionMode(true);

            if (settings.EnableWindowFade)
            {
                FadeOut();
            }
        }

        #region periodically make TopMost
        readonly DispatcherTimer makeTopMostTimer = new DispatcherTimer();
        void InitPeriodicTopmostTimer()
        {
            makeTopMostTimer.Tick += (object sender, EventArgs e) =>
            {
                IntPtr handle = new WindowInteropHelper(this).Handle;
                NativeMethodsWindow.SetWindowTopMost(handle);
            };
            makeTopMostTimer.Interval = TimeSpan.FromSeconds(1.0);

            settings.PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
            {
                if (e.PropertyName == "PeriodicTopmost")
                {
                    if (settings.PeriodicTopmost)
                    {
                        makeTopMostTimer.Start();
                    }
                    else
                    {
                        makeTopMostTimer.Stop();
                    }
                }
            };
        }
        #endregion

        #region keystroke handler

        void KeystrokeEvent(KeystrokeEventArgs e)
        {
            if (settings == null) return;
            string pressed = e.ShortcutIdentifier();
            e.raw.preventDefault = e.raw.preventDefault || CheckForSettingsMode(pressed);
            e.raw.preventDefault = e.raw.preventDefault || CheckForPasswordMode(pressed);

            if (PasswordModeActivated || settings.EnablePasswordMode)
            {
                if (e.ShouldBeDisplayed)
                {
                    if (settings.EnableWindowFade && !SettingsModeActivated)
                    {
                        FadeIn();
                    }
                }
                return;
            }

            if (e.ShouldBeDisplayed)
            {
                if (settings.EnableWindowFade && !SettingsModeActivated)
                {
                    FadeIn();
                }

                if (!e.RequiresNewLine
                    && settings.KeystrokeMethod == KeystrokeMethodEnum.TextModeBackspaceCanDeleteText
                    && NumberOfDeletionsAllowed > 0
                    && LastHistoryLineIsText
                    && !LastHistoryLineRequiredNewLineAfterwards
                    && e.NoModifiers
                    && e.Key == System.Windows.Input.Key.Back)
                {
                    if (labels.Count > 0)
                    {
                        Log.e("BS", $"delete last char -> {labels[labels.Count - 1].text}");
                    }
                    Log.e("BS", "NumberOfDeletionsAllowed " + NumberOfDeletionsAllowed.ToString());
                    if (!RemoveLastChar())
                    {
                        // again
                        Log.e("BS", " failed");
                        NumberOfDeletionsAllowed = 0;
                        KeystrokeEvent(e);
                        return;
                    }
                }
                else if (e.RequiresNewLine
                    || !settings.KeystrokeMethod.IsTextMode()
                    || !AddingWouldFitInCurrentLine(e.ToString(true, false))
                    || !LastHistoryLineIsText
                    || LastHistoryLineRequiredNewLineAfterwards)
                {
                    if (settings.KeystrokeMethod == KeystrokeMethodEnum.ShortcutModeNoText && e.StrokeType == KeystrokeType.Text)
                    {
                        // nothing
                    } else
                    {
                        string e_str = e.ToString(settings.KeystrokeMethod.IsTextMode(), false);
                        AddNextLine(e_str);
                        Log.e("BS", $"new line: {e_str} -> {labels[labels.Count - 1].text}");
                        NumberOfDeletionsAllowed = e.Deletable ? 1 : 0;
                        Log.e("BS", "NumberOfDeletionsAllowed " + NumberOfDeletionsAllowed.ToString());
                    }
                }
                else
                {
                    string e_str = e.ToString(settings.KeystrokeMethod.IsTextMode(), false);
                    AddToLine(e_str);
                    Log.e("BS", $"add to line: {e_str} -> {labels[labels.Count-1].text}");
                    if (e.Deletable)
                        NumberOfDeletionsAllowed += 1;
                    else
                        NumberOfDeletionsAllowed = 0;
                    Log.e("BS", "NumberOfDeletionsAllowed " + NumberOfDeletionsAllowed.ToString());
                }

                LastHistoryLineIsText = e.StrokeType == KeystrokeType.Text;
                LastHistoryLineRequiredNewLineAfterwards = e.RequiresNewLineAfterwards;

            }
        }


        #endregion

        #region Opacity Animation

        DoubleAnimation windowOpacityAnim;
        Storyboard windowOpacitySB;

        private void InitializeAnimations()
        {
            windowOpacityAnim = new DoubleAnimation
            {
                From = 0.0,
                To = 1.0,
                Duration = new Duration(TimeSpan.FromMilliseconds(200)),
            };
            windowOpacitySB = new Storyboard();
            windowOpacitySB.Children.Add(windowOpacityAnim);
            Storyboard.SetTarget(windowOpacityAnim, this);
            Storyboard.SetTargetProperty(windowOpacityAnim, new PropertyPath(Window.OpacityProperty));
        }

        private void FadeOut()
        {
            if (!SettingsModeActivated && !PasswordModeActivated)
            {
                ToOpacity(0.0, true);
            }
        }

        private void FadeIn()
        {
            // Opacity now via background color
            ToOpacity(1.0, true);
        }

        private void FullOpacity()
        {
            ToOpacity(1.0, false);
        }

        private void ToOpacity(double targetOpacity, bool fade)
        {
            if (Math.Abs(Opacity - targetOpacity) > 0.0001)
            {
                if (fade)
                {
                    // Fixme: Animation is restarting for every single keypress on fade-in
                    windowOpacitySB.Stop();
                    windowOpacityAnim.From = this.Opacity;
                    windowOpacityAnim.To = targetOpacity;
                    Log.e("OPACITY", $"Restart Anim, from {Opacity} to {targetOpacity}.");
                    windowOpacitySB.Begin(this);
                } else
                {
                    // https://docs.microsoft.com/de-de/dotnet/framework/wpf/graphics-multimedia/how-to-set-a-property-after-animating-it-with-a-storyboard
                    this.BeginAnimation(Window.OpacityProperty, null); // Break connection between storyboard and property
                    Opacity = targetOpacity;
                    Log.e("OPACITY", $"Remov anim, to {targetOpacity}. {Opacity}");
                }
            }
        }
        #endregion




        #region settingsChanged

        private void SettingChanged(object sender, PropertyChangedEventArgs e)
        {
            if (settings == null) return;
            switch (e.PropertyName)
            {
                case "BackgroundColor":
                    backgroundGrid.Background = new SolidColorBrush(UIHelper.ToMediaColor(settings.BackgroundColor));
                    break;
                case "WindowLocation":
                    this.Left = settings.WindowLocation.X;
                    this.Top = settings.WindowLocation.Y;
                    Log.e("KD", String.Format("Apply X: {0}", settings.WindowLocation.X));
                    break;
                case "WindowSize":
                    this.Width = settings.WindowSize.Width;
                    this.Height = settings.WindowSize.Height;
                    break;
                case "PanelLocation":
                    this.innerPanel.Margin = new Thickness(settings.PanelLocation.X, settings.PanelLocation.Y, 0.0, 0.0);
                    break;
                case "PanelSize":
                    this.innerPanel.Width = settings.PanelSize.Width;
                    this.innerPanel.Height = settings.PanelSize.Height;
                    break;
                case "LabelFont":
                case "LabelColor":
                case "LabelTextAlignment":
                case "LineDistance":
                    UpdateLabelStyles();
                    break;
                case "LabelTextDirection":
                    if (labelStack.VerticalAlignment == VerticalAlignment.Top && settings.LabelTextDirection == TextDirection.Up)
                    {
                        labelStack.VerticalAlignment = VerticalAlignment.Bottom;
                        labelStack.Children.Clear();
                        for(int i = 0; i < labels.Count; i++)
                        {
                            labelStack.Children.Add(labels[i].label);
                        }
                        UpdateLabelStyles();
                    } else if (labelStack.VerticalAlignment == VerticalAlignment.Bottom && settings.LabelTextDirection == TextDirection.Down)
                    {
                        labelStack.VerticalAlignment = VerticalAlignment.Top;
                        labelStack.Children.Clear();
                        for (int i = labels.Count - 1; i >= 0; i--)
                        {
                            labelStack.Children.Add(labels[i].label);
                        }
                        UpdateLabelStyles();
                    }
                    break;
                case "HistoryLength":
                    TruncateHistory();
                    break;
                case "EnableWindowFade":
                    if (settings.EnableWindowFade && labels.Count == 0)
                    {
                        FadeOut();
                    }
                    else
                    {
                        FadeIn();
                    }
                    break;
                case "KeystrokeHistorySettingsModeShortcut":
                    SetSettingsModeShortcut(settings.KeystrokeHistorySettingsModeShortcut);
                    break;
                case "EnableSettingsMode":
                    if (settings.EnableSettingsMode)
                    {
                        ActivateSettingsMode();
                    } else
                    {
                        ActivateDisplayOnlyMode(false);
                    }
                    break;
                case "KeystrokeHistoryPasswordModeShortcut":
                    SetPasswordModeShortcut(settings.KeystrokeHistoryPasswordModeShortcut);
                    break;
                case "EnablePasswordMode":
                    if (settings.EnablePasswordMode)
                    {
                        ActivatePasswordProtectionMode();
                    }
                    else
                    {
                        DeactivatePasswordProtectionMode(false);
                    }
                    break;
            }
        }

        #endregion


        #region Settings Mode

        public string SettingsModeShortcut;

        public void SetSettingsModeShortcut(string shortcut)
        {
            if (ValidateShortcutSetting(shortcut))
            {
                SettingsModeShortcut = shortcut;
            }
            else
            {
                SettingsModeShortcut = settings.KeystrokeHistorySettingsModeShortcutDefault;
            }
        }

        public static bool ValidateShortcutSetting(string shortcut)
        {
            if (shortcut == null)
                return false;
            if (shortcut.Length == 0)
                return false;

            // The last key must not be Ctrl/Alt/Win/Shift, and there must be multiple keys
            string[] keys = shortcut.Split('+'); // There are spaces around the +, but Split() only accepts chars
            if (keys.Length < 2)
                return false;
            if (keys.Last().Contains("Ctrl"))
                return false;
            if (keys.Last().Contains("Alt"))
                return false;
            if (keys.Last().Contains("Shift"))
                return false;
            if (keys.Last().Contains("Win"))
                return false;

            return true;
        }

        private bool CheckForSettingsMode(string pressed)
        {
            if (SettingsModeShortcut != null && pressed == SettingsModeShortcut)
            {
                settings.EnableSettingsMode = !settings.EnableSettingsMode;
                return true;
            }
            return false;
        }

        bool SettingsModeActivated = false;

        void ActivateDisplayOnlyMode(bool force)
        {
            if (SettingsModeActivated || force)
            {
                FadeIn();

                if (!PasswordModeActivated)
                {
                    NativeMethodsGWL.ClickThrough(this.windowHandle);
                }
                NativeMethodsGWL.HideFromAltTab(this.windowHandle);

                InnerPanelIsDragging = false;

                buttonClose.Visibility = Visibility.Hidden;
                buttonResizeInnerPanel.Visibility = Visibility.Hidden;
                buttonResizeWindow.Visibility = Visibility.Hidden;
                buttonSettings.Visibility = Visibility.Hidden;
                buttonLeaveSettingsMode.Visibility = Visibility.Hidden;
                innerPanel.Background = new SolidColorBrush(Color.FromArgb(0,0,0,0));
                backgroundGrid.Background = new SolidColorBrush(UIHelper.ToMediaColor(settings.BackgroundColor));

                SettingsModeActivated = false;

                settings.SaveAll();
            }
        }

        void ActivateSettingsMode()
        {
            if (!SettingsModeActivated)
            {
                NativeMethodsGWL.CatchClicks(this.windowHandle);

                FullOpacity();

                InnerPanelIsDragging = false;

                buttonClose.Visibility = Visibility.Visible;
                buttonResizeInnerPanel.Visibility = Visibility.Visible;
                buttonResizeWindow.Visibility = Visibility.Visible;
                buttonSettings.Visibility = Visibility.Visible;
                buttonLeaveSettingsMode.Visibility = Visibility.Visible;
                innerPanel.Background = OrigInnerPanelBackgroundColor;
                backgroundGrid.Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));

                foreach (LabelData d in labels)
                {
                    d.label.Visibility = Visibility.Hidden;
                }

                SettingsModeActivated = true;
            }
        }



        #endregion

        #region Password Protection Mode

        public string PasswordModeShortcut;

        public void SetPasswordModeShortcut(string shortcut)
        {
            if (ValidateShortcutSetting(shortcut))
            {
                PasswordModeShortcut = shortcut;
            }
            else
            {
                PasswordModeShortcut = settings.KeystrokeHistoryPasswordModeShortcutDefault;
            }
            PasswordProtectionModeShortcut.Text = PasswordModeShortcut;
        }


        private bool CheckForPasswordMode(string pressed)
        {
            if (PasswordModeShortcut != null && pressed == PasswordModeShortcut)
            {
                settings.EnablePasswordMode = !settings.EnablePasswordMode;
                return true;
            }
            return false;
        }


        private void ButtonLeavePasswordMode_Click(object sender, RoutedEventArgs e)
        {
            settings.EnablePasswordMode = false;
        }

        bool PasswordModeActivated = false;

        void ActivatePasswordProtectionMode()
        {
            if (!PasswordModeActivated)
            {
                NativeMethodsGWL.CatchClicks(this.windowHandle);

                buttonLeavePasswordMode.Visibility = Visibility.Visible;
                PasswordModeActivated = true;
            }
        }

        void DeactivatePasswordProtectionMode(bool force)
        {
            if (PasswordModeActivated || force)
            {
                if (!SettingsModeActivated)
                {
                    NativeMethodsGWL.ClickThrough(this.windowHandle);
                    if (settings.EnableWindowFade && labels.Count == 0)
                    {
                        FadeOut();
                    }
                }

                buttonLeavePasswordMode.Visibility = Visibility.Hidden;
                PasswordModeActivated = false;
            }
        }


        #endregion

        #region Dragging of Window and innerPanel

        private void BackgroundGrid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (this.SettingsModeActivated)
            {
                this.DragMove();
                e.Handled = true;
            }

        }

        private void Window_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (this.SettingsModeActivated)
            {
                settings.WindowLocation = new Point(this.Left, this.Top);
            }
        }

        private bool InnerPanelIsDragging = false;
        private Point InnerPanelDragStartCursorPosition;
        private Point InnerPanelDragStartPosition;

        private void InnerPanel_MouseDown(object sender, MouseButtonEventArgs e)
        {
            InnerPanelDragStartCursorPosition = e.GetPosition(this);
            InnerPanelIsDragging = true;
            InnerPanelDragStartPosition = new Point(innerPanel.Margin.Left, innerPanel.Margin.Top);
            e.Handled = true;
        }

        private void InnerPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (InnerPanelIsDragging)
            {
                Point current = e.GetPosition(this);
                Point diff = new Point(
                    current.X - InnerPanelDragStartCursorPosition.X,
                    current.Y - InnerPanelDragStartCursorPosition.Y);
                innerPanel.Margin = new Thickness(InnerPanelDragStartPosition.X + diff.X,
                    InnerPanelDragStartPosition.Y + diff.Y,
                    0,
                    0);
            }
            e.Handled = true;
        }

        private void InnerPanel_MouseUp(object sender, MouseButtonEventArgs e)
        {
            InnerPanelIsDragging = false;
            settings.PanelLocation = new Point(innerPanel.Margin.Left, innerPanel.Margin.Top);
        }


        #endregion

        #region Button Click  and Window Close Events

        private void ButtonSettings_Click(object sender, RoutedEventArgs e)
        {
            ((App)Application.Current).showSettingsWindow();
        }

        private void ButtonClose_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Application.Current.Shutdown();
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {

        }

        private void ButtonLeaveSettingsMode_Click(object sender, RoutedEventArgs e)
        {
            settings.EnableSettingsMode = false;
        }

        #endregion

        #region display and animate Label

        class LabelData
        {
            public Label label;
            public string text;
            public Storyboard storyboard;
            public DispatcherTimer historyTimeout;
        }

        readonly List<LabelData> labels = new List<LabelData>(5);
        bool LastHistoryLineIsText = false;
        bool LastHistoryLineRequiredNewLineAfterwards = false;
        int NumberOfDeletionsAllowed = 0;

        void AddToLine(string chars)
        {
            LabelData pack = labels[labels.Count - 1];
            if (pack.historyTimeout != null)
            {
                pack.historyTimeout.Stop();
                if (settings.EnableHistoryTimeout)
                {
                    pack.historyTimeout.Interval = TimeSpan.FromSeconds(settings.HistoryTimeout);
                    pack.historyTimeout.Start();
                }
            }
            pack.text += chars;
            pack.label.Content = pack.text.Replace("_", "__");
            //T.Refresh();
        }

        private bool RemoveLastChar()
        {
            if (labels.Count == 0)
            {
                return false;
            }

            LabelData pack = labels[labels.Count - 1];
            var content = ((string)pack.label.Content);
            if (content.Length == 0)
                return false;
            pack.text = pack.text.Substring(0, pack.text.Length - 1);
            pack.label.Content = pack.text.Replace("_", "__");
            NumberOfDeletionsAllowed -= 1;
            return true;
        }

        void AddNextLine(string chars)
        {
            Label next = new Label
            {
                Content = chars.Replace("_", "__")
            };
            ApplyLabelStyle(next);

            var pack = new LabelData
            {
                label = next,
                text = chars,
                storyboard = null,
                historyTimeout = null,
            };

            if (settings.LabelAnimation == KeyNStroke.Style.Slide)
            {
                Storyboard showLabelSB = new Storyboard();
                var fadeInAnimation = new DoubleAnimation
                {
                    From = 0,
                    To = 1.0,
                    Duration = new Duration(TimeSpan.FromMilliseconds(200)),
                };
                showLabelSB.Children.Add(fadeInAnimation);
                Storyboard.SetTarget(fadeInAnimation, next);
                Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(Label.OpacityProperty));

                Thickness targetMargin = next.Margin; // from ApplyLabelStyle
                if (settings.LabelTextDirection == TextDirection.Down)
                {
                    next.Margin = new Thickness(0, 0, 0, -next.Height);
                }
                else
                {
                    next.Margin = new Thickness(0, -next.Height, 0, 0);
                }

                var pushUpwardsAnimation = new ThicknessAnimation
                {
                    From = next.Margin,
                    To = targetMargin,
                    Duration = new Duration(TimeSpan.FromMilliseconds(200))
                };
                showLabelSB.Children.Add(pushUpwardsAnimation);
                Storyboard.SetTarget(pushUpwardsAnimation, next);
                Storyboard.SetTargetProperty(pushUpwardsAnimation, new PropertyPath(Label.MarginProperty));

                pack.storyboard = showLabelSB;
                pack.storyboard.Begin(pack.label);
            }

            if (settings.LabelTextDirection == TextDirection.Up)
            {
                labelStack.Children.Add(next);
            } else
            {
                labelStack.Children.Insert(0, next);
            }


            if (settings.EnableHistoryTimeout)
            {
                pack.historyTimeout = FireOnce(settings.HistoryTimeout, () =>
                {
                    FadeOutLabel(pack);
                });
            }
            labels.Add(pack);

            TruncateHistory();
        }

        void TruncateHistory()
        {
            while (labels.Count > settings.HistoryLength)
            {
                var toRemove = labels[0];
                Log.e("LABELREMOVAL", $"Truncate {toRemove.label.Content}. Currently in list: {labels.Count}");
                FadeOutLabel(toRemove);
                labels.Remove(toRemove);
            }
        }

        void FadeOutLabel(LabelData toRemove)
        {
            if (toRemove.historyTimeout != null)
            {
                toRemove.historyTimeout.Stop();
            }
            if (toRemove.storyboard != null)
            {
                toRemove.storyboard.Remove(toRemove.label);
            }

            if (settings.LabelAnimation == KeyNStroke.Style.Slide)
            {
                Storyboard hideLabelSB = new Storyboard();
                var fadeOutAnimation = new DoubleAnimation
                {
                    From = toRemove.label.Opacity,
                    To = 0,
                    Duration = new Duration(TimeSpan.FromMilliseconds(200)),
                };
                hideLabelSB.Children.Add(fadeOutAnimation);
                Storyboard.SetTarget(fadeOutAnimation, toRemove.label);
                Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(Label.OpacityProperty));
                fadeOutAnimation.Completed += (object sender, EventArgs e) =>
                {
                    Log.e("LABELREMOVAL", $"{toRemove.label.Content}: Fade out completed");
                    hideLabelSB.Remove(toRemove.label);
                    labelStack.Children.Remove(toRemove.label);

                    if (settings.EnableWindowFade && labels.Count == 0)
                    {
                        Log.e("LABELREMOVAL", $"{toRemove.label.Content}: Fade out completed -> no more labels -> Window wade out");
                        FadeOut();
                    }
                };
                hideLabelSB.Begin(toRemove.label);
                labels.Remove(toRemove);
            } else
            {
                labelStack.Children.Remove(toRemove.label);
                labels.Remove(toRemove);
                if (settings.EnableWindowFade && labels.Count == 0)
                {
                    Log.e("LABELREMOVAL", $"{toRemove.label.Content}: Fade out completed -> no more labels -> Window wade out");
                    FadeOut();
                }
            }
        }


        /// <summary>
        /// Fires an action once after "seconds"
        /// </summary>
        public static DispatcherTimer FireOnce(double timeout, Action onElapsed)
        {
            var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(timeout) };

            var handler = new EventHandler((s, args) =>
            {
                if (timer.IsEnabled)
                {
                    onElapsed();
                }
                timer.Stop();
            });

            timer.Tick += handler;
            timer.Start();
            return timer;
        }

        bool AddingWouldFitInCurrentLine(string s)
        {
            if (labels.Count == 0)
                return false;

            var label = labels[labels.Count - 1].label;
            var text = (String) label.Content + s;
            var formattedText = new FormattedText(
                text,
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface(label.FontFamily, label.FontStyle, label.FontWeight, label.FontStretch),
                label.FontSize,
                Brushes.Black,
                new NumberSubstitution(),
                1);

            return formattedText.Width < label.ActualWidth - 20;
        }

        void ApplyLabelStyle(Label label)
        {
            label.Height = 120;
            label.BeginAnimation(Label.MarginProperty, null);
            if (settings.LabelTextDirection == TextDirection.Down)
            {
                label.Margin = new Thickness(0, 0, 0, -label.Height + settings.LineDistance);
                label.VerticalContentAlignment = VerticalAlignment.Top;
            }
            else
            {
                label.Margin = new Thickness(0, -label.Height + settings.LineDistance, 0, 0);
                label.VerticalContentAlignment = VerticalAlignment.Bottom;
            }

            label.BeginAnimation(Label.OpacityProperty, null);
            label.Opacity = 1.0;
            label.Foreground = new SolidColorBrush(UIHelper.ToMediaColor(settings.LabelColor));
            label.FontSize = settings.LabelFont.Size;
            label.FontFamily = settings.LabelFont.Family;
            label.FontStretch = settings.LabelFont.Stretch;
            label.FontStyle = settings.LabelFont.Style;
            label.FontWeight = settings.LabelFont.Weight;

            if (settings.LabelTextAlignment == TextAlignment.Left)
            {
                label.HorizontalContentAlignment = HorizontalAlignment.Left;
            }
            else if (settings.LabelTextAlignment == TextAlignment.Center)
            {
                label.HorizontalContentAlignment = HorizontalAlignment.Center;
            }
            else
            {
                label.HorizontalContentAlignment = HorizontalAlignment.Right;
            }
        }

        void UpdateLabelStyles()
        {
            foreach (var pack in labels)
            {
                ApplyLabelStyle(pack.label);
            }
        }







        #endregion


    }
}


================================================
FILE: KeyNStroke/KeystrokeEvent.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace KeyNStroke
{
    public enum KeystrokeType
    {
        Undefined,
        Shortcut,
        Text,
        Modifiers
    }

    public class KeystrokeEventArgs
    {
        public string TextModeString; // for use in TextMode
        public string ShortcutString; // will always contain a shortcut

        public KeyboardRawEventArgs raw;

        public bool Shift { get { return raw.Shift; } set { raw.Shift = value; } }
        public bool LShift { get { return raw.LShift; } set { raw.LShift = value; } }
        public bool RShift { get { return raw.RShift; } set { raw.RShift = value; } }
        public bool Ctrl { get { return raw.Ctrl; } set { raw.Ctrl = value; } }
        public bool LCtrl { get { return raw.LCtrl; } set { raw.LCtrl = value; } }
        public bool RCtrl { get { return raw.RCtrl; } set { raw.RCtrl = value; } }
        public bool Caps { get { return raw.Caps; } set { raw.Caps = value; } }
        public bool LWin { get { return raw.LWin; } set { raw.LWin = value; } }
        public bool RWin { get { return raw.RWin; } set { raw.RWin = value; } }
        public bool Alt { get { return raw.Alt; } set { raw.Alt = value; } }
        public bool LAlt { get { return raw.LAlt; } set { raw.LAlt = value; } }
        public bool RAlt { get { return raw.RAlt; } set { raw.RAlt = value; } } // Alt Gr
        public bool Numlock { get { return raw.Numlock; } set { raw.Numlock = value; } }
        public bool Scrollock { get { return raw.Scrollock; } set { raw.Scrollock = value; } }

        public Key Key { get { return raw.Key; } }
        public KeyUpDown Method { get { return raw.Method; } }

        public bool Uppercase { get { return raw.Uppercase; } }
        public bool OnlyShiftOrCaps { get { return raw.OnlyShiftOrCaps; } }
        public bool NoModifiers { get { return raw.NoModifiers; } }
        public bool Win { get { return raw.Win; } }

        public bool OrigShift;
        public bool OrigLShift;
        public bool OrigRShift;
        public bool OrigCaps;

        public bool IsAlpha;
        public bool IsNumericFromNumpad;
        public bool IsNumericFromNumbers;
        public bool IsFunctionKey;
        public bool IsNoUnicodekey;
        public bool ModifierToggledEvent;

        public KeystrokeType StrokeType = KeystrokeType.Undefined;
        public bool ShouldBeDisplayed;
        public bool RequiresNewLine;
        public bool RequiresNewLineAfterwards;
        public bool Deletable = false;

        public bool IsNumeric { get { return IsNumericFromNumbers || IsNumericFromNumpad;  } }

        public override string ToString()
        {
            return ToString(true);
        }

        public string ToString(bool textMode)
        {
            if (ShortcutString != null && (StrokeType == KeystrokeType.Shortcut || !textMode))
            {
                return ShortcutString;
            }
            else if (StrokeType == KeystrokeType.Text && textMode)
            {
                return TextModeString;
            }
            return "BUG2";
        }

        public string ToString(bool textMode, bool DoubleAmpersand)
        {
            return DoubleAmpersand ? ToString(textMode).Replace("&", "&&") : ToString(textMode);
        }

        public string AsShortcutString()
        {
            List<string> output = ShortcutModifiersToList();
            if (StrokeType == KeystrokeType.Text)
            {
                output.Add(TextModeString.ToUpper());
            }
            else
            {
                output.Add(TextModeString);
            }
            return string.Join(" + ", output);
        }

        public List<string> ShortcutModifiersToList()
        {
            List<string> Modifiers = new List<string>();
            if (OrigShift) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftShift));
            if (Ctrl) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftCtrl));
            if (Alt) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftAlt));
            if (Win) Modifiers.Add(SpecialkeysParser.ToString(Key.LWin));
            return Modifiers;
        }

        public string ShortcutIdentifier()
        {
            if (StrokeType == KeystrokeType.Text)
            {
                return null;
            }
            else if(StrokeType == KeystrokeType.Shortcut)
            {

                List<string> output = new List<string>();

                if (this.LCtrl)
                {
                    output.Add("LeftCtrl");
                }
                if (this.RCtrl)
                {
                    output.Add("RightCtrl");
                }
                // else if (this.Ctrl)
                // {
                    // output.Add("Ctrl");
                // }

                if (this.LWin)
                {
                    output.Add("LeftWin");
                }
                if (this.RWin)
                {
                    output.Add("RightWin");
                }

                if (this.LAlt)
                {
                    output.Add("LeftAlt");
                }
                if (this.RAlt)
                {
                    output.Add("RightAlt");
                }
                // else if (this.Alt)
                // {
                    // output.Add("Alt");
                // }

                if (this.LShift)
                {
                    output.Add("LeftShift");
                }
                if (this.RShift)
                {
                    output.Add("RightShift");
                }
                // else if (this.Shift)
                // {
                // output.Add("Shift");
                // }
                string trimmed = TextModeString.Trim();
                if (trimmed.Length == 0) // The Space key
                {
                    output.Add(TextModeString);
                }
                else
                {
                    output.Add(trimmed);
                }
                return string.Join(" + ", output);
            }
            return null;
        }


        public KeystrokeEventArgs(KeyboardRawEventArgs e)
        {
            this.raw = e;
            this.OrigShift = e.Shift;
            this.OrigCaps = e.Caps;
            this.OrigLShift = e.LShift;
            this.OrigRShift = e.RShift;
        }
    }

    public delegate void KeystrokeEventHandler(KeystrokeEventArgs e);

    public interface IKeystrokeEventProvider
    {
        event KeystrokeEventHandler KeystrokeEvent;
    }
}


================================================
FILE: KeyNStroke/KeystrokeParser.cs
================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using KeyNStroke;

namespace KeyNStroke
{
    public class KeystrokeParser : IKeystrokeEventProvider
    {
        //KeysConverter Converter = new KeysConverter();

        #region Constructor

        public KeystrokeParser(IKeyboardRawEventProvider hook)
        {
            hook.KeyEvent += hook_KeyEvent;
        }

        #endregion

        /// <summary>
        /// Gets a KeyboardRawKeyEvent, parses it and forwards it via
        /// the KeystrokeEvent
        /// </summary>
        /// <param name="e"></param>
        void hook_KeyEvent(KeyboardRawEventArgs raw_e)
        {
            KeystrokeEventArgs e = new KeystrokeEventArgs(raw_e);

            e.IsAlpha = CheckIsAlpha(e.raw);
            e.IsNumericFromNumpad = CheckIsNumericFromNumpad(e.raw);
            e.IsNumericFromNumbers = CheckIsNumericFromNumbers(e.raw);
            e.IsNoUnicodekey = CheckIsNoUnicodekey(e.raw);
            e.IsFunctionKey = CheckIsFunctionKey(e.raw);
            e.ModifierToggledEvent = CheckKeyIsModifier(e.raw);

            Log.e("KP", " hook_KeyEvent(): Called. IsAlpha=" + e.IsAlpha.ToString() + " e.Key=" + (e.Key).ToString());

            if (e.Method == KeyUpDown.Down)
            {
                ApplyDeadKey(e);
                if (e.IsAlpha && e.OnlyShiftOrCaps)
                {
                    Log.e("KP", "  hook_KeyEvent(): IsAlpha and OnlyShiftOrCaps => ParseChar");
                    e.TextModeString = ParseChar(e);
                    e.ShouldBeDisplayed = true;
                    e.StrokeType = KeystrokeType.Text;
                    e.Deletable = true;
                }
                else if (e.IsNumeric && e.NoModifiers)
                {
                    Log.e("KP", "  hook_KeyEvent(): e.IsNumeric && e.NoModifiers => ParseNumeric");
                    e.TextModeString = ParseNumeric(e);
                    e.ShouldBeDisplayed = true;
                    e.StrokeType = KeystrokeType.Text;
                    e.Deletable = true;
                }
                else if (e.ModifierToggledEvent) // key is modifier
                {
                    Log.e("KP", "  hook_KeyEvent(): e.ModifierToggledEvent => AddModifier(" + e.Key.ToString() + ")");
                    e.ShouldBeDisplayed = false;
                    AddModifier(e.Key, e);
                    e.StrokeType = KeystrokeType.Modifiers;
                }
                else if (e.IsNoUnicodekey && e.NoModifiers)
                {
                    Log.e("KP", "  hook_KeyEvent(): e.IsNoUnicodekey && e.NoM
Download .txt
gitextract_xryqbs4m/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── DEVELOP.md
├── Directory.Build.props
├── KeyNStroke/
│   ├── AnnotateLine.xaml
│   ├── AnnotateLine.xaml.cs
│   ├── App.config
│   ├── App.xaml
│   ├── App.xaml.cs
│   ├── ButtonIndicator1.Designer.cs
│   ├── ButtonIndicator1.cs
│   ├── ButtonIndicator1.resx
│   ├── ButtonIndicator2.xaml
│   ├── ButtonIndicator2.xaml.cs
│   ├── CursorIndicator1.xaml
│   ├── CursorIndicator1.xaml.cs
│   ├── EnumBindingSourceExtention.cs
│   ├── FodyWeavers.xml
│   ├── FodyWeavers.xsd
│   ├── ImageResources.cs
│   ├── KeyNStroke.csproj
│   ├── KeyboardHook.cs
│   ├── KeyboardLayoutParser.cs
│   ├── KeyboardRawEvent.cs
│   ├── KeystrokeDisplay.xaml
│   ├── KeystrokeDisplay.xaml.cs
│   ├── KeystrokeEvent.cs
│   ├── KeystrokeParser.cs
│   ├── LabeledSlider.py
│   ├── LabeledSlider.xaml
│   ├── LabeledSlider.xaml.cs
│   ├── Log.cs
│   ├── MouseHook.cs
│   ├── MouseRawEvent.cs
│   ├── NativeMethodsDC.cs
│   ├── NativeMethodsEvents.cs
│   ├── NativeMethodsGWL.cs
│   ├── NativeMethodsKeyboard.cs
│   ├── NativeMethodsMouse.cs
│   ├── NativeMethodsWindow.cs
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   ├── Resources.Designer.cs
│   │   ├── Resources.resx
│   │   ├── Settings.Designer.cs
│   │   └── Settings.settings
│   ├── ReadShortcut.xaml
│   ├── ReadShortcut.xaml.cs
│   ├── ResizeButton.xaml
│   ├── ResizeButton.xaml.cs
│   ├── Resources/
│   │   └── updateKey.pub.xml
│   ├── Settings1.xaml
│   ├── Settings1.xaml.cs
│   ├── SettingsStore.cs
│   ├── SpecialkeysParser.cs
│   ├── Themes/
│   │   └── Generic.xaml
│   ├── TweenLabel.cs
│   ├── UIHelper.cs
│   ├── Updater/
│   │   ├── Admininstration.cs
│   │   ├── Statemachine.cs
│   │   ├── Updater.cs
│   │   └── Utils.cs
│   ├── UrlOpener.cs
│   ├── Welcome.xaml
│   ├── Welcome.xaml.cs
│   ├── app.manifest
│   └── packages.config
├── KeyNStroke.sln
├── LICENSE
├── README.md
└── version.json
Download .txt
SYMBOL INDEX (506 symbols across 40 files)

FILE: KeyNStroke/AnnotateLine.xaml.cs
  class AnnotateLine (line 23) | public partial class AnnotateLine : Window
    method AnnotateLine (line 36) | public AnnotateLine(IMouseRawEventProvider m, IKeystrokeEventProvider ...
    method Window_Loaded (line 47) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method m_KeystrokeEvent (line 60) | void m_KeystrokeEvent(KeystrokeEventArgs e)
    method CheckForTrigger (line 67) | private bool CheckForTrigger(string pressed)
    method SetAnnotateLineShortcut (line 77) | public void SetAnnotateLineShortcut(string shortcut)
    method m_MouseEvent (line 91) | private void m_MouseEvent(MouseRawEventArgs raw_e)
    method settingChanged (line 121) | private void settingChanged(object sender, PropertyChangedEventArgs e)
    method SetFormStyles (line 141) | void SetFormStyles()
    method UpdateColor (line 150) | void UpdateColor()
    method UpdatePositionAndSize (line 155) | void UpdatePositionAndSize()
    method Window_Closed (line 193) | private void Window_Closed(object sender, EventArgs e)

FILE: KeyNStroke/App.xaml.cs
  class App (line 21) | public partial class App : Application
    method Main (line 26) | [System.STAThreadAttribute()]
    method OnStartup (line 43) | protected override void OnStartup(StartupEventArgs e)
    method OnActivated (line 70) | protected override void OnActivated(EventArgs e)
    method InitSettings (line 76) | private void InitSettings()
    method InitKeyboardInterception (line 91) | private void InitKeyboardInterception()
    method OnUiClosed (line 101) | private void OnUiClosed(object sender, EventArgs e)
    method OnExit (line 108) | protected override void OnExit(ExitEventArgs e)
    method makeNotifyIcon (line 119) | void makeNotifyIcon()
    method notifyIcon_Click (line 135) | void notifyIcon_Click(object sender, EventArgs e)
    method showSettingsWindow (line 144) | public void showSettingsWindow()
    method onSettingsWindowClosed (line 156) | public void onSettingsWindowClosed()
    method showWelcomeWindow (line 165) | public void showWelcomeWindow()
    method onWelcomeWindowClosed (line 178) | public void onWelcomeWindowClosed()
    method m_KeystrokeEvent (line 189) | void m_KeystrokeEvent(KeystrokeEventArgs e)
    method CheckForTrigger (line 195) | private bool CheckForTrigger(string pressed)
    method SetStandbyShortcut (line 205) | public void SetStandbyShortcut(string shortcut)
    method OnSettingChanged (line 221) | private void OnSettingChanged(object sender, PropertyChangedEventArgs e)
    method OnKeystrokeHistorySettingChanged (line 266) | private void OnKeystrokeHistorySettingChanged()
    method EnableKeystrokeHistory (line 278) | private void EnableKeystrokeHistory()
    method DisableKeystrokeHistory (line 287) | private void DisableKeystrokeHistory()
    method OnButtonIndicatorSettingChanged (line 302) | private void OnButtonIndicatorSettingChanged()
    method EnableButtonIndicator (line 315) | private void EnableButtonIndicator()
    method DisableButtonIndicator (line 326) | private void DisableButtonIndicator()
    method OnCursorIndicatorSettingChanged (line 342) | private void OnCursorIndicatorSettingChanged()
    method EnableCursorIndicator (line 355) | private void EnableCursorIndicator()
    method DisableCursorIndicator (line 367) | private void DisableCursorIndicator()
    method OnAnnotateLineSettingChanged (line 385) | private void OnAnnotateLineSettingChanged()
    method EnableAnnotateLine (line 397) | private void EnableAnnotateLine()
    method DisableAnnotateLine (line 409) | private void DisableAnnotateLine()
    method EnableMouseHook (line 425) | private void EnableMouseHook()
    method DisableMouseHookIfNotNeeded (line 432) | private void DisableMouseHookIfNotNeeded()
    method DisableMouseHook (line 438) | private void DisableMouseHook()

FILE: KeyNStroke/ButtonIndicator1.Designer.cs
  class ButtonIndicator1 (line 3) | partial class ButtonIndicator1
    method Dispose (line 14) | protected override void Dispose(bool disposing)
    method InitializeComponent (line 29) | private void InitializeComponent()

FILE: KeyNStroke/ButtonIndicator1.cs
  class ButtonIndicator1 (line 19) | public partial class ButtonIndicator1 : Form
    method ButtonIndicator1 (line 27) | public ButtonIndicator1(IMouseRawEventProvider m, IKeystrokeEventProvi...
    method Redraw (line 59) | private void Redraw()
    method ButtonIndicator_Load (line 80) | private void ButtonIndicator_Load(object sender, EventArgs e)
    method k_KeystrokeEvent (line 88) | private void k_KeystrokeEvent(KeystrokeEventArgs e)
    method m_MouseEvent (line 125) | void m_MouseEvent(MouseRawEventArgs raw_e)
    method IndicateWheel (line 154) | private void IndicateWheel(MouseRawEventArgs raw_e)
    method WheelIconTimer_Tick (line 173) | void WheelIconTimer_Tick(object sender, EventArgs e)
    method IndicateDoubleClick (line 183) | private void IndicateDoubleClick(MouseButton mouseButton)
    method leftDoubleClickIconTimeout_Tick (line 201) | void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)
    method ShowButton (line 209) | private void ShowButton(MouseButton mouseButton)
    method HideButton (line 244) | private void HideButton(MouseRawEventArgs raw_e)
    method HideMouseIfNoButtonPressed (line 281) | void HideMouseIfNoButtonPressed()
    method ButtonIndicator_FormClosed (line 295) | void ButtonIndicator_FormClosed(object sender, FormClosedEventArgs e)
    method SetFormStyles (line 309) | void SetFormStyles()
    method StartImageResizeThread (line 324) | private void StartImageResizeThread()
    method BackgroundResizeImages (line 331) | void BackgroundResizeImages()
    method UpdateSize (line 362) | void UpdateSize()
    method RecalcOffset (line 369) | void RecalcOffset()
    method UpdatePosition (line 375) | void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)
    method OnlyDblClkIconVisible (line 387) | private bool OnlyDblClkIconVisible()
    method settingChanged (line 398) | private void settingChanged(object sender, PropertyChangedEventArgs e)

FILE: KeyNStroke/ButtonIndicator2.xaml.cs
  class ButtonIndicator2 (line 24) | public partial class ButtonIndicator2 : Window
    method ButtonIndicator2 (line 32) | public ButtonIndicator2(IMouseRawEventProvider m, SettingsStore s)
    method Window_Loaded (line 43) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method OnRender (line 67) | protected override void OnRender(DrawingContext drawingContext)
    method Redraw (line 72) | private void Redraw()
    method ButtonIndicator_Load (line 82) | private void ButtonIndicator_Load(object sender, EventArgs e)
    method m_MouseEvent (line 93) | void m_MouseEvent(MouseRawEventArgs raw_e)
    method IndicateWheel (line 121) | private void IndicateWheel(MouseRawEventArgs raw_e)
    method WheelIconTimer_Tick (line 140) | void WheelIconTimer_Tick(object sender, EventArgs e)
    method IndicateDoubleClick (line 150) | private void IndicateDoubleClick(MouseButton mouseButton)
    method leftDoubleClickIconTimeout_Tick (line 168) | void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)
    method ShowButton (line 176) | private void ShowButton(MouseButton mouseButton)
    method HideButton (line 211) | private void HideButton(MouseRawEventArgs raw_e)
    method HideMouseIfNoButtonPressed (line 248) | void HideMouseIfNoButtonPressed()
    method Window_Closed (line 262) | private void Window_Closed(object sender, EventArgs e)
    method SetFormStyles (line 272) | void SetFormStyles()
    method UpdateSize (line 285) | void UpdateSize()
    method RecalcOffset (line 294) | void RecalcOffset()
    method UpdatePosition (line 300) | void UpdatePosition()
    method OnlyDblClkIconVisible (line 323) | private bool OnlyDblClkIconVisible()
    method settingChanged (line 334) | private void settingChanged(object sender, PropertyChangedEventArgs e)

FILE: KeyNStroke/CursorIndicator1.xaml.cs
  class CursorIndicator1 (line 13) | public partial class CursorIndicator1 : Window
    method CursorIndicator1 (line 21) | public CursorIndicator1(IMouseRawEventProvider m, SettingsStore s)
    method Window_Loaded (line 32) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method m_MouseEvent (line 40) | void m_MouseEvent(MouseRawEventArgs raw_e)
    method m_CursorEvent (line 58) | void m_CursorEvent(bool visible)
    method SetFormStyles (line 78) | void SetFormStyles()
    method UpdateSize (line 90) | void UpdateSize()
    method UpdatePosition (line 96) | void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)
    method settingChanged (line 107) | private void settingChanged(object sender, PropertyChangedEventArgs e)
    method UpdateColor (line 138) | private void UpdateColor()
    method Window_Closed (line 163) | private void Window_Closed(object sender, EventArgs e)

FILE: KeyNStroke/EnumBindingSourceExtention.cs
  class EnumBindingSourceExtention (line 9) | public class EnumBindingSourceExtention : MarkupExtension
    method EnumBindingSourceExtention (line 13) | public EnumBindingSourceExtention(Type enumType)
    method GetAttributeOfType (line 20) | public static T GetAttributeOfType<T>(Enum enumVal) where T : System.A...
    method GetAttributeDescription (line 28) | public static string GetAttributeDescription(Enum enumValue)
    method ProvideValue (line 34) | public override object ProvideValue(IServiceProvider serviceProvider)

FILE: KeyNStroke/ImageResources.cs
  class ImageResources (line 17) | public class ImageResources
    type BitmapCollection (line 21) | private struct BitmapCollection
    method Init (line 40) | public static void Init(string customIconFolder)
    method ExportBuiltinRessources (line 61) | public static void ExportBuiltinRessources(string exportFolder)
    method ReloadRessources (line 111) | public static void ReloadRessources(string customIconFolder)
    method LoadRessource (line 131) | private static Bitmap LoadRessource(string customIconFolder, string pn...
    method ApplyScalingFactor (line 157) | public static void ApplyScalingFactor(double scalingfactor)
    method RefreshScalingCache (line 168) | private static void RefreshScalingCache()
    method CreateScaledBitmapCollection (line 189) | private static BitmapCollection CreateScaledBitmapCollection(float sca...
    method Scale (line 210) | private static Bitmap Scale(float scalingFactor, Bitmap original, uint...
    type ComposeOptions (line 227) | public struct ComposeOptions
      method ToString (line 253) | public override string ToString()
      method Equals (line 271) | public override bool Equals(object obj)
      method GetHashCode (line 276) | public override int GetHashCode()
    method Compose (line 286) | public static Bitmap Compose(ComposeOptions c)

FILE: KeyNStroke/KeyboardHook.cs
  class KeyboardHook (line 19) | public class KeyboardHook : IDisposable, IKeyboardRawEventProvider
    method KeyboardHook (line 27) | public KeyboardHook()
    method RegisterKeyboardHook (line 46) | private void RegisterKeyboardHook()
    method UnregisterKeyboardHook (line 64) | private void UnregisterKeyboardHook()
    method HookCallback (line 101) | private IntPtr HookCallback(int nCode,
    method CheckModifiers (line 153) | private void CheckModifiers(KeyboardRawEventArgs e)
    method CheckModifierDown (line 176) | private bool CheckModifierDown(int modifiercode)
    method CheckModifierToggled (line 195) | private bool CheckModifierToggled(int modifiercode)
    method FixKeyStateArray (line 201) | private void FixKeyStateArray(KeyboardRawEventArgs e)
    method OnKeyEvent (line 242) | public void OnKeyEvent(KeyboardRawEventArgs e)
    method Dispose (line 261) | public void Dispose()

FILE: KeyNStroke/KeyboardLayoutParser.cs
  class KeyboardLayoutParser (line 10) | public class KeyboardLayoutParser
    method Parse (line 12) | public static string Parse(KeyboardRawEventArgs e)
    method ParseViaMapKeycode (line 29) | public static string ParseViaMapKeycode(KeyboardRawEventArgs e)
    method ParseViaToAscii (line 37) | public static string ParseViaToAscii(KeyboardRawEventArgs e)
    method ParseViaToUnicode (line 69) | public static string ParseViaToUnicode(KeyboardRawEventArgs e)
    method ProcessDeadkeyWithNextKey (line 132) | public static string ProcessDeadkeyWithNextKey(KeyboardRawEventArgs de...

FILE: KeyNStroke/KeyboardRawEvent.cs
  type KeyUpDown (line 12) | public enum KeyUpDown
  class KeyboardRawEventArgs (line 19) | public class KeyboardRawEventArgs
    method KeyboardRawEventArgs (line 51) | public KeyboardRawEventArgs(NativeMethodsKeyboard.KBDLLHOOKSTRUCT Kbdl...
  type IKeyboardRawEventProvider (line 60) | public interface IKeyboardRawEventProvider : IDisposable
  type WindowsVirtualKey (line 65) | public enum WindowsVirtualKey

FILE: KeyNStroke/KeystrokeDisplay.xaml.cs
  class KeystrokeDisplay (line 28) | public partial class KeystrokeDisplay : Window
    method KeystrokeDisplay (line 35) | public KeystrokeDisplay(IKeystrokeEventProvider k, SettingsStore s)
    method Window_Loaded (line 54) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method InitPeriodicTopmostTimer (line 74) | void InitPeriodicTopmostTimer()
    method KeystrokeEvent (line 102) | void KeystrokeEvent(KeystrokeEventArgs e)
    method InitializeAnimations (line 194) | private void InitializeAnimations()
    method FadeOut (line 208) | private void FadeOut()
    method FadeIn (line 216) | private void FadeIn()
    method FullOpacity (line 222) | private void FullOpacity()
    method ToOpacity (line 227) | private void ToOpacity(double targetOpacity, bool fade)
    method SettingChanged (line 255) | private void SettingChanged(object sender, PropertyChangedEventArgs e)
    method SetSettingsModeShortcut (line 354) | public void SetSettingsModeShortcut(string shortcut)
    method ValidateShortcutSetting (line 366) | public static bool ValidateShortcutSetting(string shortcut)
    method CheckForSettingsMode (line 389) | private bool CheckForSettingsMode(string pressed)
    method ActivateDisplayOnlyMode (line 401) | void ActivateDisplayOnlyMode(bool force)
    method ActivateSettingsMode (line 429) | void ActivateSettingsMode()
    method SetPasswordModeShortcut (line 464) | public void SetPasswordModeShortcut(string shortcut)
    method CheckForPasswordMode (line 478) | private bool CheckForPasswordMode(string pressed)
    method ButtonLeavePasswordMode_Click (line 489) | private void ButtonLeavePasswordMode_Click(object sender, RoutedEventA...
    method ActivatePasswordProtectionMode (line 496) | void ActivatePasswordProtectionMode()
    method DeactivatePasswordProtectionMode (line 507) | void DeactivatePasswordProtectionMode(bool force)
    method BackgroundGrid_MouseDown (line 530) | private void BackgroundGrid_MouseDown(object sender, MouseButtonEventA...
    method Window_MouseUp (line 540) | private void Window_MouseUp(object sender, MouseButtonEventArgs e)
    method InnerPanel_MouseDown (line 552) | private void InnerPanel_MouseDown(object sender, MouseButtonEventArgs e)
    method InnerPanel_MouseMove (line 560) | private void InnerPanel_MouseMove(object sender, MouseEventArgs e)
    method InnerPanel_MouseUp (line 576) | private void InnerPanel_MouseUp(object sender, MouseButtonEventArgs e)
    method ButtonSettings_Click (line 587) | private void ButtonSettings_Click(object sender, RoutedEventArgs e)
    method ButtonClose_Click (line 592) | private void ButtonClose_Click(object sender, RoutedEventArgs e)
    method Window_Closing (line 597) | private void Window_Closing(object sender, CancelEventArgs e)
    method ButtonLeaveSettingsMode_Click (line 602) | private void ButtonLeaveSettingsMode_Click(object sender, RoutedEventA...
    class LabelData (line 611) | class LabelData
    method AddToLine (line 624) | void AddToLine(string chars)
    method RemoveLastChar (line 641) | private bool RemoveLastChar()
    method AddNextLine (line 658) | void AddNextLine(string chars)
    method TruncateHistory (line 732) | void TruncateHistory()
    method FadeOutLabel (line 743) | void FadeOutLabel(LabelData toRemove)
    method FireOnce (line 796) | public static DispatcherTimer FireOnce(double timeout, Action onElapsed)
    method AddingWouldFitInCurrentLine (line 814) | bool AddingWouldFitInCurrentLine(string s)
    method ApplyLabelStyle (line 834) | void ApplyLabelStyle(Label label)
    method UpdateLabelStyles (line 872) | void UpdateLabelStyles()

FILE: KeyNStroke/KeystrokeEvent.cs
  type KeystrokeType (line 10) | public enum KeystrokeType
  class KeystrokeEventArgs (line 18) | public class KeystrokeEventArgs
    method ToString (line 68) | public override string ToString()
    method ToString (line 73) | public string ToString(bool textMode)
    method ToString (line 86) | public string ToString(bool textMode, bool DoubleAmpersand)
    method AsShortcutString (line 91) | public string AsShortcutString()
    method ShortcutModifiersToList (line 105) | public List<string> ShortcutModifiersToList()
    method ShortcutIdentifier (line 115) | public string ShortcutIdentifier()
    method KeystrokeEventArgs (line 188) | public KeystrokeEventArgs(KeyboardRawEventArgs e)
  type IKeystrokeEventProvider (line 200) | public interface IKeystrokeEventProvider

FILE: KeyNStroke/KeystrokeParser.cs
  class KeystrokeParser (line 13) | public class KeystrokeParser : IKeystrokeEventProvider
    method KeystrokeParser (line 19) | public KeystrokeParser(IKeyboardRawEventProvider hook)
    method hook_KeyEvent (line 31) | void hook_KeyEvent(KeyboardRawEventArgs raw_e)
    method BackupDeadKey (line 174) | private void BackupDeadKey(KeystrokeEventArgs e)
    method ApplyDeadKey (line 183) | private bool ApplyDeadKey(KeystrokeEventArgs e)
    method ParseShortcutViaSpecialkeysParser (line 201) | private void ParseShortcutViaSpecialkeysParser(KeystrokeEventArgs e)
    method ParseTexttViaSpecialkeysParser (line 217) | private void ParseTexttViaSpecialkeysParser(KeystrokeEventArgs e)
    method AddModifier (line 232) | private void AddModifier(Key keys, KeystrokeEventArgs e)
    method RemoveModifier (line 250) | private void RemoveModifier(Key keys, KeystrokeEventArgs e)
    method ParseChar (line 269) | private string ParseChar(KeystrokeEventArgs e)
    method ParseNumeric (line 283) | private string ParseNumeric(KeystrokeEventArgs e)
    method CheckIsAlpha (line 296) | bool CheckIsAlpha(KeyboardRawEventArgs e)
    method CheckIsAlpha (line 301) | bool CheckIsAlpha(Key key)
    method CheckIsAlpha (line 306) | bool CheckIsAlpha(string s)
    method CheckIsASCII (line 311) | bool CheckIsASCII(string s)
    method CheckIsNumericFromNumpad (line 316) | bool CheckIsNumericFromNumpad(KeyboardRawEventArgs e)
    method CheckIsNumericFromNumpad (line 321) | bool CheckIsNumericFromNumpad(Key key)
    method CheckIsNumeric (line 326) | bool CheckIsNumeric(string s)
    method CheckIsNumericFromNumbers (line 331) | bool CheckIsNumericFromNumbers(KeyboardRawEventArgs e)
    method CheckIsNumericFromNumbers (line 336) | bool CheckIsNumericFromNumbers(Key key)
    method CheckIsFunctionKey (line 341) | bool CheckIsFunctionKey(KeyboardRawEventArgs e)
    method CheckIsNoUnicodekey (line 352) | bool CheckIsNoUnicodekey(KeyboardRawEventArgs e)
    method IsDeletableSpecialKey (line 381) | private bool IsDeletableSpecialKey(Key key)
    method CheckKeyIsModifier (line 390) | bool CheckKeyIsModifier(KeyboardRawEventArgs e)
    method OnKeystrokeEvent (line 405) | private void OnKeystrokeEvent(KeystrokeEventArgs e)

FILE: KeyNStroke/LabeledSlider.py
  function pq (line 88) | def pq(p, q):
  function calc (line 93) | def calc(ysper):
  function update (line 129) | def update(ysper):

FILE: KeyNStroke/LabeledSlider.xaml.cs
  class LabeledSlider (line 24) | public partial class LabeledSlider : UserControl
    method LabeledSlider (line 29) | public LabeledSlider()
    method pq (line 124) | private double pq(double p, double q) {
    method recalcParams (line 136) | private void recalcParams()
    method ValueToSlider (line 167) | private void ValueToSlider()
    method SliderToValue (line 185) | private void SliderToValue()
    method Slider_ValueChanged (line 205) | private void Slider_ValueChanged(object sender, RoutedPropertyChangedE...

FILE: KeyNStroke/Log.cs
  class Log (line 9) | public class Log
    method SetTagFilter (line 13) | public static void SetTagFilter(string tagfilter)
    method filter (line 19) | static bool filter(string tag)
    method e (line 24) | public static void e(string tag, string msg)
    method e (line 32) | public static void e(string msg)

FILE: KeyNStroke/MouseHook.cs
  class MouseHook (line 15) | public class MouseHook : IDisposable, IMouseRawEventProvider
    method MouseHook (line 26) | public MouseHook(SettingsStore s)
    method RegisterMouseHook (line 52) | private void RegisterMouseHook()
    method UnregisterMouseHook (line 86) | private void UnregisterMouseHook()
    method WindowsHookExCallback (line 110) | private IntPtr WindowsHookExCallback(int nCode,
    method CheckDoubleClick (line 147) | private void CheckDoubleClick(MouseRawEventArgs args)
    method WinEventCallback (line 164) | private void WinEventCallback(IntPtr hWinEventHook, NativeMethodsEvent...
    method OnMouseEvent (line 210) | public void OnMouseEvent(MouseRawEventArgs e)
    method OnCursorEvent (line 225) | public void OnCursorEvent(bool visible)
    method Dispose (line 244) | public void Dispose()

FILE: KeyNStroke/MouseRawEvent.cs
  type MouseEventType (line 11) | public enum MouseEventType
  type MouseButton (line 30) | public enum MouseButton
  type MouseAction (line 39) | public enum MouseAction
  class MouseRawEventArgs (line 48) | public class MouseRawEventArgs
    method MouseRawEventArgs (line 57) | public MouseRawEventArgs(NativeMethodsMouse.MSLLHOOKSTRUCT msllhookstr...
    method ParseWparam (line 67) | public void ParseWparam(UIntPtr wParam)
  type IMouseRawEventProvider (line 163) | public interface IMouseRawEventProvider : IDisposable

FILE: KeyNStroke/NativeMethodsDC.cs
  class NativeMethodsDC (line 44) | public class NativeMethodsDC
    method SetBitmapForWindow (line 54) | public static void SetBitmapForWindow(IntPtr windowHandle, Point pos, ...
    type POINT (line 129) | [StructLayout(LayoutKind.Sequential)]
      method POINT (line 135) | public POINT(Int32 x, Int32 y)
    type SIZE (line 139) | [StructLayout(LayoutKind.Sequential)]
      method SIZE (line 145) | public SIZE(Int32 cx, Int32 cy)
    type ARGB (line 149) | [StructLayout(LayoutKind.Sequential, Pack = 1)]
    type BLENDFUNCTION (line 158) | [StructLayout(LayoutKind.Sequential, Pack = 1)]
    method UpdateLayeredWindow (line 214) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method CreateCompatibleDC (line 220) | [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetDC (line 223) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method ReleaseDC (line 226) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method DeleteDC (line 229) | [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method SelectObject (line 233) | [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method DeleteObject (line 236) | [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

FILE: KeyNStroke/NativeMethodsEvents.cs
  class NativeMethodsEvents (line 10) | public class NativeMethodsEvents
    method SetWinEventHook (line 12) | [DllImport("user32.dll")]
    method UnhookWinEvent (line 15) | [DllImport("user32.dll")]
    type WinEventFlags (line 20) | public enum WinEventFlags : uint
    type WinEvents (line 28) | public enum WinEvents : uint
    type ObjIds (line 283) | public enum ObjIds : uint {

FILE: KeyNStroke/NativeMethodsGWL.cs
  class NativeMethodsGWL (line 15) | public class NativeMethodsGWL
    method ClickThrough (line 21) | public static void ClickThrough(IntPtr Handle)
    method CatchClicks (line 32) | public static void CatchClicks(IntPtr Handle)
    method HideFromAltTab (line 39) | public static void HideFromAltTab(IntPtr Handle)
    type GWL (line 50) | public enum GWL : int
    type WindowStyles (line 65) | [Flags]
    method GetWindowLong (line 88) | public static uint GetWindowLong(IntPtr hWnd, GWL nIndex)
    method SetWindowLong (line 104) | public static int SetWindowLong(IntPtr hWnd, GWL nIndex, uint dwNewLong)
    method GetWindowLong32 (line 115) | [DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharS...
    method GetWindowLongPtr64 (line 118) | [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = Ch...
    method SetWindowLong32 (line 121) | [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharS...
    method SetWindowLongPtr64 (line 124) | [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = Ch...

FILE: KeyNStroke/NativeMethodsKeyboard.cs
  class NativeMethodsKeyboard (line 10) | [ComVisibleAttribute(false),
    type KBDLLHOOKSTRUCT (line 15) | [StructLayout(LayoutKind.Sequential)]
    method GetModuleHandle (line 30) | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method SetWindowsHookEx (line 33) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method UnhookWindowsHookEx (line 37) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method CallNextHookEx (line 41) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetKeyState (line 45) | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true,...
    method GetKeyNameText (line 49) | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    method MapVirtualKey (line 52) | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    method ToAscii (line 94) | [DllImport("user32")]
    method ToUnicode (line 109) | [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
    method GetKeyboardState (line 142) | [DllImport("user32")]

FILE: KeyNStroke/NativeMethodsMouse.cs
  class NativeMethodsMouse (line 10) | [ComVisibleAttribute(false),
    method SetWindowsHookEx (line 32) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method UnhookWindowsHookEx (line 36) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method CallNextHookEx (line 40) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetDoubleClickTime (line 44) | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    type POINT (line 50) | [StructLayout(LayoutKind.Sequential)]
      method POINT (line 56) | public POINT(int x, int y)
      method POINT (line 62) | public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }
      method Equals (line 79) | public bool Equals(POINT p2)
    type MSLLHOOKSTRUCT (line 85) | [StructLayout(LayoutKind.Sequential)]
    method GetCursorPos (line 99) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    type CURSORINFO (line 113) | [StructLayout(LayoutKind.Sequential)]
    method GetCursorInfo (line 124) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetCursorInfoWrapper (line 127) | public static CURSORINFO GetCursorInfoWrapper()

FILE: KeyNStroke/NativeMethodsWindow.cs
  class NativeMethodsWindow (line 10) | public class NativeMethodsWindow
    method SetWindowTopMost (line 18) | public static void SetWindowTopMost(IntPtr Handle) {
    method SetWindowPosition (line 26) | public static void SetWindowPosition(IntPtr Handle, int x, int y)
    method SetWindowSize (line 35) | public static void SetWindowSize(IntPtr Handle, int width, int height)
    method SetWindowPos (line 49) | [DllImport("user32.dll")]
    type MonitorOptions (line 53) | public enum MonitorOptions : uint
    method MonitorFromPoint (line 60) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    type DpiType (line 63) | public enum DpiType : uint
    method GetDpiForMonitor (line 71) | [DllImport("Shcore.dll", CharSet = CharSet.Auto, SetLastError = true)]
    type ProcessDpiAwareness (line 74) | public enum ProcessDpiAwareness : uint
    method GetProcessDpiAwareness (line 81) | [DllImport("Shcore.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetThreadDpiAwarenessContext (line 90) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    type DpiAwareness (line 93) | public enum DpiAwareness : uint
    method GetAwarenessFromDpiAwarenessContext (line 101) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetDpiFromDpiAwarenessContext (line 104) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method EnumDisplayMonitors (line 109) | [DllImport("user32.dll", CharSet = CharSet.Auto)]
    type Rect (line 116) | [StructLayout(LayoutKind.Sequential)]
    method GetAllUsedDpis (line 125) | static public List<uint> GetAllUsedDpis()
    method PrintDpiAwarenessInfo (line 143) | static public void PrintDpiAwarenessInfo()

FILE: KeyNStroke/Properties/Resources.Designer.cs
  class Resources (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
    method Resources (line 31) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...

FILE: KeyNStroke/Properties/Settings.Designer.cs
  class Settings (line 14) | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

FILE: KeyNStroke/ReadShortcut.xaml.cs
  class ReadShortcut (line 20) | public partial class ReadShortcut : Window
    method ReadShortcut (line 30) | public ReadShortcut(IKeystrokeEventProvider k, string purpose)
    method Button_Click (line 37) | private void Button_Click(object sender, RoutedEventArgs e)
    method Window_Loaded (line 42) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method k_KeystrokeEvent (line 48) | void k_KeystrokeEvent(KeystrokeEventArgs e)

FILE: KeyNStroke/ResizeButton.xaml.cs
  class ResizeButton (line 25) | public partial class ResizeButton : UserControl
    method ResizeButton (line 40) | public ResizeButton()
    method resizeWindowButton_MouseDown (line 61) | private void resizeWindowButton_MouseDown(object sender, MouseButtonEv...
    method MouseHook_MouseEvent (line 86) | private void MouseHook_MouseEvent(MouseRawEventArgs raw_e)
    method tick (line 105) | private void tick()
  type ResizeTarget (line 140) | public enum ResizeTarget

FILE: KeyNStroke/Settings1.xaml.cs
  class EnumBooleanConverter (line 19) | public class EnumBooleanConverter : IValueConverter
    method Convert (line 21) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 27) | public object ConvertBack(object value, Type targetType, object parame...
  class FloatPercentageConverter (line 34) | public class FloatPercentageConverter : IValueConverter
    method Convert (line 36) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 42) | public object ConvertBack(object value, Type targetType, object parame...
  class MediaColorDrawingColorConverter (line 49) | public class MediaColorDrawingColorConverter : IValueConverter
    method Convert (line 51) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 57) | public object ConvertBack(object value, Type targetType, object parame...
  class Settings1 (line 67) | public partial class Settings1 : Window
    method Settings1 (line 69) | public Settings1(SettingsStore s, IKeystrokeEventProvider k)
    method OnClosed (line 87) | protected override void OnClosed(EventArgs e)
    method RadioButton_Checked (line 93) | private void RadioButton_Checked(object sender, RoutedEventArgs e)
    method Hyperlink_RequestNavigate (line 98) | private void Hyperlink_RequestNavigate(object sender, System.Windows.N...
    method Window_Closing (line 103) | private void Window_Closing(object sender, System.ComponentModel.Cance...
    method Bn_reset_position_Click (line 108) | private void Bn_reset_position_Click(object sender, RoutedEventArgs e)
    method bn_reset_all_Click (line 116) | private void bn_reset_all_Click(object sender, RoutedEventArgs e)
    method Button_close_Click (line 121) | private void Button_close_Click(object sender, RoutedEventArgs e)
    method button_exit_Click (line 126) | private void button_exit_Click(object sender, RoutedEventArgs e)
    method OnButtonTextFontClick (line 131) | private void OnButtonTextFontClick(object sender, RoutedEventArgs e)
    method FontDialogLoaded (line 146) | private void FontDialogLoaded(object sender, RoutedEventArgs e)
    method Hyperlink_ChangeResizeMoveShortcut (line 157) | private void Hyperlink_ChangeResizeMoveShortcut(object sender, RoutedE...
    method Hyperlink_ResetResizeMoveShortcut (line 167) | private void Hyperlink_ResetResizeMoveShortcut(object sender, RoutedEv...
    method Hyperlink_TriggerResizeMoveShortcut (line 172) | private void Hyperlink_TriggerResizeMoveShortcut(object sender, Routed...
    method Hyperlink_ChangePasswordModeShortcut (line 181) | private void Hyperlink_ChangePasswordModeShortcut(object sender, Route...
    method Hyperlink_ResetPasswordModeShortcut (line 191) | private void Hyperlink_ResetPasswordModeShortcut(object sender, Routed...
    method Hyperlink_TriggerPasswordModeShortcut (line 196) | private void Hyperlink_TriggerPasswordModeShortcut(object sender, Rout...
    method Hyperlink_ChangeAnnotateLineShortcut (line 205) | private void Hyperlink_ChangeAnnotateLineShortcut(object sender, Route...
    method Hyperlink_ResetAnnotateLineShortcut (line 215) | private void Hyperlink_ResetAnnotateLineShortcut(object sender, Routed...
    method Hyperlink_TriggerAnnotateLineShortcut (line 220) | private void Hyperlink_TriggerAnnotateLineShortcut(object sender, Rout...
    method Hyperlink_ChangeStandbyShortcut (line 230) | private void Hyperlink_ChangeStandbyShortcut(object sender, RoutedEven...
    method Hyperlink_ResetStandbyShortcut (line 240) | private void Hyperlink_ResetStandbyShortcut(object sender, RoutedEvent...
    method Hyperlink_TriggerStandbyShortcut (line 245) | private void Hyperlink_TriggerStandbyShortcut(object sender, RoutedEve...
    method S_PropertyChanged (line 255) | private void S_PropertyChanged(object sender, System.ComponentModel.Pr...
    method OnButtonCustomIconsSelectFolder (line 274) | private void OnButtonCustomIconsSelectFolder(object sender, RoutedEven...
    method OnButtonExportBuiltinIcons (line 289) | private void OnButtonExportBuiltinIcons(object sender, RoutedEventArgs e)
    method OnClickCustomIconsHelp (line 305) | private void OnClickCustomIconsHelp(object sender, RoutedEventArgs e)
    method OnClickCustomIconsRefresh (line 310) | private void OnClickCustomIconsRefresh(object sender, RoutedEventArgs e)
    method Hyperlink_WelcomeWindow (line 329) | private void Hyperlink_WelcomeWindow(object sender, RoutedEventArgs e)
    method Hyperlink_DisableStandbyMode (line 334) | private void Hyperlink_DisableStandbyMode(object sender, RoutedEventAr...

FILE: KeyNStroke/SettingsStore.cs
  type TextAlignment (line 21) | public enum TextAlignment
  type TextDirection (line 28) | public enum TextDirection
  type Style (line 34) | public enum Style
  type ButtonIndicatorType (line 40) | public enum ButtonIndicatorType
  type KeystrokeMethodEnum (line 46) | public enum KeystrokeMethodEnum
  class Extensions (line 58) | public static class Extensions
    method IsTextMode (line 60) | public static bool IsTextMode(this KeystrokeMethodEnum method)
  type IGet (line 70) | interface IGet<T>
    method Get (line 72) | T Get();
  class SerializableFont (line 75) | [DataContract]
    method SerializableFont (line 85) | public SerializableFont(FontInfo value)
    method Get (line 97) | public FontInfo Get()
    method ToString (line 110) | public override string ToString()
  class SerializablePoint (line 116) | [DataContract]
    method SerializablePoint (line 121) | public SerializablePoint(System.Windows.Point value)
    method Get (line 126) | public System.Windows.Point Get()
  class SerializableSize (line 132) | [DataContract]
    method SerializableSize (line 137) | public SerializableSize(Size value)
    method Get (line 142) | public Size Get()
  class SerializableColor (line 148) | [DataContract]
    method SerializableColor (line 156) | public SerializableColor(System.Windows.Media.Color value)
    method Get (line 163) | public System.Windows.Media.Color Get()
  class SerializableColor2 (line 169) | [DataContract]
    method SerializableColor2 (line 173) | public SerializableColor2(Color value)
    method Get (line 177) | public Color Get()
  class Settings (line 187) | [DataContract]
  class SettingsStore (line 243) | public class SettingsStore : INotifyPropertyChanged
    method SettingsStore (line 248) | public SettingsStore()
    method Or (line 261) | private T Or<T>(IGet<T> a, T b)
    method Or (line 267) | private T Or<T>(Nullable<T> a, T b) where T : struct
    method Or (line 273) | private T Or<T>(T a, T b) where T : class
    method SetWindowSizeWithoutOnSettingChangedEvent (line 351) | public void SetWindowSizeWithoutOnSettingChangedEvent(Size value)
    method SetPanelSizeWithoutOnSettingChangedEvent (line 371) | public void SetPanelSizeWithoutOnSettingChangedEvent(Size value)
    method OnSettingChanged (line 669) | private void OnSettingChanged(string property)
    method CallPropertyChangedForAllProperties (line 678) | public void CallPropertyChangedForAllProperties()
    method SaveAll (line 740) | public void SaveAll()
    method LoadAll (line 767) | public void LoadAll()
    method ResetAll (line 800) | public void ResetAll()
    method ToString (line 811) | public override string ToString()

FILE: KeyNStroke/SpecialkeysParser.cs
  class SpecialkeysParser (line 10) | class SpecialkeysParser
    method ToString (line 12) | public static string ToString(Key k)

FILE: KeyNStroke/TweenLabel.cs
  class TweenLabel (line 14) | class TweenLabel : Control
    method PrintDebug (line 23) | void PrintDebug(String msg)
    method getNewLabel (line 28) | public static TweenLabel getNewLabel(Form form, SettingsStore s)
    method Recycle (line 40) | public void Recycle()
    method Init (line 57) | private TweenLabel Init(Form form, SettingsStore s)
    method TweenLabel_TextChanged (line 73) | void TweenLabel_TextChanged(object sender, EventArgs e)
    method settingChanged (line 79) | private void settingChanged(object sender, PropertyChangedEventArgs e)
    method tweenFadeIn (line 95) | public void tweenFadeIn()
    method T_FadeIn (line 106) | void T_FadeIn(object sender, EventArgs e)
    method FadeOutAndRecycle (line 123) | public void FadeOutAndRecycle(Func<TweenLabel, bool> onFinish)
    method T_FadeOut (line 142) | void T_FadeOut(object sender, EventArgs e)
    method IsFadingOut (line 161) | bool IsFadingOut()
    method TweenMove (line 173) | public void TweenMove(Point newMoveDir)
    method T_Move (line 201) | void T_Move(object sender, EventArgs e)
    method DrawString (line 227) | public void DrawString(System.Drawing.Graphics G)
    method AddingWouldFit (line 286) | public bool AddingWouldFit(string additionalChars)
    method OnPaint (line 297) | protected override void OnPaint(PaintEventArgs e)
    method ResetHistoryTimeoutTimer (line 307) | private void ResetHistoryTimeoutTimer()
    method CancelTimeoutTimer (line 323) | private void CancelTimeoutTimer()
    method timeoutTimer_Tick (line 331) | void timeoutTimer_Tick(object sender, EventArgs e)

FILE: KeyNStroke/UIHelper.cs
  class UIHelper (line 14) | class UIHelper
    method ToMediaColor (line 17) | public static MColor ToMediaColor(DColor color)
    method ToDrawingColor (line 22) | public static DColor ToDrawingColor(MColor color)
    method FindChild (line 37) | public static T FindChild<T>(DependencyObject parent, string childName)

FILE: KeyNStroke/Updater/Admininstration.cs
  class Admininstration (line 9) | class Admininstration
    method HandleArgs (line 21) | static public bool HandleArgs(string[] args)
    method CreateSigningKeyPair (line 60) | static void CreateSigningKeyPair()
    method GetPrivateKey (line 82) | static RSA GetPrivateKey()
    method TestSigning (line 97) | static void TestSigning()
    method CreateUpdateManifest (line 150) | static void CreateUpdateManifest()
    method SignUpdateManifest (line 177) | static void SignUpdateManifest()
    method DownloadAndVerifyManifestAndUpdate (line 195) | static void DownloadAndVerifyManifestAndUpdate()

FILE: KeyNStroke/Updater/Statemachine.cs
  class Statemachine (line 29) | public class Statemachine
    class UiUpdateEventArgs (line 39) | public class UiUpdateEventArgs
    method UiButton_Click (line 70) | public void UiButton_Click(object sender, RoutedEventArgs e)
    method UiRefresh (line 107) | public void UiRefresh()
    method Statemachine (line 197) | private Statemachine()
    type UpdateState (line 244) | public enum UpdateState
    type UpdateCommands (line 258) | private enum UpdateCommands
    method UpdateThread (line 264) | void UpdateThread()

FILE: KeyNStroke/Updater/Updater.cs
  class Updater (line 7) | class Updater
    method Updater (line 15) | private Updater()
    method HandleArgs (line 23) | static public bool HandleArgs(string[] args)
    method TriggerUpdateStep2 (line 47) | static public void TriggerUpdateStep2(byte[] update)
    method UpdateStep2 (line 83) | static public void UpdateStep2(string oldExePath, int startingProcessPid)
    method UpdateStep3 (line 125) | static public void UpdateStep3(string tmpExePath, int startingProcessPid)

FILE: KeyNStroke/Updater/Utils.cs
  class Utils (line 15) | class Utils
    class VerificationException (line 22) | public class VerificationException : Exception
      method VerificationException (line 24) | public VerificationException()
      method VerificationException (line 28) | public VerificationException(string message)
      method VerificationException (line 33) | public VerificationException(string message, Exception inner)
    class DownloadFailedException (line 42) | public class DownloadFailedException : Exception
      method DownloadFailedException (line 44) | public DownloadFailedException()
      method DownloadFailedException (line 48) | public DownloadFailedException(string message)
      method DownloadFailedException (line 53) | public DownloadFailedException(string message, Exception inner)
    method SHA256OfFile (line 68) | public static string SHA256OfFile(string path)
    method SHA256OfBytes (line 86) | public static string SHA256OfBytes(byte[] data)
    method IsDirectoryWritable (line 100) | public static bool IsDirectoryWritable(string path)
    method HasAdminPrivileges (line 115) | public static bool HasAdminPrivileges()
    method SignXml (line 136) | public static void SignXml(XmlDocument xmlDoc, RSA rsaKey)
    method VerifyXml (line 183) | public static void VerifyXml(XmlDocument xmlDoc, RSA rsaKey)
    method DownloadWithTimeoutAndProgress (line 234) | private static byte[] DownloadWithTimeoutAndProgress(string url, Progr...
    method DownloadExecutableAndVerifyHash (line 322) | public static byte[] DownloadExecutableAndVerifyHash(XmlDocument manif...
    method DownloadAndVerifyManifest (line 361) | public static XmlDocument DownloadAndVerifyManifest()
    method GetEmbeddedPubKey (line 409) | public static RSA GetEmbeddedPubKey()
    method CanUpdate (line 428) | public static bool CanUpdate(XmlDocument manifest)

FILE: KeyNStroke/UrlOpener.cs
  class UrlOpener (line 10) | public class UrlOpener
    method OpenGithub (line 12) | public static void OpenGithub()
    method OpenGithubREADME (line 19) | public static void OpenGithubREADME()
    method OpenGithubIssues (line 26) | public static void OpenGithubIssues()

FILE: KeyNStroke/Welcome.xaml.cs
  class Welcome (line 13) | public partial class Welcome : Window
    method Welcome (line 17) | public Welcome(SettingsStore s)
    method OnClosed (line 32) | protected override void OnClosed(EventArgs e)
    method Hyperlink_RequestNavigate_README (line 38) | private void Hyperlink_RequestNavigate_README(object sender, System.Wi...
    method Hyperlink_RequestNavigate_Issues (line 44) | private void Hyperlink_RequestNavigate_Issues(object sender, System.Wi...
    method ButtonSettings_Click (line 50) | private void ButtonSettings_Click(object sender, RoutedEventArgs e)
    method Updater_onUIUpdate (line 59) | private void Updater_onUIUpdate(object sender, Updater.Statemachine.Ui...
    method Hyperlink_RequestNavigate_UpdateDetails (line 83) | private void Hyperlink_RequestNavigate_UpdateDetails(object sender, Sy...
    method Updater_onShutdownRequest (line 93) | private void Updater_onShutdownRequest(object sender)
    method ButtonHideThisWindow_Click (line 103) | private void ButtonHideThisWindow_Click(object sender, RoutedEventArgs e)
    method ButtonExitApplication_Click (line 108) | private void ButtonExitApplication_Click(object sender, RoutedEventArg...
    method Hyperlink_RequestNavigate_settings (line 113) | private void Hyperlink_RequestNavigate_settings(object sender, System....
Condensed preview — 71 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (523K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 2403,
    "preview": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT license.\n\n# https://github.com/microsoft/github-actions-"
  },
  {
    "path": ".gitignore",
    "chars": 2844,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n*/Thum"
  },
  {
    "path": "DEVELOP.md",
    "chars": 6594,
    "preview": "# Key'n'Stroke\n\n(previously PxKeystrokesForScreencasts)\n\nThis is a little documentation about how the source code is org"
  },
  {
    "path": "Directory.Build.props",
    "chars": 365,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/ms"
  },
  {
    "path": "KeyNStroke/AnnotateLine.xaml",
    "chars": 913,
    "preview": "<Window x:Class=\"KeyNStroke.AnnotateLine\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n   "
  },
  {
    "path": "KeyNStroke/AnnotateLine.xaml.cs",
    "chars": 6718,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusin"
  },
  {
    "path": "KeyNStroke/App.config",
    "chars": 353,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".N"
  },
  {
    "path": "KeyNStroke/App.xaml",
    "chars": 355,
    "preview": "<Application x:Class=\"KeyNStroke.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n  "
  },
  {
    "path": "KeyNStroke/App.xaml.cs",
    "chars": 12395,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Configuration;\nusing System.D"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.Designer.cs",
    "chars": 1680,
    "preview": "namespace KeyNStroke\n{\n    partial class ButtonIndicator1\n    {\n        /// <summary>\n        /// Required designer var"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.cs",
    "chars": 13933,
    "preview": "using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.resx",
    "chars": 5696,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The prim"
  },
  {
    "path": "KeyNStroke/ButtonIndicator2.xaml",
    "chars": 695,
    "preview": "<Window x:Class=\"KeyNStroke.ButtonIndicator2\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\""
  },
  {
    "path": "KeyNStroke/ButtonIndicator2.xaml.cs",
    "chars": 11518,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawing;\nusing System.Linq;\nu"
  },
  {
    "path": "KeyNStroke/CursorIndicator1.xaml",
    "chars": 798,
    "preview": "<Window x:Class=\"KeyNStroke.CursorIndicator1\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\""
  },
  {
    "path": "KeyNStroke/CursorIndicator1.xaml.cs",
    "chars": 5627,
    "preview": "using System;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Med"
  },
  {
    "path": "KeyNStroke/EnumBindingSourceExtention.cs",
    "chars": 1733,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing Sy"
  },
  {
    "path": "KeyNStroke/FodyWeavers.xml",
    "chars": 176,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Weavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSc"
  },
  {
    "path": "KeyNStroke/FodyWeavers.xsd",
    "chars": 8399,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n  <!-- This file was gen"
  },
  {
    "path": "KeyNStroke/ImageResources.cs",
    "chars": 12450,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/KeyNStroke.csproj",
    "chars": 21594,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "KeyNStroke/KeyboardHook.cs",
    "chars": 9341,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/KeyboardLayoutParser.cs",
    "chars": 8671,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/KeyboardRawEvent.cs",
    "chars": 13962,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusin"
  },
  {
    "path": "KeyNStroke/KeystrokeDisplay.xaml",
    "chars": 4179,
    "preview": "<Window x:Class=\"KeyNStroke.KeystrokeDisplay\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\""
  },
  {
    "path": "KeyNStroke/KeystrokeDisplay.xaml.cs",
    "chars": 30791,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.L"
  },
  {
    "path": "KeyNStroke/KeystrokeEvent.cs",
    "chars": 6672,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/KeystrokeParser.cs",
    "chars": 16943,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing"
  },
  {
    "path": "KeyNStroke/LabeledSlider.py",
    "chars": 5355,
    "preview": "import numpy as np\nimport matplotlib.pyplot as plt\nimport math\nfrom matplotlib.widgets import Slider\n\n\n#      LabeledSli"
  },
  {
    "path": "KeyNStroke/LabeledSlider.xaml",
    "chars": 1009,
    "preview": "<UserControl x:Class=\"KeyNStroke.LabeledSlider\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presen"
  },
  {
    "path": "KeyNStroke/LabeledSlider.xaml.cs",
    "chars": 7179,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.L"
  },
  {
    "path": "KeyNStroke/Log.cs",
    "chars": 902,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nna"
  },
  {
    "path": "KeyNStroke/MouseHook.cs",
    "chars": 8763,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing S"
  },
  {
    "path": "KeyNStroke/MouseRawEvent.cs",
    "chars": 5444,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/NativeMethodsDC.cs",
    "chars": 10990,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Linq;\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsEvents.cs",
    "chars": 26638,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.T"
  },
  {
    "path": "KeyNStroke/NativeMethodsGWL.cs",
    "chars": 4660,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/NativeMethodsKeyboard.cs",
    "chars": 6470,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.T"
  },
  {
    "path": "KeyNStroke/NativeMethodsMouse.cs",
    "chars": 4697,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/NativeMethodsWindow.cs",
    "chars": 7048,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.T"
  },
  {
    "path": "KeyNStroke/Properties/AssemblyInfo.cs",
    "chars": 2695,
    "preview": "using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropSer"
  },
  {
    "path": "KeyNStroke/Properties/Resources.Designer.cs",
    "chars": 2999,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     Dieser Code"
  },
  {
    "path": "KeyNStroke/Properties/Resources.resx",
    "chars": 5494,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The prim"
  },
  {
    "path": "KeyNStroke/Properties/Settings.Designer.cs",
    "chars": 1101,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     Dieser Code"
  },
  {
    "path": "KeyNStroke/Properties/Settings.settings",
    "chars": 193,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    "
  },
  {
    "path": "KeyNStroke/ReadShortcut.xaml",
    "chars": 856,
    "preview": "<Window x:Class=\"KeyNStroke.ReadShortcut\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n   "
  },
  {
    "path": "KeyNStroke/ReadShortcut.xaml.cs",
    "chars": 1357,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/ResizeButton.xaml",
    "chars": 660,
    "preview": "<UserControl x:Class=\"KeyNStroke.ResizeButton\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/present"
  },
  {
    "path": "KeyNStroke/ResizeButton.xaml.cs",
    "chars": 5806,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/Resources/updateKey.pub.xml",
    "chars": 243,
    "preview": "<RSAKeyValue><Modulus>pKQQkG41DRCmvzfZbQUleBei4oBubdxsxyqM9LbKMI42EysS6Cpgsc4M90T9c7+nhLqPmqHTsU0pPJoABJe80NnaPvqUK16fIA"
  },
  {
    "path": "KeyNStroke/Settings1.xaml",
    "chars": 30442,
    "preview": "<Window x:Class=\"KeyNStroke.Settings1\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      "
  },
  {
    "path": "KeyNStroke/Settings1.xaml.cs",
    "chars": 12078,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing"
  },
  {
    "path": "KeyNStroke/SettingsStore.cs",
    "chars": 36547,
    "preview": "using Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawin"
  },
  {
    "path": "KeyNStroke/SpecialkeysParser.cs",
    "chars": 6109,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/Themes/Generic.xaml",
    "chars": 756,
    "preview": "<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n    xmlns:x=\"http://schemas.mi"
  },
  {
    "path": "KeyNStroke/TweenLabel.cs",
    "chars": 10918,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawing;\nusing System.Drawing"
  },
  {
    "path": "KeyNStroke/UIHelper.cs",
    "chars": 2977,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "KeyNStroke/Updater/Admininstration.cs",
    "chars": 7941,
    "preview": "using System;\nusing System.IO;\nusing System.Reflection;\nusing System.Security.Cryptography;\nusing System.Xml;\n\nnamespac"
  },
  {
    "path": "KeyNStroke/Updater/Statemachine.cs",
    "chars": 16122,
    "preview": "using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Syst"
  },
  {
    "path": "KeyNStroke/Updater/Updater.cs",
    "chars": 5034,
    "preview": "using System;\nusing System.IO;\nusing System.Diagnostics;\n\nnamespace KeyNStroke.Updater\n{\n    class Updater\n    {\n      "
  },
  {
    "path": "KeyNStroke/Updater/Utils.cs",
    "chars": 15376,
    "preview": "using System;\nusing System.IO;\nusing System.Net;\nusing System.Reflection;\nusing System.Security.Cryptography;\nusing Sys"
  },
  {
    "path": "KeyNStroke/UrlOpener.cs",
    "chars": 909,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing S"
  },
  {
    "path": "KeyNStroke/Welcome.xaml",
    "chars": 4347,
    "preview": "<Window x:Class=\"KeyNStroke.Welcome\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        "
  },
  {
    "path": "KeyNStroke/Welcome.xaml.cs",
    "chars": 3685,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Threading;\nusing System.Windows;\n"
  },
  {
    "path": "KeyNStroke/app.manifest",
    "chars": 3363,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <ass"
  },
  {
    "path": "KeyNStroke/packages.config",
    "chars": 4311,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Costura.Fody\" version=\"5.0.2\" targetFramework=\"net48\" "
  },
  {
    "path": "KeyNStroke.sln",
    "chars": 1108,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3001"
  },
  {
    "path": "LICENSE",
    "chars": 11325,
    "preview": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licens"
  },
  {
    "path": "README.md",
    "chars": 6332,
    "preview": "# Key'n'Stroke\n\nDisplays Keystrokes in an overlay window and visualize mouse clicks.\n\n\n# Features\n\n - Displays special k"
  },
  {
    "path": "version.json",
    "chars": 282,
    "preview": "{\n  \"$schema\": \"https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/versio"
  }
]

About this extraction

This page contains the full source code of the Phaiax/Key-n-Stroke GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 71 files (489.1 KB), approximately 106.8k tokens, and a symbol index with 506 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!