[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT license.\n\n# https://github.com/microsoft/github-actions-for-desktop-apps\n\n# This continuous integration pipeline is triggered anytime a user pushes code to the repo.\n\n# This pipeline builds the project, runs unit tests, then saves the build artifact.\n\nname:  Key-n-Stroke Continuous Integration\n\n# Trigger on every master branch push and pull request\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n    runs-on: windows-latest\n\n    env:\n      Solution_Path: KeyNStroke.sln\n      #Test_Project_Path: KeyNStroke.Tests\\KeyNStroke.Tests.csproj\n      App_Project_Path: KeyNStroke\\KeyNStroke.csproj\n      App_Output_Directory: KeyNStroke\\bin\n      App_Assembly: Key-n-Stroke.exe\n      Actions_Allow_Unsecure_Commands: true # Allows AddPAth and SetEnv commands\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n      with:\n        fetch-depth: 0 # avoid shallow clone so nbgv can do its work.\n\n    # Versioning\n    - name: Use Nerdbank.GitVersioning to set version variables\n      uses: dotnet/nbgv@master\n      with:\n        setAllVars: true\n\n    # Install the .NET Core workload\n    - name: Install .NET\n      uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: '5.0.x'\n\n    # Add  MsBuild to the PATH: https://github.com/microsoft/setup-msbuild\n    - name: Setup MSBuild.exe\n      uses: microsoft/setup-msbuild@v1.0.1\n\n    #- name: Execute Unit Tests\n    #  run: dotnet test $env:Test_Project_Path\n\n    # Restore the application\n    - name:  Restore the application to populate the obj and packages folder\n      run: msbuild $env:Solution_Path /t:Restore /p:RestorePackagesConfig=true /p:Configuration=$env:Configuration\n      env:\n        Configuration: Release\n\n\n    # Actual build\n    - name:  Build the application\n      run: msbuild $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform=\"Any CPU\"\n\n      env:\n        Configuration: Release\n\n    \n    # Signing\n    # https://github.com/dlemstra/code-sign-action\n    # https://github.com/GabrielAcostaEngler/signtool-code-sign\n    # https://archi-lab.net/code-signing-assemblies-with-github-actions/\n\n    - name: Upload build artifacts\n      uses: actions/upload-artifact@v1\n      with:\n        name: KeyNStroke-${{env.NBGV_SemVer2}}\n        path: ${{ env.App_Output_Directory }}\\Release\\\n\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n*/Thumbs.db\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\nx64/\nbuild/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Roslyn cache directories\n*.ide/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n#NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding addin-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n## TODO: Comment the next line if you want to checkin your\n## web deploy settings but do note that will include unencrypted\n## passwords\n#*.pubxml\n\n# NuGet Packages Directory\npackages/*\n## TODO: If the tool you use requires repositories.config\n## uncomment the next line\n#!packages/repositories.config\n\n# Enable \"build/\" folder in the NuGet Packages folder since\n# NuGet packages use it for MSBuild targets.\n# This line needs to be after the ignore of the build folder\n# (and the packages folder if the line above has been uncommented)\n!packages/build/\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# LightSwitch generated files\nGeneratedArtifacts/\n_Pvt_Extensions/\nModelManifest.xml\n\n.vs\n\n# keys\n*.priv.xml\n"
  },
  {
    "path": "DEVELOP.md",
    "content": "# Key'n'Stroke\n\n(previously PxKeystrokesForScreencasts)\n\nThis is a little documentation about how the source code is organized and works\n\n## How to make a new release\n\n 1. Change Version: \n   - Twice in AssemblyInfo.cs\n   - In project settings under \"Publish\"\n 2. Build the App in Release mode\n 3. Sign the file using the certum certificate\n    - cmd.exe: `signtool.exe sign /n Open /t http://time.certum.pl/ /fd sha256 /v Key-n-Stroke.exe`\n 4. Verify the signature:\n    - cmd.exe: `signtool.exe verify /pa Key-n-Stroke.exe`\n 4. `./Key-n-Stroke.exe --create-update-manifest`\n 5. Change manifest: Update description\n 6. `./Key-n-Stroke.exe --sign-update-manifest`\n 7. Copy executable into release folder\n 8. Commit and push\n 7. Upload manifest `scp updateManifest.xml $SSHSERVER:html/key-n-stroke/`\n\n## How to prepare the development environment\n\n - Install Visual Studio 2019\n - Download dependencies:\n   - Clean the folder `packages` in the repository root\n   - Search a msbuild.exe and run it like this:\n   - 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`\n - Then run the application using Visual Studio\n\n## Main (Program.cs)\n\nThe main function is found in Program.cs.\n\nIt initializes the classes decribed below.\n * new KeyboardHook()\n * new KeystrokeParser()\n * new SettingsStore()\n * new KeystrokeDisplay()\n\nThe keypress information is intercepted in KeyboardHook, passed to KeystrokeParser and then passed to KeystrokeDisplay.\n\n\n## How key events are intercepted from the system\n\nIn Windows, you can register a callback (also known as hook) for certain process messages like [keyboard][LowLevelKeyboardProc] and mouse events.\n\nCallbacks are registered using the function [SetWindowsHookEx(event type, callback function, ...)][SetWindowsHookEx].\nThis 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.\n\nSometimes special data structures and constants are needed for these system functions (for example [KBDLLHOOKSTRUCT][KBDLLHOOKSTRUCT]).They are also defined in these files.\n\n\n### System calls (KeyboardHook.cs KeyboardRawEvents.cs)\n\nNow 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().\n\nKeyboardHook exposes the C# event <code>KeyEvent</code> that takes methods of delegate type <code>KeyboardRawEventHandler</code>. (via <code>interface IKeyboardRawEventProvider</code> in KeyboardRawEvent.cs).\n\nThe idea is, that you just do this nice pure C# thing\n\n```\tvoid hook_KeyEvent(KeyboardRawEventArgs raw_e)\n\t{\n\t\t// process hook\n\t}\n\tIKeyboardRawEventProvider myKeyboardHook = new KeyboardHook();\n\thook.KeyEvent += hook_KeyEvent;\n```\n\n... instead of dealing with the raw system library calls.\n\nThe 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>.\n\n### Key event processing (KeystrokeParser.cs KeystrokeEvent.cs)\n\nNext, the <code>KeyboardRawEventArgs raw_e</code> are converted into Keystrokes. This is happening in a similar interface/event pattern.\n\nThe 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.\n\nInput: The KeystrokeParser registers itself on the <code>KeyEvent</code> of the KeyboardHook in the constructor.\n\nOutput: The KeystrokeParser exposes the C# event <code>KeystrokeEvent</code> that takes methods of delegate type KeystrokeEventHandler. (via interface IKeystrokeEventProvider in KeystrokeEvent.cs).\n\nDuring 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.\n\nThe <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).\n\nThe <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.\n\n\nThe output of the KeystrokeParser is used by the KeystrokesDisplay.\n\n[SetWindowsHookEx]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx \"SetWindowsHookEx function\"\n[LowLevelKeyboardProc]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985%28v=vs.85%29.aspx \"LowLevelKeyboardProc callback function\"\n[TranslateMessage]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955%28v=vs.85%29.aspx \"TranslateMessage function\"\n[KBDLLHOOKSTRUCT]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644967%28v=vs.85%29.aspx \"KBDLLHOOKSTRUCT structure\"\n\n## Displaying Keystrokes (KeystrokesDisplay.cs code (F7))\n\n * displays new keys as wished by the information in KeystrokeEventArgs (<code>k_KeystrokeEvent()</code>)\n * Checks if resizing mode is activated (<code>CheckForSettingsMode()</code>)\n * allows resizing and moving of window\n * keeps track of the History of keystrokes (<code>List&lt;TweenLabel&gt; tweenLabels</code>)\n\n### TweenLabel (TweenLabel.cs code (F7))\n\nThe 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.\n\n## Settings\n\nAre stored in the C# Application.UserAppDataRegistry using the wrapper functions in SettingsStore.cs.\n\nThe Settings are changed in the UI window Settings.cs\n\n## NativeMethodsSWP.cs\n\nA wrapper for calling the system library function SetWindowPos.\nThe class NativeMethodsSWP provides a method to pin the UI on top of everything.\n\n## NativeMethodsGWL.cs\n\nSome wrappers for calling system library functions.\nThey provide methods to make the UI through clickable and clickable again.\n\n## UrlOpener.cs\n\nOpen a browser."
  },
  {
    "path": "Directory.Build.props",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <PackageReference Include=\"Nerdbank.GitVersioning\" Condition=\"!Exists('packages.config')\">\n      <Version>3.4.244</Version>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "KeyNStroke/AnnotateLine.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.AnnotateLine\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"AnnotateLine\" Height=\"8\" Width=\"800\"\n        AllowsTransparency=\"True\"\n        Topmost=\"True\"\n        ShowInTaskbar=\"False\"\n        WindowStyle=\"None\"\n        ResizeMode=\"NoResize\"\n        Background=\"Transparent\" Loaded=\"Window_Loaded\" Closed=\"Window_Closed\">\n    <Grid>\n        <Rectangle x:Name=\"line\" Fill=\"#FF7EEF84\" Margin=\"1\">\n            <Rectangle.Effect>\n                <BlurEffect Radius=\"2\" KernelType=\"Box\"/>\n            </Rectangle.Effect>\n        </Rectangle>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/AnnotateLine.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\nusing static KeyNStroke.NativeMethodsMouse;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für AnnotateLine.xaml\n    /// </summary>\n    public partial class AnnotateLine : Window\n    {\n\n        IMouseRawEventProvider m;\n        IKeystrokeEventProvider k;\n        SettingsStore s;\n        IntPtr windowHandle;\n        bool isDown;\n        POINT startCursorPosition = new POINT(0, 0);\n        POINT endCursorPosition = new POINT(0, 0);\n        bool nextClickDraws = false;\n        bool nextClickHides = false;\n\n        public AnnotateLine(IMouseRawEventProvider m, IKeystrokeEventProvider k, SettingsStore s)\n        {\n            InitializeComponent();\n\n            this.m = m;\n            this.s = s;\n            this.k = k;\n\n            s.PropertyChanged += settingChanged;\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            s.CallPropertyChangedForAllProperties();\n            m.MouseEvent += m_MouseEvent;\n            this.k.KeystrokeEvent += m_KeystrokeEvent;\n            windowHandle = new WindowInteropHelper(this).Handle;\n            SetFormStyles();\n        }\n\n        #region Shortcut\n\n        public string AnnotateLineShortcut;\n\n        void m_KeystrokeEvent(KeystrokeEventArgs e)\n        {\n            if (s == null) return;\n            string pressed = e.ShortcutIdentifier();\n            e.raw.preventDefault = e.raw.preventDefault || CheckForTrigger(pressed);\n        }\n\n        private bool CheckForTrigger(string pressed)\n        {\n            if (AnnotateLineShortcut != null && pressed == AnnotateLineShortcut)\n            {\n                nextClickDraws = true;\n                return true;\n            }\n            return false;\n        }\n\n        public void SetAnnotateLineShortcut(string shortcut)\n        {\n            if (KeystrokeDisplay.ValidateShortcutSetting(shortcut))\n            {\n                AnnotateLineShortcut = shortcut;\n            }\n            else\n            {\n                AnnotateLineShortcut = s.AnnotateLineShortcutDefault;\n            }\n        }\n\n        #endregion\n\n        private void m_MouseEvent(MouseRawEventArgs raw_e)\n        {\n            if (s == null) return;\n            if (!isDown && nextClickHides && raw_e.Action == MouseAction.Down)\n            {\n                raw_e.preventDefault = true;\n                nextClickHides = false;\n                this.Hide();\n            }\n\n            if (isDown && raw_e.Action == MouseAction.Move)\n            {\n                endCursorPosition = raw_e.Position;\n                UpdatePositionAndSize();\n            }\n            else if (!isDown && raw_e.Action == MouseAction.Down && nextClickDraws)\n            {\n                isDown = true;\n                raw_e.preventDefault = true;\n                nextClickDraws = false;\n                startCursorPosition = raw_e.Position;\n                endCursorPosition = raw_e.Position;\n            }\n            else if (isDown && raw_e.Action == MouseAction.Up)\n            {\n                isDown = false;\n                nextClickHides = true;\n            }\n        }\n\n        private void settingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            this.Dispatcher.BeginInvoke((Action)(() =>\n            {\n                if (s == null) return;\n                switch (e.PropertyName)\n                {\n                    case \"AnnotateLineShortcutTrigger\":\n                        nextClickDraws = true;\n                        break;\n                    case \"AnnotateLineColor\":\n                        UpdateColor();\n                        break;\n                    case \"AnnotateLineShortcut\":\n                        SetAnnotateLineShortcut(s.AnnotateLineShortcut);\n                        break;\n                }\n            }));\n        }\n\n        void SetFormStyles()\n        {\n            Log.e(\"CI\", $\"WindowHandle={windowHandle}\");\n            NativeMethodsGWL.ClickThrough(windowHandle);\n            NativeMethodsGWL.HideFromAltTab(windowHandle);\n\n            UpdatePositionAndSize();\n        }\n\n        void UpdateColor()\n        {\n            line.Fill = new SolidColorBrush(UIHelper.ToMediaColor(s.AnnotateLineColor));\n        }\n\n        void UpdatePositionAndSize()\n        {\n            if (isDown)\n            {\n                this.Show();\n                Int32 xmin = Math.Min(startCursorPosition.X, endCursorPosition.X);\n                Int32 ymin = Math.Min(startCursorPosition.Y, endCursorPosition.Y);\n                Int32 h = Math.Abs(startCursorPosition.Y - endCursorPosition.Y);\n                Int32 w = Math.Abs(startCursorPosition.X - endCursorPosition.X);\n                bool horizontal = w >= h;\n\n                IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(startCursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);\n                uint adpiX = 0, adpiY = 0;\n                NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);\n                Log.e(\"AL\", $\"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={startCursorPosition.X} cy={startCursorPosition.Y}\");\n\n\n                if (horizontal)\n                {\n                    this.Width = w / (double)adpiX * 96.0;\n                    this.Height = 4;\n                    NativeMethodsWindow.SetWindowPosition(windowHandle, (int)xmin, (int)startCursorPosition.Y - 2);\n                }\n                else\n                {\n                    this.Width = 4;\n                    this.Height = h / (double)adpiY * 96.0;\n                    NativeMethodsWindow.SetWindowPosition(windowHandle, (int)startCursorPosition.X - 2, ymin);\n                }\n\n            }\n            else\n            {\n                this.Hide();\n            }\n            \n        }\n\n        private void Window_Closed(object sender, EventArgs e)\n        {\n            if (m != null)\n            {\n                m.MouseEvent -= m_MouseEvent;\n            }\n            if (k != null)\n            {\n                k.KeystrokeEvent -= m_KeystrokeEvent;\n            }\n            if (s != null)\n            {\n                s.PropertyChanged -= settingChanged;\n            }\n            m = null;\n            s = null;\n            k = null;\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\" />\n    </startup>\n    <System.Windows.Forms.ApplicationConfigurationSection>\n      <add key=\"DpiAwareness\" value=\"PerMonitorV2\" />\n    </System.Windows.Forms.ApplicationConfigurationSection>\n</configuration>"
  },
  {
    "path": "KeyNStroke/App.xaml",
    "content": "﻿<Application x:Class=\"KeyNStroke.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:KeyNStroke\" ShutdownMode=\"OnExplicitShutdown\">\n    <Application.Resources>\n         \n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "KeyNStroke/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Configuration;\nusing System.Data;\nusing System.Drawing.Printing;\nusing System.IO;\nusing System.IO.IsolatedStorage;\nusing System.Linq;\nusing System.Runtime.Serialization.Json;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für \"App.xaml\"\n    /// </summary>\n    public partial class App : Application\n    {\n\n        #region Main()\n\n        [System.STAThreadAttribute()]\n        [System.Diagnostics.DebuggerNonUserCodeAttribute()]\n        public static void Main()\n        {\n            App app = new App();\n            app.InitializeComponent();\n            app.Run();\n        }\n\n        #endregion\n\n        #region Init\n\n        SettingsStore mySettings;\n        Window welcomeWindow;\n        Settings1 settingsWindow;\n\n        protected override void OnStartup(StartupEventArgs e)\n        {\n            Log.SetTagFilter(\"UPDATE\");\n\n            if (Updater.Updater.HandleArgs(e.Args))\n            {\n                Shutdown();\n            }\n\n            InitSettings();\n            ImageResources.Init(mySettings.ButtonIndicatorCustomIconsFolder);\n            InitKeyboardInterception();\n\n            mySettings.PropertyChanged += OnSettingChanged;\n            myKeystrokeConverter.KeystrokeEvent += m_KeystrokeEvent;\n\n            //myUi.FormClosed += OnUiClosed;\n            mySettings.CallPropertyChangedForAllProperties();\n\n            makeNotifyIcon();\n\n            if (mySettings.WelcomeOnStartup)\n            {\n                showWelcomeWindow();\n            }\n        }\n\n        protected override void OnActivated(EventArgs e)\n        {\n\n        }\n\n\n        private void InitSettings()\n        {\n            mySettings = new SettingsStore();\n\n            mySettings.WindowLocationDefault = new Point(\n                System.Windows.SystemParameters.PrimaryScreenWidth - mySettings.WindowSizeDefault.Width - 20,\n                System.Windows.SystemParameters.PrimaryScreenHeight - mySettings.WindowSizeDefault.Height - 40);\n\n            //mySettings.ResetAll(); // test defaults\n            mySettings.LoadAll();\n        }\n\n        IKeyboardRawEventProvider myKeyboardHook;\n        IKeystrokeEventProvider myKeystrokeConverter;\n\n        private void InitKeyboardInterception()\n        {\n            myKeyboardHook = new KeyboardHook();\n            myKeystrokeConverter = new KeystrokeParser(myKeyboardHook);\n        }\n\n        #endregion\n\n        #region Closing/Exiting\n\n        private void OnUiClosed(object sender, EventArgs e)\n        {\n            DisableCursorIndicator();\n            DisableButtonIndicator();\n            DisableKeystrokeHistory();\n        }\n\n        protected override void OnExit(ExitEventArgs e)\n        {\n            OnUiClosed(this, e);\n            this.notifyIcon_main.Visible = false;\n        }\n\n        #endregion\n\n        #region Tray Icon\n\n        private System.Windows.Forms.NotifyIcon notifyIcon_main;\n        void makeNotifyIcon()\n        {\n            var _assembly = System.Reflection.Assembly.GetExecutingAssembly();\n            \n            Stream iconStream = Application.GetResourceStream(new Uri(\"pack://application:,,,/Key-n-Stroke;component/Resources/app.ico\")).Stream;\n            var icon = new System.Drawing.Icon(iconStream);\n\n            this.notifyIcon_main = new System.Windows.Forms.NotifyIcon();\n            this.notifyIcon_main.BalloonTipText = \"xfxfn\";\n            this.notifyIcon_main.BalloonTipTitle = \"xfgn\";\n            this.notifyIcon_main.Click += new EventHandler(notifyIcon_Click);\n            this.notifyIcon_main.Icon = icon;\n            this.notifyIcon_main.Visible = true;\n\n        }\n\n        void notifyIcon_Click(object sender, EventArgs e)\n        {\n            showSettingsWindow();\n        }\n\n        #endregion\n\n        #region Settings Window\n\n        public void showSettingsWindow()\n        {\n            if (settingsWindow == null)\n            {\n                settingsWindow = new Settings1(mySettings, myKeystrokeConverter);\n                settingsWindow.Show();\n            } else\n            {\n                settingsWindow.Activate();\n            }\n        }\n\n        public void onSettingsWindowClosed()\n        {\n            settingsWindow = null;\n        }\n\n        #endregion\n\n        #region Welcome Window\n\n        public void showWelcomeWindow()\n        {\n            if (welcomeWindow == null)\n            {\n                welcomeWindow = new Welcome(mySettings);\n                welcomeWindow.Show();\n            }\n            else\n            {\n                welcomeWindow.Activate();\n            }\n        }\n\n        public void onWelcomeWindowClosed()\n        {\n            welcomeWindow = null;\n        }\n\n        #endregion\n\n        #region Shortcut\n\n        public string StandbyShortcut;\n\n        void m_KeystrokeEvent(KeystrokeEventArgs e)\n        {\n            string pressed = e.ShortcutIdentifier();\n            e.raw.preventDefault = e.raw.preventDefault || CheckForTrigger(pressed);\n        }\n\n        private bool CheckForTrigger(string pressed)\n        {\n            if (StandbyShortcut != null && pressed == StandbyShortcut)\n            {\n                mySettings.Standby = !mySettings.Standby;\n                return true;\n            }\n            return false;\n        }\n\n        public void SetStandbyShortcut(string shortcut)\n        {\n            if (KeystrokeDisplay.ValidateShortcutSetting(shortcut))\n            {\n                StandbyShortcut = shortcut;\n            }\n            else\n            {\n                StandbyShortcut = mySettings.StandbyShortcutDefault;\n            }\n        }\n\n        #endregion\n\n        #region OnSettingChanged\n\n        private void OnSettingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            switch (e.PropertyName)\n            {\n                case \"EnableCursorIndicator\":\n                    OnCursorIndicatorSettingChanged();\n                    break;\n                case \"ButtonIndicator\":\n                    OnButtonIndicatorSettingChanged();\n                    break;\n                case \"EnableKeystrokeHistory\":\n                    OnKeystrokeHistorySettingChanged();\n                    break;\n                case \"EnableAnnotateLine\":\n                    OnAnnotateLineSettingChanged();\n                    break;\n                case \"ButtonIndicatorCustomIconsFolder\":\n                case \"ButtonIndicatorUseCustomIcons\":\n                    if (mySettings.ButtonIndicatorUseCustomIcons)\n                    {\n                        ImageResources.ReloadRessources(mySettings.ButtonIndicatorCustomIconsFolder);\n                    } else\n                    {\n                        ImageResources.ReloadRessources(null);\n                    }\n                    break;\n                case \"StandbyShortcut\":\n                    SetStandbyShortcut(mySettings.StandbyShortcut);\n                    break;\n                case \"Standby\":\n                    OnCursorIndicatorSettingChanged();\n                    OnButtonIndicatorSettingChanged();\n                    OnKeystrokeHistorySettingChanged();\n                    OnAnnotateLineSettingChanged();\n                    break;\n            }\n        }\n\n        #endregion\n\n        #region Keystroke History\n\n        KeystrokeDisplay KeystrokeHistoryWindow;\n        bool KeystrokeHistoryVisible;\n\n        private void OnKeystrokeHistorySettingChanged()\n        {\n            if (mySettings.EnableKeystrokeHistory && !mySettings.Standby)\n            {\n                EnableKeystrokeHistory();\n            }\n            else\n            {\n                DisableKeystrokeHistory();\n            }\n        }\n\n        private void EnableKeystrokeHistory()\n        {\n            if (KeystrokeHistoryVisible || KeystrokeHistoryWindow != null)\n                return;\n            KeystrokeHistoryVisible = true; // Prevent Recursive call\n            KeystrokeHistoryWindow = new KeystrokeDisplay(myKeystrokeConverter, mySettings);\n            KeystrokeHistoryWindow.Show();\n        }\n\n        private void DisableKeystrokeHistory()\n        {\n            KeystrokeHistoryVisible = false;\n            if (KeystrokeHistoryWindow == null)\n                return;\n            KeystrokeHistoryWindow.Close();\n            KeystrokeHistoryWindow = null;\n        }\n\n        #endregion\n\n        #region Button Indicator\n\n        ButtonIndicator1 ButtonIndicatorWindow = null;\n\n        private void OnButtonIndicatorSettingChanged()\n        {\n            if (mySettings.ButtonIndicator != ButtonIndicatorType.Disabled && !mySettings.Standby)\n            {\n                EnableButtonIndicator();\n            }\n            else\n            {\n                DisableButtonIndicator();\n            }\n        }\n\n\n        private void EnableButtonIndicator()\n        {\n            if (ButtonIndicatorWindow != null)\n                return;\n            Log.e(\"BI\", \"EnableButtonIndicator\");\n            EnableMouseHook();\n            ButtonIndicatorWindow = new ButtonIndicator1(myMouseHook, myKeystrokeConverter, mySettings);\n            ButtonIndicatorWindow.Show();\n        }\n\n\n        private void DisableButtonIndicator()\n        {\n            if (ButtonIndicatorWindow == null)\n                return;\n            ButtonIndicatorWindow.Close();\n            ButtonIndicatorWindow = null;\n            DisableMouseHookIfNotNeeded();\n            Log.e(\"BI\", \"DisableButtonIndicator\");\n        }\n\n        #endregion\n\n        #region Cursor Indicator\n\n        CursorIndicator1 CursorIndicatorWindow = null;\n\n        private void OnCursorIndicatorSettingChanged()\n        {\n            if (mySettings.EnableCursorIndicator && !mySettings.Standby)\n            {\n                EnableCursorIndicator();\n            }\n            else\n            {\n                DisableCursorIndicator();\n            }\n        }\n\n        \n        private void EnableCursorIndicator()\n        {\n            if (CursorIndicatorWindow != null)\n                return;\n            Log.e(\"CI\", \"EnableCursorIndicator\");\n            EnableMouseHook();\n            CursorIndicatorWindow = new CursorIndicator1(myMouseHook, mySettings);\n            CursorIndicatorWindow.Show();\n        }\n\n\n\n        private void DisableCursorIndicator()\n        {\n            if (CursorIndicatorWindow == null)\n                return;\n            CursorIndicatorWindow.Close();\n            CursorIndicatorWindow = null;\n            DisableMouseHookIfNotNeeded();\n            Log.e(\"CI\", \"DisableCursorIndicator\");\n        }\n\n\n        #endregion\n\n        #region Annotate Line\n\n        AnnotateLine AnnotateLineWindow;\n\n\n        private void OnAnnotateLineSettingChanged()\n        {\n            if (mySettings.EnableAnnotateLine && !mySettings.Standby)\n            {\n                EnableAnnotateLine();\n            }\n            else\n            {\n                DisableAnnotateLine();\n            }\n        }\n\n        private void EnableAnnotateLine()\n        {\n            if (AnnotateLineWindow != null)\n                return;\n            Log.e(\"AL\", \"EnableAnnotateLineWindow\");\n            EnableMouseHook();\n            AnnotateLineWindow = new AnnotateLine(myMouseHook, myKeystrokeConverter, mySettings);\n            AnnotateLineWindow.Show();\n        }\n\n\n\n        private void DisableAnnotateLine()\n        {\n            if (AnnotateLineWindow == null)\n                return;\n            AnnotateLineWindow.Close();\n            AnnotateLineWindow = null;\n            DisableMouseHookIfNotNeeded();\n            Log.e(\"AL\", \"DisableAnnotateLineWindow\");\n        }\n\n        #endregion\n\n        #region Mouse Hook\n\n        IMouseRawEventProvider myMouseHook = null;\n\n        private void EnableMouseHook()\n        {\n            if (myMouseHook != null)\n                return;\n            myMouseHook = new MouseHook(mySettings);\n        }\n\n        private void DisableMouseHookIfNotNeeded()\n        {\n            if (CursorIndicatorWindow == null && ButtonIndicatorWindow == null && AnnotateLineWindow == null)\n                DisableMouseHook();\n        }\n\n        private void DisableMouseHook()\n        {\n            if (myMouseHook == null)\n                return;\n            myMouseHook.Dispose();\n            myMouseHook = null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.Designer.cs",
    "content": "﻿namespace KeyNStroke\n{\n    partial class ButtonIndicator1\n    {\n        /// <summary>\n        /// Required designer variable.\n        /// </summary>\n        private System.ComponentModel.IContainer components = null;\n\n        /// <summary>\n        /// Clean up any resources being used.\n        /// </summary>\n        /// <param name=\"disposing\">true if managed resources should be disposed; otherwise, false.</param>\n        protected override void Dispose(bool disposing)\n        {\n            if (disposing && (components != null))\n            {\n                components.Dispose();\n            }\n            base.Dispose(disposing);\n        }\n\n        #region Windows Form Designer generated code\n\n        /// <summary>\n        /// Required method for Designer support - do not modify\n        /// the contents of this method with the code editor.\n        /// </summary>\n        private void InitializeComponent()\n        {\n            this.SuspendLayout();\n            // \n            // ButtonIndicator\n            // \n            this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 24F);\n            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;\n            this.ClientSize = new System.Drawing.Size(521, 482);\n            this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6);\n            this.Name = \"ButtonIndicator\";\n            this.ShowInTaskbar = false;\n            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;\n            this.Text = \"CursorIndicator\";\n            this.Load += new System.EventHandler(this.ButtonIndicator_Load);\n            this.ResumeLayout(false);\n\n        }\n\n        #endregion\n\n\n\n    }\n}"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Imaging;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows.Forms;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    public partial class ButtonIndicator1 : Form\n    {\n        IMouseRawEventProvider m;\n        IKeystrokeEventProvider k;\n        SettingsStore s;\n        ImageResources.ComposeOptions c;\n        Size bitmapsize;\n\n        public ButtonIndicator1(IMouseRawEventProvider m, IKeystrokeEventProvider k, SettingsStore s)\n        {\n            InitializeComponent();\n            StartImageResizeThread();\n\n            this.m = m;\n            this.s = s;\n            this.k = k;\n            FormClosed += ButtonIndicator_FormClosed;\n\n            HideMouseIfNoButtonPressed();\n\n            c.dpi = 96;\n            UpdatePosition(NativeMethodsMouse.CursorPosition);\n\n            NativeMethodsWindow.SetWindowTopMost(this.Handle);\n            SetFormStyles();\n\n            m.MouseEvent += m_MouseEvent;\n            k.KeystrokeEvent += k_KeystrokeEvent;\n            s.PropertyChanged += settingChanged;\n            DoubleClickIconTimer.Tick += leftDoubleClickIconTimeout_Tick;\n            DoubleClickIconTimer.Interval = 750;\n\n            WheelIconTimer.Interval = 750;\n            WheelIconTimer.Tick += WheelIconTimer_Tick;\n\n            UpdateSize(); // will call Redraw() after 200ms and scaling the images\n            //Redraw();\n\n        }\n\n        private void Redraw()\n        {\n            Bitmap scaledAndComposedBitmap = ImageResources.Compose(c);\n            this.bitmapsize = scaledAndComposedBitmap.Size;\n            IntPtr handle;\n            try\n            {\n                handle = this.Handle; // May be already disposed if leftDoubleClickIconTimeout triggers\n            }\n            catch (System.ObjectDisposedException)\n            {\n                return; // throw;\n            }\n\n            NativeMethodsDC.SetBitmapForWindow(handle,\n                                               this.Location,\n                                               scaledAndComposedBitmap,\n                                               1.0f);   // opacity\n            NativeMethodsWindow.PrintDpiAwarenessInfo();\n        }\n\n        private void ButtonIndicator_Load(object sender, EventArgs e)\n        {\n            Log.e(\"SOME\", \"ButtonIndicator => Load\");\n\n            RecalcOffset();\n            UpdateSize();\n        }\n\n        private void k_KeystrokeEvent(KeystrokeEventArgs e)\n        {\n            if (s == null) return;\n            bool changed = false;\n            if (s.ButtonIndicatorShowModifiers)\n            {\n                if (e.StrokeType == KeystrokeType.Modifiers)\n                {\n                    if (e.Shift != c.addMShift) changed = true;\n                    c.addMShift = e.Shift;\n                    if (e.Ctrl != c.addMCtrl) changed = true;\n                    c.addMCtrl = e.Ctrl;\n                    if (e.Alt != c.addMAlt) changed = true;\n                    c.addMAlt = e.Alt;\n                    if (e.Win != c.addMWin) changed = true;\n                    c.addMWin = e.Win;\n                }\n            }\n            else\n            {\n                if (c.addMShift) changed = true;\n                c.addMShift = false;\n                if (c.addMCtrl) changed = true;\n                c.addMCtrl = false;\n                if (c.addMAlt) changed = true;\n                c.addMAlt = false;\n                if (c.addMWin) changed = true;\n                c.addMWin = false;\n            }\n            if (changed)\n            {\n                Redraw();\n            }\n        }\n\n        MouseRawEventArgs lastDblClk;\n\n        void m_MouseEvent(MouseRawEventArgs raw_e)\n        {\n            if (s == null) return;\n            switch (raw_e.Action)\n            {\n                case MouseAction.Up:\n                    HideButton(raw_e);\n                    break;\n                case MouseAction.Down:\n                    ShowButton(raw_e.Button);\n                    break;\n                case MouseAction.DblClk:\n                    lastDblClk = raw_e;\n                    IndicateDoubleClick(raw_e.Button);\n                    break;\n                case MouseAction.Move:\n                    UpdatePosition(raw_e.Position);\n                    break;\n                case MouseAction.Wheel:\n                    IndicateWheel(raw_e);\n                    break;\n                default:\n                    break;\n            }\n        }\n\n\n        System.Windows.Forms.Timer WheelIconTimer = new System.Windows.Forms.Timer();\n\n        private void IndicateWheel(MouseRawEventArgs raw_e)\n        {\n            c.addBMouse = true;\n            Log.e(\"WHEEL\", \"Display \" + raw_e.wheelDelta.ToString());\n            WheelIconTimer.Stop();\n            WheelIconTimer.Start();\n            if (raw_e.wheelDelta < 0)\n            {\n                c.addBWheelDown = true;\n                c.addBWheelUp = false;\n            }\n            else if (raw_e.wheelDelta > 0)\n            {\n                c.addBWheelUp = true;\n                c.addBWheelDown = false;\n            }\n            Redraw();\n        }\n\n        void WheelIconTimer_Tick(object sender, EventArgs e)\n        {\n            WheelIconTimer.Stop();\n            Log.e(\"WHEEL\", \"Hide \");\n            c.addBWheelDown = false;\n            c.addBWheelUp = false;\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        private void IndicateDoubleClick(MouseButton mouseButton)\n        {\n            switch (mouseButton)\n            {\n                case MouseButton.LButton:\n                    c.addBMouse = true;\n                    c.addBLeft = false;\n                    c.addBLeftDouble = true;\n                    doubleClickReleased = false;\n                    Redraw();\n                    break;\n                case MouseButton.RButton:\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)\n        {\n            ((System.Windows.Forms.Timer)sender).Stop();\n            c.addBLeftDouble = false;\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        private void ShowButton(MouseButton mouseButton)\n        {\n            switch (mouseButton)\n            {\n                case MouseButton.LButton:\n                    c.addBMouse = true;\n                    c.addBLeft = true;\n                    c.addBLeftDouble = false;\n                    //UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.RButton:\n                    c.addBMouse = true;\n                    c.addBRight = true;\n                    //UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.MButton:\n                    c.addBMouse = true;\n                    c.addBMiddle = true;\n                    //UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.XButton:\n                    break;\n                case MouseButton.None:\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        System.Windows.Forms.Timer DoubleClickIconTimer = new System.Windows.Forms.Timer();\n        bool doubleClickReleased = true;\n\n        private void HideButton(MouseRawEventArgs raw_e)\n        {\n            switch (raw_e.Button)\n            {\n                case MouseButton.LButton:\n                    c.addBLeft = false;\n                    doubleClickReleased = true;\n                    if (c.addBLeftDouble)\n                    {\n                        if (raw_e.Msllhookstruct.time - lastDblClk.Msllhookstruct.time > DoubleClickIconTimer.Interval)\n                        {\n                            c.addBLeftDouble = false;\n                        }\n                        else\n                        {\n                            DoubleClickIconTimer.Stop();\n                            DoubleClickIconTimer.Start();\n                        }\n                    }\n                    break;\n                case MouseButton.RButton:\n                    c.addBRight = false;\n                    break;\n                case MouseButton.MButton:\n                    c.addBMiddle = false;\n                    break;\n                case MouseButton.XButton:\n                    break;\n                case MouseButton.None:\n                    break;\n                default:\n                    break;\n            }\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        void HideMouseIfNoButtonPressed()\n        {\n            if (   !c.addBLeft\n                && !c.addBRight\n                && !c.addBMiddle\n                && !c.addBLeftDouble\n                && !c.addBRightDouble\n                && !c.addBWheelDown\n                && !c.addBWheelUp)\n            {\n                c.addBMouse = false;\n            }\n        }\n\n        void ButtonIndicator_FormClosed(object sender, FormClosedEventArgs e)\n        {\n            if (m != null)\n                m.MouseEvent -= m_MouseEvent;\n            if (s != null)\n                s.PropertyChanged -= settingChanged;\n            if (k != null)\n                k.KeystrokeEvent -= k_KeystrokeEvent;\n            if (newScalingFactors != null)\n                newScalingFactors.CompleteAdding();\n            m = null;\n            s = null;\n        }\n\n        void SetFormStyles()\n        {\n            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;\n            //this.Opacity = 0.8;\n            NativeMethodsGWL.ClickThrough(this.Handle); // This also makes the window style WS_EX_LAYERED\n            NativeMethodsGWL.HideFromAltTab(this.Handle);\n\n            UpdateSize();\n            UpdatePosition(NativeMethodsMouse.CursorPosition);\n        }\n\n\n        Thread imagesResizeThread;\n        BlockingCollection<double> newScalingFactors = new BlockingCollection<double>(new ConcurrentQueue<double>());\n\n        private void StartImageResizeThread()\n        {\n            imagesResizeThread = new Thread(new ThreadStart(BackgroundResizeImages));\n            imagesResizeThread.Start();\n        }\n\n\n        void BackgroundResizeImages()\n        {\n            while (true)\n            {\n                try\n                {\n                    double scalingFactor = newScalingFactors.Take(); // Blocks until at least one new Item is available\n                    Thread.Sleep(200); // wait for more data, so we don't do too eager calculations\n                    // skip all elements except for the last\n                    while (newScalingFactors.Count > 0)\n                    {\n                        scalingFactor = newScalingFactors.Take();\n                    }\n                    Log.e(\"BI\", $\"size change applied: {scalingFactor}\");\n                    ImageResources.ApplyScalingFactor(scalingFactor);\n                    this.Invoke((Action) (() =>\n                    {\n                        Redraw();\n                        UpdatePosition(NativeMethodsMouse.CursorPosition);\n                    }));\n                }\n                catch (InvalidOperationException)\n                {\n                    // Window closed\n                    Log.e(\"BI\", \"BackgrounResizeThread stopped\");\n                    return;\n                }\n            }\n        }\n\n\n        void UpdateSize()\n        {\n            newScalingFactors.Add(s.ButtonIndicatorScaling);\n        }\n\n        Size offset = new Size(0, 0);\n\n        void RecalcOffset()\n        {\n            offset.Width = (int)(s.ButtonIndicatorPositionDistance * Math.Sin(s.ButtonIndicatorPositionAngle / 10.0f));\n            offset.Height = (int)(s.ButtonIndicatorPositionDistance * Math.Cos(s.ButtonIndicatorPositionAngle / 10.0f));\n        }\n\n        void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)\n        {\n            if (OnlyDblClkIconVisible() && doubleClickReleased)\n                return;\n            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);\n            uint adpiX = 0, adpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);\n            c.dpi = adpiX;\n            NativeMethodsWindow.SetWindowPosition(this.Handle, cursorPosition.X - this.bitmapsize.Width/2 + offset.Width, cursorPosition.Y - this.bitmapsize.Height/2 + offset.Height);\n\n        }\n\n        private bool OnlyDblClkIconVisible()\n        {\n            return !c.addBLeft\n                && !c.addBRight\n                && !c.addBMiddle\n                && !c.addBWheelUp\n                && !c.addBWheelDown\n                && (c.addBLeftDouble\n               || c.addBRightDouble);\n        }\n\n        private void settingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            if (s == null) return;\n            // Log.e(\"BI\", $\"ButtonIndicator => settingChanged {e.PropertyName}\");\n            switch (e.PropertyName)\n            {\n                case \"ButtonIndicator\":\n                    break;\n                case \"ButtonIndicatorPositionAngle\":\n                    RecalcOffset();\n                    UpdatePosition(NativeMethodsMouse.CursorPosition);\n                    break;\n                case \"ButtonIndicatorPositionDistance\":\n                    RecalcOffset();\n                    UpdatePosition(NativeMethodsMouse.CursorPosition);\n                    break;\n                case \"ButtonIndicatorScaling\":\n                    UpdateSize();\n                    break;\n            }\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/ButtonIndicator1.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "KeyNStroke/ButtonIndicator2.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.ButtonIndicator2\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"ButtonIndicator\" Height=\"100\" Width=\"100\"\n        AllowsTransparency=\"True\" \n        WindowStyle='None' \n        Background=\"Transparent\" ResizeMode=\"NoResize\" ShowInTaskbar=\"False\" IsEnabled=\"False\">\n    <Ellipse Width=\"100\" Height=\"100\" Fill=\"Red\" Opacity=\"0.5\"/>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/ButtonIndicator2.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawing;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\nusing System.Windows.Threading;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für ButtonIndicator2.xaml\n    /// </summary>\n    public partial class ButtonIndicator2 : Window\n    {\n\n        IMouseRawEventProvider m;\n        SettingsStore s;\n        ImageResources.ComposeOptions c;\n        IntPtr handle;\n\n        public ButtonIndicator2(IMouseRawEventProvider m, SettingsStore s)\n        {\n            InitializeComponent();\n            this.m = m;\n            this.s = s;\n            this.Closed += this.Window_Closed;\n            this.Loaded += Window_Loaded;\n            \n\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            this.handle = new WindowInteropHelper(this).Handle;\n            HideMouseIfNoButtonPressed();\n\n            c.dpi = 96;\n            UpdatePosition();\n\n            NativeMethodsWindow.SetWindowTopMost(handle);\n            SetFormStyles();\n\n            m.MouseEvent += m_MouseEvent;\n            s.PropertyChanged += settingChanged;\n            DoubleClickIconTimer.Tick += leftDoubleClickIconTimeout_Tick;\n            DoubleClickIconTimer.Interval = TimeSpan.FromMilliseconds(750.0);\n\n            WheelIconTimer.Interval = TimeSpan.FromMilliseconds(750.0);\n            WheelIconTimer.Tick += WheelIconTimer_Tick;\n\n\n            Redraw();\n        }\n\n\n        protected override void OnRender(DrawingContext drawingContext)\n        {\n            base.OnRender(drawingContext);\n        }\n\n        private void Redraw()\n        {\n            Bitmap scaledAndComposedBitmap = ImageResources.Compose(c);\n            Log.e(\"DPI\", $\"DPI at Redraw: {c.dpi}\");\n            NativeMethodsDC.SetBitmapForWindow(this.handle,\n                                               new System.Drawing.Point((int)this.Left, (int)this.Top),\n                                               scaledAndComposedBitmap,\n                                               1.0f);   // opacity\n        }\n\n        private void ButtonIndicator_Load(object sender, EventArgs e)\n        {\n            Log.e(\"SOME\", \"ButtonIndicator => Load\");\n\n            RecalcOffset();\n            UpdateSize();\n        }\n\n        //System.Drawing.Point cursorPosition;\n        MouseRawEventArgs lastDblClk;\n\n        void m_MouseEvent(MouseRawEventArgs raw_e)\n        {\n            // cursorPosition = raw_e.Position; this position does not work nice with multiple monitor/dpi setups\n            switch (raw_e.Action)\n            {\n                case MouseAction.Up:\n                    HideButton(raw_e);\n                    break;\n                case MouseAction.Down:\n                    ShowButton(raw_e.Button);\n                    break;\n                case MouseAction.DblClk:\n                    lastDblClk = raw_e;\n                    IndicateDoubleClick(raw_e.Button);\n                    break;\n                case MouseAction.Move:\n                    UpdatePosition();\n                    break;\n                case MouseAction.Wheel:\n                    IndicateWheel(raw_e);\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        DispatcherTimer WheelIconTimer = new DispatcherTimer();\n\n        private void IndicateWheel(MouseRawEventArgs raw_e)\n        {\n            c.addBMouse = true;\n            Log.e(\"WHEEL\", \"Display \" + raw_e.wheelDelta.ToString());\n            WheelIconTimer.Stop();\n            WheelIconTimer.Start();\n            if (raw_e.wheelDelta < 0)\n            {\n                c.addBWheelDown = true;\n                c.addBWheelUp = false;\n            }\n            else if (raw_e.wheelDelta > 0)\n            {\n                c.addBWheelUp = true;\n                c.addBWheelDown = false;\n            }\n            Redraw();\n        }\n\n        void WheelIconTimer_Tick(object sender, EventArgs e)\n        {\n            WheelIconTimer.Stop();\n            Log.e(\"WHEEL\", \"Hide \");\n            c.addBWheelDown = false;\n            c.addBWheelUp = false;\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        private void IndicateDoubleClick(MouseButton mouseButton)\n        {\n            switch (mouseButton)\n            {\n                case MouseButton.LButton:\n                    c.addBMouse = true;\n                    c.addBLeft = false;\n                    c.addBLeftDouble = true;\n                    doubleClickReleased = false;\n                    Redraw();\n                    break;\n                case MouseButton.RButton:\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        void leftDoubleClickIconTimeout_Tick(object sender, EventArgs e)\n        {\n            ((DispatcherTimer)sender).Stop();\n            c.addBLeftDouble = false;\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        private void ShowButton(MouseButton mouseButton)\n        {\n            switch (mouseButton)\n            {\n                case MouseButton.LButton:\n                    c.addBMouse = true;\n                    c.addBLeft = true;\n                    c.addBLeftDouble = false;\n                    UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.RButton:\n                    c.addBMouse = true;\n                    c.addBRight = true;\n                    UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.MButton:\n                    c.addBMouse = true;\n                    c.addBMiddle = true;\n                    UpdatePosition();\n                    Redraw();\n                    break;\n                case MouseButton.XButton:\n                    break;\n                case MouseButton.None:\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        DispatcherTimer DoubleClickIconTimer = new DispatcherTimer();\n        bool doubleClickReleased = true;\n\n        private void HideButton(MouseRawEventArgs raw_e)\n        {\n            switch (raw_e.Button)\n            {\n                case MouseButton.LButton:\n                    c.addBLeft = false;\n                    doubleClickReleased = true;\n                    if (c.addBLeftDouble)\n                    {\n                        if (raw_e.Msllhookstruct.time - lastDblClk.Msllhookstruct.time > DoubleClickIconTimer.Interval.TotalMilliseconds)\n                        {\n                            c.addBLeftDouble = false;\n                        }\n                        else\n                        {\n                            DoubleClickIconTimer.Stop();\n                            DoubleClickIconTimer.Start();\n                        }\n                    }\n                    break;\n                case MouseButton.RButton:\n                    c.addBRight = false;\n                    break;\n                case MouseButton.MButton:\n                    c.addBMiddle = false;\n                    break;\n                case MouseButton.XButton:\n                    break;\n                case MouseButton.None:\n                    break;\n                default:\n                    break;\n            }\n            HideMouseIfNoButtonPressed();\n            Redraw();\n        }\n\n        void HideMouseIfNoButtonPressed()\n        {\n            if (!c.addBLeft\n                && !c.addBRight\n                && !c.addBMiddle\n                && !c.addBLeftDouble\n                && !c.addBRightDouble\n                && !c.addBWheelDown\n                && !c.addBWheelUp)\n            {\n                c.addBMouse = false;\n            }\n        }\n\n        private void Window_Closed(object sender, EventArgs e)\n        {\n            if (m != null)\n                m.MouseEvent -= m_MouseEvent;\n            if (s != null)\n                s.PropertyChanged -= settingChanged;\n            m = null;\n            s = null;\n        }\n\n        void SetFormStyles()\n        {\n            //this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;\n            //this.Opacity = 0.8;\n            NativeMethodsGWL.ClickThrough(this.handle); // This also makes the window style WS_EX_LAYERED\n            NativeMethodsGWL.HideFromAltTab(this.handle);\n\n            UpdateSize();\n            UpdatePosition();\n        }\n\n\n\n        void UpdateSize()\n        {\n            ImageResources.ApplyScalingFactor(s.ButtonIndicatorScalingPercentage / 100.0f);\n            Log.e(\"BI\", \"size change\");\n            Redraw();\n        }\n\n        System.Drawing.Size offset = new System.Drawing.Size(0, 0); // dpi independend\n\n        void RecalcOffset()\n        {\n            offset.Width = (int)(s.ButtonIndicatorPositionDistance * Math.Sin(s.ButtonIndicatorPositionAngle / 10.0f));\n            offset.Height = (int)(s.ButtonIndicatorPositionDistance * Math.Cos(s.ButtonIndicatorPositionAngle / 10.0f));\n        }\n\n        void UpdatePosition()\n        {\n            if (OnlyDblClkIconVisible() && doubleClickReleased)\n                return;\n            NativeMethodsMouse.POINT cursorPosition = new NativeMethodsMouse.POINT(0, 0);\n            NativeMethodsWindow.GetCursorPos(ref cursorPosition);\n            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);\n\n            // Angular DPI seems to work best\n            //uint edpiX = 0, edpiY = 0;\n            //NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref edpiX, ref edpiY);\n            uint adpiX = 0, adpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY);\n            //uint rdpiX = 0, rdpiY = 0;\n            //NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_RAW_DPI, ref rdpiX, ref rdpiY);\n\n            c.dpi = adpiX;\n\n\n            NativeMethodsWindow.SetWindowPosition(this.handle, cursorPosition.X, cursorPosition.Y);\n\n        }\n\n        private bool OnlyDblClkIconVisible()\n        {\n            return !c.addBLeft\n                && !c.addBRight\n                && !c.addBMiddle\n                && !c.addBWheelUp\n                && !c.addBWheelDown\n                && (c.addBLeftDouble\n               || c.addBRightDouble);\n        }\n\n        private void settingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            Log.e(\"SOME\", \"ButtonIndicator => settingChanged\");\n            switch (e.PropertyName)\n            {\n                case \"ButtonIndicator\":\n                    break;\n                case \"ButtonIndicatorPositionAngle\":\n                    RecalcOffset();\n                    UpdatePosition();\n                    break;\n                case \"ButtonIndicatorPositionDistance\":\n                    RecalcOffset();\n                    UpdatePosition();\n                    break;\n                case \"ButtonIndicatorScalingPercentage\":\n                    UpdateSize();\n                    UpdatePosition();\n                    break;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/CursorIndicator1.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.CursorIndicator1\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"CursorIndicator1\"\n        Height=\"450\"\n        Width=\"800\"\n        Loaded=\"Window_Loaded\"\n        Closed=\"Window_Closed\"\n        AllowsTransparency=\"True\"\n        Topmost=\"True\"\n        ShowInTaskbar=\"False\"\n        WindowStyle=\"None\"\n        ResizeMode=\"NoResize\"\n        Background=\"Transparent\">\n    <Ellipse x:Name=\"circle\"\n             Fill=\"#7FEF7E7E\"></Ellipse>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/CursorIndicator1.xaml.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing static KeyNStroke.NativeMethodsMouse;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für CursorIndicator1.xaml\n    /// </summary>\n    public partial class CursorIndicator1 : Window\n    {\n        IMouseRawEventProvider m;\n        SettingsStore s;\n        IntPtr windowHandle;\n        bool isHidden;\n        bool isDown;\n\n        public CursorIndicator1(IMouseRawEventProvider m, SettingsStore s)\n        {\n            InitializeComponent();\n\n            this.m = m;\n            this.s = s;\n\n            s.PropertyChanged += settingChanged;\n            this.isHidden = false;\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            m.MouseEvent += m_MouseEvent;\n            m.CursorEvent += m_CursorEvent;\n            windowHandle = new WindowInteropHelper(this).Handle;\n            SetFormStyles();\n        }\n\n        void m_MouseEvent(MouseRawEventArgs raw_e)\n        {\n            if (raw_e.Action == MouseAction.Move)\n            {\n                UpdatePosition(raw_e.Position);\n            }\n            else if (raw_e.Action == MouseAction.Down || raw_e.Action == MouseAction.DblClk)\n            {\n                isDown = true;\n                UpdateColor();\n            }\n            else if (raw_e.Action == MouseAction.Up)\n            {\n                isDown = false;\n                UpdateColor();\n            }\n        }\n\n        void m_CursorEvent(bool visible)\n        {\n            if (visible)\n            {\n                if (isHidden)\n                {\n                    this.Show();\n                    isHidden = false;\n                }\n            }\n            else\n            {\n                if (!isHidden)\n                {\n                    this.Hide();\n                    isHidden = true;\n                }\n            }\n        }\n\n        void SetFormStyles()\n        {\n            //this.Opacity = s.CursorIndicatorOpacity;\n            Log.e(\"CI\", $\"WindowHandle={windowHandle}\");\n            NativeMethodsGWL.ClickThrough(windowHandle);\n            NativeMethodsGWL.HideFromAltTab(windowHandle);\n\n            UpdateSize();\n            UpdateColor();\n            UpdatePosition(NativeMethodsMouse.CursorPosition);\n        }\n\n        void UpdateSize()\n        {\n            this.Width = s.CursorIndicatorSize;\n            this.Height = s.CursorIndicatorSize;\n        }\n\n        void UpdatePosition(NativeMethodsMouse.POINT cursorPosition)\n        {\n            IntPtr monitor = NativeMethodsWindow.MonitorFromPoint(cursorPosition, NativeMethodsWindow.MonitorOptions.MONITOR_DEFAULTTONEAREST);\n            uint adpiX = 0, adpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref adpiX, ref adpiY);\n            Log.e(\"CI\", $\"apix={adpiX} adpiy={adpiY} aw={ActualWidth} ah={ActualHeight} cx={cursorPosition.X} cy={cursorPosition.Y}\");\n            NativeMethodsWindow.SetWindowPosition(windowHandle, \n                    (int)(cursorPosition.X - (this.ActualWidth / 2) * (double)adpiX / 96.0),\n                    (int)(cursorPosition.Y - (this.ActualHeight / 2) * (double)adpiY / 96.0));\n        }\n\n        private void settingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            this.Dispatcher.BeginInvoke((Action) (() =>\n            {\n                if (s == null) return;\n                switch (e.PropertyName)\n                {\n                    case \"EnableCursorIndicator\":\n                        break;\n                    case \"CursorIndicatorOpacity\":\n                        UpdateColor();\n                        break;\n                    case \"CursorIndicatorSize\":\n                        UpdateSize();\n                        break;\n                    case \"CursorIndicatorColor\":\n                        UpdateColor();\n                        break;\n                    case \"CursorIndicatorEdgeColor\":\n                        UpdateColor();\n                        break;\n                    case \"CursorIndicatorEdgeStrokeThickness\":\n                        UpdateColor();\n                        break;\n                    case \"CursorIndicatorDrawEdge\":\n                        UpdateColor();\n                        break;\n                }\n            }));\n        }\n\n        private void UpdateColor()\n        {\n            Color c;\n            if (isDown && s.CursorIndicatorFlashOnClick)\n            {\n                c = UIHelper.ToMediaColor(s.CursorIndicatorClickColor);\n            }\n            else\n            {\n                c = UIHelper.ToMediaColor(s.CursorIndicatorColor);\n            }\n            circle.Fill = new SolidColorBrush(Color.FromArgb((byte)(255 * (1 - s.CursorIndicatorOpacity)), c.R, c.G, c.B));\n            if (s.CursorIndicatorDrawEdge)\n            {\n                circle.Stroke = new SolidColorBrush(UIHelper.ToMediaColor(s.CursorIndicatorEdgeColor));\n                circle.StrokeThickness = s.CursorIndicatorEdgeStrokeThickness;\n            }\n            else\n            {\n                circle.Stroke = null;\n                circle.StrokeThickness = 0;\n            }\n        }\n\n\n        private void Window_Closed(object sender, EventArgs e)\n        {\n            if (m != null)\n            {\n                m.MouseEvent -= m_MouseEvent;\n                m.CursorEvent -= m_CursorEvent;\n            }\n            if (s != null)\n                s.PropertyChanged -= settingChanged;\n            m = null;\n            s = null;\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/EnumBindingSourceExtention.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing System.Windows.Markup;\n\nnamespace KeyNStroke\n{\n    public class EnumBindingSourceExtention : MarkupExtension\n    {\n        public Type EnumType { get; private set; }\n\n        public EnumBindingSourceExtention(Type enumType)\n        {\n            if (enumType == null || !enumType.IsEnum)\n                throw new Exception(\"enumType must be of type enum\");\n            this.EnumType = enumType;\n        }\n\n        public static T GetAttributeOfType<T>(Enum enumVal) where T : System.Attribute\n        {\n            var type = enumVal.GetType();\n            var memInfo = type.GetMember(Enum.GetName(type, enumVal)); // type.GetMember(enumVal.ToString());\n            var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);\n            return (attributes.Length > 0) ? (T)attributes[0] : null;\n        }\n\n        public static string GetAttributeDescription(Enum enumValue)\n        {\n            var attribute = GetAttributeOfType<System.ComponentModel.DescriptionAttribute>(enumValue);\n            return attribute == null ? String.Empty : attribute.Description;\n        }\n\n        public override object ProvideValue(IServiceProvider serviceProvider)\n        {\n            Array variants = Enum.GetValues(EnumType);\n            List<ComboBoxItem> b = new List<ComboBoxItem>(variants.Length);\n            foreach (var a in variants)\n            {\n                ComboBoxItem i = new ComboBoxItem();\n                i.Content = GetAttributeDescription((Enum)a); // new TextBlock(new Run(\"Hi\"));\n                i.Tag = a;\n                b.Add(i);\n            }\n            return b;\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/FodyWeavers.xml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Weavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"FodyWeavers.xsd\">\n  <Costura />\n</Weavers>"
  },
  {
    "path": "KeyNStroke/FodyWeavers.xsd",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->\n  <xs:element name=\"Weavers\">\n    <xs:complexType>\n      <xs:all>\n        <xs:element name=\"Costura\" minOccurs=\"0\" maxOccurs=\"1\">\n          <xs:complexType>\n            <xs:all>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"ExcludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <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>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"IncludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <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>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"ExcludeRuntimeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <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>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"IncludeRuntimeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <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>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"PreloadOrder\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n            </xs:all>\n            <xs:attribute name=\"CreateTemporaryAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeDebugSymbols\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeRuntimeReferences\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Controls if (.NET Core) runtime assemblies are also embedded.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCompression\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCleanup\" type=\"xs:boolean\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"LoadAtModuleInit\" type=\"xs:boolean\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IgnoreSatelliteAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"ExcludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to exclude from the default action of \"embed all Copy Local references\", delimited with |</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to include from the default action of \"embed all Copy Local references\", delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"ExcludeRuntimeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeRuntimeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <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>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"PreloadOrder\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n          </xs:complexType>\n        </xs:element>\n      </xs:all>\n      <xs:attribute name=\"VerifyAssembly\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"VerifyIgnoreCodes\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"GenerateXsd\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n</xs:schema>"
  },
  {
    "path": "KeyNStroke/ImageResources.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.IO;\nusing System.Reflection;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Drawing.Drawing2D;\nusing KeyNStroke;\nusing System.Windows.Forms.VisualStyles;\nusing System.Windows;\n\nnamespace KeyNStroke\n{\n    public class ImageResources\n    {\n        static Assembly _assembly;\n\n        private struct BitmapCollection\n        {\n            public Bitmap BMouse;\n            public Bitmap BLeft;\n            public Bitmap BRight;\n            public Bitmap BMiddle;\n            public Bitmap BLeftDouble;\n            public Bitmap BRightDouble;\n            public Bitmap BWheelUp;\n            public Bitmap BWheelDown;\n            public Bitmap MCtrl;\n            public Bitmap MWin;\n            public Bitmap MAlt;\n            public Bitmap MShift;\n        }\n\n        static Dictionary<uint, BitmapCollection> ScaledByDpi;\n        static BitmapCollection Orig; // original size\n\n        public static void Init(string customIconFolder)\n        {\n            try\n            {\n                _assembly = Assembly.GetExecutingAssembly();\n\n                foreach (string i in _assembly.GetManifestResourceNames())\n                {\n                    Log.e(\"RES\", i);\n                }\n\n                ReloadRessources(customIconFolder);\n            }\n            catch\n            {\n                Log.e(\"RES\", \"Error accessing resources!\");\n            }\n\n            ApplyScalingFactor(1.0f);\n        }\n\n        public static void ExportBuiltinRessources(string exportFolder)\n        {\n            string[] filenames = { \"mouse.svg\",\n                                   \"mouse.png\",\n                                   \"mouse_left.png\",\n                                   \"mouse_right.png\",\n                                   \"mouse_middle.png\",\n                                   \"mouse_left_double.png\",\n                                   \"mouse_right_double.png\",\n                                   \"mouse_wheel_up.png\",\n                                   \"mouse_wheel_down.png\",\n                                   \"mouse_modifier_ctrl.png\",\n                                   \"mouse_modifier_win.png\",\n                                   \"mouse_modifier_alt.png\",\n                                   \"mouse_modifier_shift.png\" };\n            foreach (string png_name in filenames)\n            {\n                try\n                {\n\n                    string target = Path.Combine(exportFolder, png_name);\n                    if (File.Exists(target))\n                    {\n                        var result = MessageBox.Show($\"Overwrite {png_name}?\", $\"File already exists\", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);\n                        if (result == MessageBoxResult.Cancel)\n                        {\n                            return;\n                        }\n                        else if (result == MessageBoxResult.No)\n                        {\n                            continue;\n                        }\n                        else\n                        {\n                            // Yes -> Overwrite\n                        }\n                    }\n                    using (var fs = new FileStream(target, FileMode.Create, FileAccess.Write))\n                    {\n                        _assembly.GetManifestResourceStream($\"KeyNStroke.Resources.{png_name}\").CopyTo(fs);\n                    }\n                }\n                catch (Exception)\n                {\n\n                }\n            }\n        }\n\n\n        public static void ReloadRessources(string customIconFolder)\n        {\n            // FIXME: This happens three times on startup. \n\n            Orig.BMouse = LoadRessource(customIconFolder, \"mouse.png\");\n            Orig.BLeft = LoadRessource(customIconFolder, \"mouse_left.png\");\n            Orig.BRight = LoadRessource(customIconFolder, \"mouse_right.png\");\n            Orig.BMiddle = LoadRessource(customIconFolder, \"mouse_middle.png\");\n            Orig.BLeftDouble = LoadRessource(customIconFolder, \"mouse_left_double.png\");\n            Orig.BRightDouble = LoadRessource(customIconFolder, \"mouse_right_double.png\");\n            Orig.BWheelUp = LoadRessource(customIconFolder, \"mouse_wheel_up.png\");\n            Orig.BWheelDown = LoadRessource(customIconFolder, \"mouse_wheel_down.png\");\n            Orig.MCtrl = LoadRessource(customIconFolder, \"mouse_modifier_ctrl.png\");\n            Orig.MWin = LoadRessource(customIconFolder, \"mouse_modifier_win.png\");\n            Orig.MAlt = LoadRessource(customIconFolder, \"mouse_modifier_alt.png\");\n            Orig.MShift = LoadRessource(customIconFolder, \"mouse_modifier_shift.png\");\n\n            RefreshScalingCache();\n        }\n\n        private static Bitmap LoadRessource(string customIconFolder, string png_name)\n        {\n            if (customIconFolder != null)\n            {\n                try\n                {\n                    string customIconPath = Path.Combine(customIconFolder, png_name);\n                    if (File.Exists(customIconPath))\n                    {\n                        using (var fs = new FileStream(customIconPath, FileMode.Open, FileAccess.Read))\n                        {\n                            return new Bitmap(fs);\n                        }\n                    }\n                }\n                catch (Exception)\n                {\n\n                }\n            }\n            return new Bitmap(_assembly.GetManifestResourceStream($\"KeyNStroke.Resources.{png_name}\"));\n        }\n\n\n        static double appliedScalingFactor = -1.0;\n\n        public static void ApplyScalingFactor(double scalingfactor)\n        {\n            scalingfactor = Math.Min(2f, Math.Max(0.1f, scalingfactor));\n\n            if (appliedScalingFactor != scalingfactor)\n            {\n                appliedScalingFactor = scalingfactor;\n                RefreshScalingCache();\n            }\n        }\n\n        private static void RefreshScalingCache()\n        {\n            if (appliedScalingFactor == -1.0)\n                return;\n\n            var newByDpi = new Dictionary<uint, BitmapCollection>();\n\n            List<uint> dpis = NativeMethodsWindow.GetAllUsedDpis();\n\n            foreach (uint dpi in dpis)\n            {\n                if (!newByDpi.ContainsKey(dpi)) { \n                    newByDpi.Add(dpi, CreateScaledBitmapCollection((float)appliedScalingFactor, dpi));\n                }\n            }\n\n            ScaledByDpi = newByDpi;\n            lastComposedBitmap = null; // Force regeneration of Image\n        }\n\n\n        private static BitmapCollection CreateScaledBitmapCollection(float scalingFactor, uint dpi)\n        {\n            BitmapCollection scaled = new BitmapCollection\n            {\n                BMouse = Scale(scalingFactor, Orig.BMouse, dpi),\n                BLeft = Scale(scalingFactor, Orig.BLeft, dpi),\n                BRight = Scale(scalingFactor, Orig.BRight, dpi),\n                BMiddle = Scale(scalingFactor, Orig.BMiddle, dpi),\n                BLeftDouble = Scale(scalingFactor, Orig.BLeftDouble, dpi),\n                BRightDouble = Scale(scalingFactor, Orig.BRightDouble, dpi),\n                BWheelUp = Scale(scalingFactor, Orig.BWheelUp, dpi),\n                BWheelDown = Scale(scalingFactor, Orig.BWheelDown, dpi),\n                MCtrl = Scale(scalingFactor, Orig.MCtrl, dpi),\n                MWin = Scale(scalingFactor, Orig.MWin, dpi),\n                MAlt = Scale(scalingFactor, Orig.MAlt, dpi),\n                MShift = Scale(scalingFactor, Orig.MShift, dpi)\n            };\n\n            return scaled;\n        }\n\n        private static Bitmap Scale(float scalingFactor, Bitmap original, uint dpi)\n        {\n            var dpiScale = (float)dpi / 96.0f;\n            scalingFactor *= dpiScale;\n            var scaledWidth = (int)(original.Width * scalingFactor);\n            Log.e(\"DPI\", $\"Scale: DPI: {dpi}, dpiScaleFactor:{dpiScale}, scaledWidth:{scaledWidth}, totalScalingFactor:{scalingFactor}\");\n            var scaledHeight = (int)(original.Height * scalingFactor);\n            var scaledBitmap = new Bitmap(scaledWidth, scaledHeight, PixelFormat.Format32bppArgb);\n            // Draw original image onto new bitmap and interpolate\n            Graphics graph = Graphics.FromImage(scaledBitmap);\n            graph.InterpolationMode = InterpolationMode.High;\n            graph.CompositingQuality = CompositingQuality.HighQuality;\n            graph.SmoothingMode = SmoothingMode.AntiAlias;\n            graph.DrawImage(original, new Rectangle(0, 0, scaledWidth, scaledHeight));\n            return scaledBitmap;\n        }\n\n        public struct ComposeOptions\n        {\n            public uint dpi;\n            public bool addBMouse;\n            public bool addBLeft;\n            public bool addBRight;\n            public bool addBMiddle;\n            public bool addBLeftDouble;\n            public bool addBRightDouble;\n            public bool addBWheelUp;\n            public bool addBWheelDown;\n            public bool addMCtrl;\n            public bool addMAlt;\n            public bool addMWin;\n            public bool addMShift;\n\n            public static bool operator ==(ComposeOptions first, ComposeOptions second)\n            {\n                return Equals(first, second);\n            }\n\n            public static bool operator !=(ComposeOptions first, ComposeOptions second)\n            {\n                return !(first == second);\n            }\n\n            public override string ToString()\n            {\n                String s = \"\";\n                if (addBMouse) s += \"Mouse \";\n                if (addBLeft) s += \"Left \";\n                if (addBRight) s += \"Right \";\n                if (addBMiddle) s += \"Middle \";\n                if (addBLeftDouble) s += \"LeftDouble \";\n                if (addBRightDouble) s += \"RightDouble \";\n                if (addBWheelUp) s += \"WheelUp \";\n                if (addBWheelDown) s += \"WheelDown \";\n                if (addMCtrl) s += \"Ctrl \";\n                if (addMAlt) s += \"Alt \";\n                if (addMWin) s += \"Win \";\n                if (addMShift) s += \"Shift \";\n                return s;\n            }\n\n            public override bool Equals(object obj)\n            {\n                return base.Equals(obj);\n            }\n\n            public override int GetHashCode()\n            {\n                return base.GetHashCode();\n            }\n        }\n\n        static ComposeOptions lastComposeOptions;\n        static Bitmap lastComposedBitmap;\n\n\n        public static Bitmap Compose(ComposeOptions c)\n        {\n\n            if (lastComposedBitmap != null && (c == lastComposeOptions))\n            {\n                return lastComposedBitmap;\n            }\n\n\n\n            var byDpi = ScaledByDpi; // take reference to prevent datarace with update/replace logic on scaling factor change\n            if (!byDpi.ContainsKey(c.dpi))\n            {\n                byDpi.Add(c.dpi, CreateScaledBitmapCollection((float)appliedScalingFactor, c.dpi));\n            }\n\n            BitmapCollection scaled = byDpi[c.dpi];\n            Log.e(\"DPI\", $\"COMPOSE! {c.dpi}, {c}, {scaled.BMouse.Size.Width}\");\n\n            var targetBitmap = new Bitmap(scaled.BMouse.Size.Width, scaled.BMouse.Size.Height, PixelFormat.Format32bppArgb);\n            Graphics graph = Graphics.FromImage(targetBitmap);\n\n            if (c.addBMouse) graph.DrawImageUnscaled(scaled.BMouse, 0, 0);\n            if (c.addBLeft) graph.DrawImageUnscaled(scaled.BLeft, 0, 0);\n            if (c.addBRight) graph.DrawImageUnscaled(scaled.BRight, 0, 0);\n            if (c.addBMiddle) graph.DrawImageUnscaled(scaled.BMiddle, 0, 0);\n            if (c.addBLeftDouble) graph.DrawImageUnscaled(scaled.BLeftDouble, 0, 0);\n            if (c.addBRightDouble) graph.DrawImageUnscaled(scaled.BRightDouble, 0, 0);\n            if (c.addBWheelUp) graph.DrawImageUnscaled(scaled.BWheelUp, 0, 0);\n            if (c.addBWheelDown) graph.DrawImageUnscaled(scaled.BWheelDown, 0, 0);\n            if (c.addMCtrl) graph.DrawImageUnscaled(scaled.MCtrl, 0, 0);\n            if (c.addMWin) graph.DrawImageUnscaled(scaled.MWin, 0, 0);\n            if (c.addMAlt) graph.DrawImageUnscaled(scaled.MAlt, 0, 0);\n            if (c.addMShift) graph.DrawImageUnscaled(scaled.MShift, 0, 0);\n\n            lastComposeOptions = c;\n            lastComposedBitmap = targetBitmap;\n            return targetBitmap;\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/KeyNStroke.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\Costura.Fody.5.0.2\\build\\Costura.Fody.props\" Condition=\"Exists('..\\packages\\Costura.Fody.5.0.2\\build\\Costura.Fody.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{BA117557-FEBC-45C5-8895-4017D05C3DCD}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>KeyNStroke</RootNamespace>\n    <AssemblyName>Key-n-Stroke</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <IsWebBootstrapper>false</IsWebBootstrapper>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n    <PublishUrl>publish\\</PublishUrl>\n    <Install>true</Install>\n    <InstallFrom>Disk</InstallFrom>\n    <UpdateEnabled>false</UpdateEnabled>\n    <UpdateMode>Foreground</UpdateMode>\n    <UpdateInterval>7</UpdateInterval>\n    <UpdateIntervalUnits>Days</UpdateIntervalUnits>\n    <UpdatePeriodically>false</UpdatePeriodically>\n    <UpdateRequired>false</UpdateRequired>\n    <MapFileExtensions>true</MapFileExtensions>\n    <ApplicationRevision>0</ApplicationRevision>\n    <ApplicationVersion>1.2.0.%2a</ApplicationVersion>\n    <UseApplicationTrust>false</UseApplicationTrust>\n    <BootstrapperEnabled>true</BootstrapperEnabled>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject>KeyNStroke.App</StartupObject>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Costura, Version=5.0.2.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Costura.Fody.5.0.2\\lib\\netstandard1.0\\Costura.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.Win32.Primitives.4.3.0\\lib\\net46\\Microsoft.Win32.Primitives.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.AppContext.4.3.0\\lib\\net463\\System.AppContext.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.ComponentModel.Composition\" />\n    <Reference Include=\"System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Console.4.3.0\\lib\\net46\\System.Console.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Diagnostics.DiagnosticSource.4.3.0\\lib\\net46\\System.Diagnostics.DiagnosticSource.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Diagnostics.Tracing.4.3.0\\lib\\net462\\System.Diagnostics.Tracing.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Globalization.Calendars.4.3.0\\lib\\net46\\System.Globalization.Calendars.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.IO.4.3.0\\lib\\net462\\System.IO.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.IO.Compression.4.3.0\\lib\\net46\\System.IO.Compression.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\n    <Reference Include=\"System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.IO.Compression.ZipFile.4.3.0\\lib\\net46\\System.IO.Compression.ZipFile.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.IO.FileSystem.4.3.0\\lib\\net46\\System.IO.FileSystem.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.IO.FileSystem.Primitives.4.3.0\\lib\\net46\\System.IO.FileSystem.Primitives.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Linq.4.3.0\\lib\\net463\\System.Linq.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Linq.Expressions.4.3.0\\lib\\net463\\System.Linq.Expressions.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Net\" />\n    <Reference Include=\"System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Net.Http.4.3.0\\lib\\net46\\System.Net.Http.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Net.Sockets.4.3.0\\lib\\net46\\System.Net.Sockets.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Numerics\" />\n    <Reference Include=\"System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Reflection.4.3.0\\lib\\net462\\System.Reflection.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Runtime.4.3.0\\lib\\net462\\System.Runtime.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Runtime.Extensions.4.3.0\\lib\\net462\\System.Runtime.Extensions.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Runtime.InteropServices.4.3.0\\lib\\net463\\System.Runtime.InteropServices.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Runtime.InteropServices.RuntimeInformation.4.3.0\\lib\\net45\\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.Security\" />\n    <Reference Include=\"System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Security.Cryptography.Algorithms.4.3.0\\lib\\net463\\System.Security.Cryptography.Algorithms.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Security.Cryptography.Encoding.4.3.0\\lib\\net46\\System.Security.Cryptography.Encoding.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Security.Cryptography.Primitives.4.3.0\\lib\\net46\\System.Security.Cryptography.Primitives.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Security.Cryptography.X509Certificates.4.3.0\\lib\\net461\\System.Security.Cryptography.X509Certificates.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Text.RegularExpressions.4.3.0\\lib\\net463\\System.Text.RegularExpressions.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System.Web\" />\n    <Reference Include=\"System.Windows.Forms\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Xaml\">\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\n    </Reference>\n    <Reference Include=\"System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Xml.ReaderWriter.4.3.0\\lib\\net46\\System.Xml.ReaderWriter.dll</HintPath>\n      <Private>True</Private>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"WindowsBase\" />\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"WpfColorFontDialog, Version=1.0.7.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\WpfColorFontDialog.1.0.7\\lib\\net40\\WpfColorFontDialog.dll</HintPath>\n      <EmbedInteropTypes>False</EmbedInteropTypes>\n    </Reference>\n    <Reference Include=\"Xceed.Wpf.Toolkit, Version=3.8.0.0, Culture=neutral, PublicKeyToken=3e4669d2f30244f4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Extended.Wpf.Toolkit.3.8.1\\lib\\net40\\Xceed.Wpf.Toolkit.dll</HintPath>\n      <EmbedInteropTypes>False</EmbedInteropTypes>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"AnnotateLine.xaml.cs\">\n      <DependentUpon>AnnotateLine.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"CursorIndicator1.xaml.cs\">\n      <DependentUpon>CursorIndicator1.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"EnumBindingSourceExtention.cs\" />\n    <Compile Include=\"NativeMethodsEvents.cs\" />\n    <Compile Include=\"ReadShortcut.xaml.cs\">\n      <DependentUpon>ReadShortcut.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"ResizeButton.xaml.cs\">\n      <DependentUpon>ResizeButton.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"Updater\\Updater.cs\" />\n    <Compile Include=\"Updater\\Admininstration.cs\" />\n    <Compile Include=\"Updater\\Utils.cs\" />\n    <Compile Include=\"Updater\\Statemachine.cs\" />\n    <Compile Include=\"Welcome.xaml.cs\">\n      <DependentUpon>Welcome.xaml</DependentUpon>\n    </Compile>\n    <Page Include=\"AnnotateLine.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"App.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"ButtonIndicator1.cs\">\n      <SubType>Form</SubType>\n    </Compile>\n    <Compile Include=\"ButtonIndicator1.Designer.cs\">\n      <DependentUpon>ButtonIndicator1.cs</DependentUpon>\n    </Compile>\n    <Compile Include=\"ImageResources.cs\" />\n    <Compile Include=\"KeyboardHook.cs\" />\n    <Compile Include=\"KeyboardLayoutParser.cs\" />\n    <Compile Include=\"KeyboardRawEvent.cs\" />\n    <Compile Include=\"KeystrokeDisplay.xaml.cs\">\n      <DependentUpon>KeystrokeDisplay.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"KeystrokeEvent.cs\" />\n    <Compile Include=\"KeystrokeParser.cs\" />\n    <Compile Include=\"LabeledSlider.xaml.cs\">\n      <DependentUpon>LabeledSlider.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"Log.cs\" />\n    <Compile Include=\"MouseHook.cs\" />\n    <Compile Include=\"MouseRawEvent.cs\" />\n    <Compile Include=\"NativeMethodsDC.cs\" />\n    <Compile Include=\"NativeMethodsGWL.cs\" />\n    <Compile Include=\"NativeMethodsKeyboard.cs\" />\n    <Compile Include=\"NativeMethodsMouse.cs\" />\n    <Compile Include=\"NativeMethodsWindow.cs\" />\n    <Compile Include=\"Settings1.xaml.cs\">\n      <DependentUpon>Settings1.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"SettingsStore.cs\" />\n    <Compile Include=\"SpecialkeysParser.cs\" />\n    <Compile Include=\"TweenLabel.cs\">\n      <SubType>Component</SubType>\n    </Compile>\n    <Compile Include=\"UIHelper.cs\" />\n    <Compile Include=\"UrlOpener.cs\" />\n    <Page Include=\"CursorIndicator1.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"ReadShortcut.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"Themes\\Generic.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"ResizeButton.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"Welcome.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"ButtonIndicator1.resx\">\n      <DependentUpon>ButtonIndicator1.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <None Include=\"app.manifest\" />\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Page Include=\"KeystrokeDisplay.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"LabeledSlider.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"Settings1.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <BootstrapperPackage Include=\".NETFramework,Version=v4.8\">\n      <Visible>False</Visible>\n      <ProductName>Microsoft .NET Framework 4.8 %28x86 and x64%29</ProductName>\n      <Install>true</Install>\n    </BootstrapperPackage>\n    <BootstrapperPackage Include=\"Microsoft.Net.Framework.3.5.SP1\">\n      <Visible>False</Visible>\n      <ProductName>.NET Framework 3.5 SP1</ProductName>\n      <Install>false</Install>\n    </BootstrapperPackage>\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"app.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <WCFMetadata Include=\"Connected Services\\\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"Resources\\updateKey.pub.xml\" />\n    <Resource Include=\"Resources\\app.ico\" />\n    <EmbeddedResource Include=\"Resources\\mouse.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse.svg\" />\n    <EmbeddedResource Include=\"Resources\\mouse_left.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_left_double.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_middle.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_right.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_right_double.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_modifier_alt.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_modifier_ctrl.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_modifier_shift.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_modifier_win.png\" />\n    <Content Include=\"Resources\\mouse_test.svg\" />\n    <EmbeddedResource Include=\"Resources\\mouse_wheel_down.png\" />\n    <EmbeddedResource Include=\"Resources\\mouse_wheel_up.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Costura.Fody\">\n      <Version>5.0.2</Version>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include=\"Fody\">\n      <Version>6.3.0</Version>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\Fody.6.3.0\\build\\Fody.targets\" Condition=\"Exists('..\\packages\\Fody.6.3.0\\build\\Fody.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <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>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\Fody.6.3.0\\build\\Fody.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Fody.6.3.0\\build\\Fody.targets'))\" />\n    <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'))\" />\n  </Target>\n</Project>"
  },
  {
    "path": "KeyNStroke/KeyboardHook.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing System.Windows.Input;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    /// Some Parts are from \n    /// KEYBOARD.CS (c) 2006 by Emma Burrows\n\n    /// <summary>\n    /// Low-level keyboard intercept class\n    /// </summary>\n    public class KeyboardHook : IDisposable, IKeyboardRawEventProvider\n    {\n \n        #region Initializion\n\n        /// <summary>\n        /// Sets up a keyboard hook\n        /// </summary>\n        public KeyboardHook()\n        {\n            RegisterKeyboardHook();\n        }\n\n        #endregion\n\n        #region Hook Add/Remove\n\n        // Keyboard Hook Identifier for WinApi\n        private const int WH_KEYBOARD_LL = 13;\n\n        //Variables used in the call to SetWindowsHookEx\n        private NativeMethodsKeyboard.HookHandlerDelegate proc;\n        private IntPtr hookID = IntPtr.Zero;\n\n        /// <summary>\n        /// Registers the function HookCallback for the global key events winapi \n        /// </summary>\n        private void RegisterKeyboardHook()\n        {\n            if (hookID != IntPtr.Zero)\n                return;\n            proc = new NativeMethodsKeyboard.HookHandlerDelegate(HookCallback);\n            using (Process curProcess = Process.GetCurrentProcess())\n            {\n                using (ProcessModule curModule = curProcess.MainModule)\n                {\n                    hookID = NativeMethodsKeyboard.SetWindowsHookEx(WH_KEYBOARD_LL, proc,\n                        NativeMethodsKeyboard.GetModuleHandle(curModule.ModuleName), 0);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Unregisters the winapi hook for global key events\n        /// </summary>\n        private void UnregisterKeyboardHook()\n        {\n            if (hookID == IntPtr.Zero)\n                return;\n            NativeMethodsKeyboard.UnhookWindowsHookEx(hookID);\n            hookID = IntPtr.Zero;\n        }\n\n        #endregion\n\n        #region EventHandling\n\n        //Keyboard API constants: press or release  \n        private const int WM_KEYUP = 0x0101;\n        private const int WM_SYSKEYUP = 0x0105;\n        private const int WM_KEYDOWN = 0x0100;\n        private const int WM_SYSKEYDOWN = 0x0104;\n\n        private const int\n        VK_SHIFT = 0x10,\n        VK_CONTROL = 0x11,\n        VK_MENU = 0x12,\n        VK_CAPITAL = 0x14,\n        VK_LWIN = 0x5B,\n        VK_RWIN = 0x5C,\n        VK_NUMLOCK = 0x90,\n        VK_SCROLL = 0x91,\n        VK_LSHIFT = 0xA0,\n        VK_RSHIFT = 0xA1,\n        VK_LCONTROL = 0xA2,\n        VK_RCONTROL = 0xA3,\n        VK_LMENU = 0xA4,\n        VK_RMENU = 0xA5;\n\n        /// <summary>\n        /// Processes the key event captured by the hook.\n        /// </summary>\n        private IntPtr HookCallback(int nCode, \n                                    IntPtr wParam, \n                                    ref NativeMethodsKeyboard.KBDLLHOOKSTRUCT lParam)\n        {\n            if (nCode >= 0)\n            {\n                KeyboardRawEventArgs e = new KeyboardRawEventArgs(lParam);\n                e.keyState = new byte[256];\n                //NativeMethodsKeyboard.GetKeyboardState(e.keyState); probability of dataraces\n\n                if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)\n                {\n                    e.Method = KeyUpDown.Down;\n                }\n                else if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)\n                {\n                    e.Method = KeyUpDown.Up;\n                }\n\n                if (e.Method != KeyUpDown.Undefined)\n                {\n                    CheckModifiers(e);\n                    FixKeyStateArray(e);\n                    Log.e(\"KE\", $\"EVENT scanCode={lParam.scanCode} vkCode={e.vkCode} method={e.Method} uppercase={e.Uppercase} \");\n                    OnKeyEvent(e);\n                }\n\n                /*System.Diagnostics.Debug.WriteLine(\n                    String.Format(\"Key: sc {0} vk {1} ext {2} fl {3}, {4}\", lParam.scanCode,\n                        lParam.vkCode, lParam.dwExtraInfo, lParam.flags, e.Method));\n                */\n                if (e.preventDefault)\n                {\n                    return IntPtr.Add(IntPtr.Zero, 1);\n                } else\n                {\n                    return NativeMethodsKeyboard.CallNextHookEx(hookID, nCode, wParam, ref lParam);\n                }\n            }\n            else\n            {\n                //Pass key to next application\n                return NativeMethodsKeyboard.CallNextHookEx(hookID, nCode, wParam, ref lParam);\n            }\n        }\n\n        /// <summary>\n        /// Checks whether Alt, Shift, Control or CapsLock\n        /// is enabled at the same time as another key.\n        /// Modify the relevant sections and return type \n        /// depending on what you want to do with modifier keys.\n        /// </summary>\n        private void CheckModifiers(KeyboardRawEventArgs e)\n        {\n            e.Shift = CheckModifierDown(VK_SHIFT);\n            e.Ctrl = CheckModifierDown(VK_CONTROL);\n            e.Alt = CheckModifierDown(VK_MENU);\n            e.Caps = CheckModifierToggled(VK_CAPITAL);\n            e.LWin = CheckModifierDown(VK_LWIN);\n            e.RWin = CheckModifierDown(VK_RWIN);\n            e.Numlock = CheckModifierToggled(VK_NUMLOCK);\n            e.Scrollock = CheckModifierToggled(VK_SCROLL);\n            e.LShift = CheckModifierDown(VK_LSHIFT);\n            e.RShift = CheckModifierDown(VK_RSHIFT);\n            e.LCtrl = CheckModifierDown(VK_LCONTROL);\n            e.RCtrl = CheckModifierDown(VK_RCONTROL);\n            e.LAlt = CheckModifierDown(VK_LMENU);\n            e.RAlt = CheckModifierDown(VK_RMENU);\n        }\n\n        /// <summary>\n        /// Uses the winapi to check if the VK key modifiercode is pressed\n        /// </summary>\n        /// <param name=\"modifiercode\"></param>\n        /// <returns></returns>\n        private bool CheckModifierDown(int modifiercode)\n        {\n            // SHORT NativeMethodsKeyboard.GetKeyState(int)\n            // The return value specifies the status of the specified virtual key, as follows:\n\n            // - If the high-order bit is 1, the key is down; otherwise, it is up.\n            // - If the low-order bit is 1, the key is toggled. A key, such as \n            //   the CAPS LOCK key, is toggled if it is turned on. The key is \n            //   off and untoggled if the low-order bit is 0. A toggle key's \n            //   indicator light (if any) on the keyboard will be on when the \n            //   key is toggled, and off when the key is untoggled.\n            return ((NativeMethodsKeyboard.GetKeyState(modifiercode) & 0x8000) != 0);\n        }\n\n        /// <summary>\n        /// Uses the winapi to check weather the caps/numlock/scrolllock is activated\n        /// </summary>\n        /// <param name=\"modifiercode\"></param>\n        /// <returns></returns>\n        private bool CheckModifierToggled(int modifiercode)\n        {\n            return (NativeMethodsKeyboard.GetKeyState(modifiercode) & 0x0001) != 0;\n        }\n\n\n        private void FixKeyStateArray(KeyboardRawEventArgs e)\n        {\n            Log.e(\"KP\", \"FixKeyStateArray()\");\n            if (e.Uppercase)\n            {\n                e.keyState[VK_SHIFT] = 129;\n            } else\n            {\n                e.keyState[VK_SHIFT] = 0;\n            }\n            //return;\n#pragma warning disable CS0162 // Unerreichbarer Code wurde entdeckt.\n            e.keyState[VK_CONTROL] = (byte)(e.Ctrl ? 129 : 0);\n#pragma warning restore CS0162 // Unerreichbarer Code wurde entdeckt.\n            e.keyState[VK_MENU] = (byte)(e.Alt ? 129 : 0);\n            e.keyState[VK_CAPITAL] = (byte)(e.Caps ? 129 : 0);\n            e.keyState[VK_LWIN] = (byte)(e.LWin ? 129 : 0);\n            e.keyState[VK_RWIN] = (byte)(e.RWin ? 129 : 0);\n            e.keyState[VK_NUMLOCK] = (byte)(e.Numlock ? 129 : 0);\n            e.keyState[VK_SCROLL] = (byte)(e.Scrollock ? 129 : 0);\n            e.keyState[VK_LSHIFT] = (byte)(e.LShift ? 129 : 0);\n            e.keyState[VK_RSHIFT] = (byte)(e.RShift ? 129 : 0);\n            e.keyState[VK_LCONTROL] = (byte)(e.LCtrl ? 129 : 0);\n            e.keyState[VK_RCONTROL] = (byte)(e.RCtrl ? 129 : 0);\n            e.keyState[VK_LMENU] = (byte)(e.LAlt ? 129 : 0);\n            e.keyState[VK_RMENU] = (byte)(e.RAlt ? 129 : 0);\n        }\n\n        #endregion\n\n        #region Event Forwarding\n\n        /// <summary>\n        /// Fires if key is pressed or released.\n        /// </summary>\n        public event KeyboardRawEventHandler KeyEvent;\n        \n        /// <summary>\n        /// Raises the KeyEvent event.\n        /// </summary>\n        /// <param name=\"e\">An instance of KeyboardRawEvent</param>\n        public void OnKeyEvent(KeyboardRawEventArgs e)\n        {\n            if (KeyEvent != null)\n                KeyEvent(e);\n        }\n\n        #endregion\n\n        #region Finalizing and Disposing\n\n        ~KeyboardHook()\n        {\n            Log.e(\"HOOK\", \"~KeyboardHook\");\n            UnregisterKeyboardHook();\n        }\n\n        /// <summary>\n        /// Releases the keyboard hook.\n        /// </summary>\n        public void Dispose()\n        {\n            UnregisterKeyboardHook();\n        }\n        #endregion\n\n\n    }\n\n}\n"
  },
  {
    "path": "KeyNStroke/KeyboardLayoutParser.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    public class KeyboardLayoutParser\n    {\n        public static string Parse(KeyboardRawEventArgs e)\n        {\n            StringBuilder sb = new StringBuilder(128);\n            int lParam = 0;\n            // Bits in lParam\n            // 16-23\tScan code.\n            // 24\tExtended-key flag. Distinguishes some keys on an enhanced keyboard.\n            // 25\t\"Do not care\" bit. The application calling this function sets this \n            //      bit to indicate that the function should not distinguish between left \n            //      and right CTRL and SHIFT keys, for example.\n\n            lParam = e.Kbdllhookstruct.scanCode << 16;\n\n            int result = NativeMethodsKeyboard.GetKeyNameText(lParam, sb, 128);\n            return sb.ToString();\n        }\n\n        public static string ParseViaMapKeycode(KeyboardRawEventArgs e)\n        {\n            uint r = NativeMethodsKeyboard.MapVirtualKey((uint)e.vkCode, \n                                                    NativeMethodsKeyboard.MAPVK_VK_TO_CHAR);\n            return ((char)r).ToString();\n        }\n\n\n        public static string ParseViaToAscii(KeyboardRawEventArgs e)\n        {\n            byte[] inBuffer = new byte[2];\n            int buffertype = NativeMethodsKeyboard.ToAscii(e.vkCode,\n                        e.Kbdllhookstruct.scanCode,\n                        e.keyState,\n                        inBuffer,\n                        e.Alt ? 1 : 0);\n\n            if (buffertype < 0) // deadkey\n            {\n\n            }\n            else if (buffertype == 1) // one char in inBuffer[0]\n            {\n                char key = (char)inBuffer[0];\n                return key.ToString();\n            }\n            else if (buffertype == 2) // two chars in inBuffer\n            {\n                char key = (char)inBuffer[0];\n                char key2 = (char)inBuffer[1];\n                return key.ToString() + key2.ToString();\n            }\n            else if (buffertype == 0)\n            {\n                // no translation\n            }\n            return \"\";\n        }\n\n\n        public static string ParseViaToUnicode(KeyboardRawEventArgs e)\n        {\n            StringBuilder inBuffer = new StringBuilder(128);\n            int buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode,\n                        e.Kbdllhookstruct.scanCode,\n                        e.keyState,\n                        inBuffer,\n                        128,\n                        0); /* 4 == \"don't change keyboard state\" (Windows 10 version 1607 and higher) */\n            Log.e(\"KP\",\n                    String.Format(\"   ParseViaToUnicode(): First call to ToUnicode: returned={0} translated='{1}' alt={2} ctrl={3} vk={4}\", buffertype,\n                        inBuffer.ToString(), e.Alt, e.Ctrl, e.vkCode));\n            string keystate = \"\";\n            for (int i = 0; i < e.keyState.Length; i++ )\n            {\n                if(e.keyState[i] != 0)\n                {\n                    keystate += \" \" + ((WindowsVirtualKey) i).ToString() + \":\" + e.keyState[i];\n\n                }\n            }\n\n            Log.e(\"KP\", \"   ParseViaToUnicode(): Key state: \" + keystate);\n\n            // call ToUnicode again, otherwise it will destoy the dead key for the rest of the system\n            int buffertype2 = NativeMethodsKeyboard.ToUnicode(e.vkCode,\n                e.Kbdllhookstruct.scanCode,\n                e.keyState,\n                inBuffer,\n                128,\n                0); /* 4 == \"don't change keyboard state\" (Windows 10 version 1607 and higher) */\n\n            Log.e(\"KP\",\n                    String.Format(\"   ParseViaToUnicode(): Secnd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}\", buffertype2,\n                        inBuffer.ToString(), e.Alt, e.vkCode));\n\n            if (buffertype < 0) // deadkey\n            {\n                // return DEADKEY, so the next key can try to assemble the deadkey\n                //return \"DEADKEY\";\n                return buffertype2 >= 1 ? inBuffer.ToString(0, 1) : \"\";\n            }\n            else if(buffertype2 < 0) // type two dead keys in a row\n            {\n                Log.e(\"KP\", \"   ParseViaToUnicode(): TwoDeadKeysInARow \" + buffertype2.ToString() + \" & deadkey\");\n                return buffertype >= 1 ? inBuffer.ToString(0, 1) : \"\";\n            }\n            else if (buffertype2 >= 1) // buffertype chars in inBuffer[0..buffertype]\n            {\n                string out_ = inBuffer.ToString(0, buffertype2);\n                if (out_ == \"\\b\") // Backspace is no text\n                {\n                    return \"\";\n                }\n                return out_;\n            }\n            else if (buffertype2 == 0)\n            {\n                // no translation\n            }\n            return \"\";\n        }\n\n        public static string ProcessDeadkeyWithNextKey(KeyboardRawEventArgs dead, KeyboardRawEventArgs e)\n        {\n            Log.e(\"KP\", \"   ProcessDeadkeyWithNextKey()\");\n            StringBuilder inBuffer = new StringBuilder(128);\n            int buffertype = NativeMethodsKeyboard.ToUnicode(dead.vkCode,\n                        dead.Kbdllhookstruct.scanCode,\n                        dead.keyState,\n                        inBuffer,\n                        128,\n                        4); /* 4 == \"don't change keyboard state\" (Windows 10 version 1607 and higher) */\n\n            Log.e(\"KP\",\n                    String.Format(\"   ProcessDeadkeyWithNextKey(): First call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}\", buffertype,\n                        inBuffer.ToString(), e.Alt, e.vkCode));\n            buffertype = NativeMethodsKeyboard.ToUnicode(e.vkCode,\n                e.Kbdllhookstruct.scanCode,\n                e.keyState,\n                inBuffer,\n                128,\n                4); /* 4 == \"don't change keyboard state\" (Windows 10 version 1607 and higher) */\n            Log.e(\"KP\",\n                    String.Format(\"   ProcessDeadkeyWithNextKey(): Sednd call to ToUnicode: returned={0} translated='{1}' alt={2} vk={3}\", buffertype,\n                        inBuffer.ToString(), e.Alt, e.vkCode));\n\n            if (buffertype >= 1) // buffertype chars in inBuffer[0..buffertype]\n            {\n                return inBuffer.ToString(0, buffertype);\n            }\n            else if (buffertype == 0)\n            {\n                // no translation\n            }\n            return \"\";\n        }\n\n        /*\n        int convertVirtualKeyToWChar(int virtualKey, PWCHAR outputChar, PWCHAR deadChar)\n        {\n            int i = 0;\n            short state = 0;\n            int capsLock;\n            int shift = -1;\n            int mod = 0;\n            int charCount = 0;\n            WCHAR baseChar;\n            WCHAR diacritic;\n            *outputChar = 0;\n            capsLock = (GetKeyState(VK_CAPITAL) & 0x1);\n            do\n            {\n                state = GetAsyncKeyState(pgCharModifiers->pVkToBit[i].Vk);\n                if (pgCharModifiers->pVkToBit[i].Vk == VK_SHIFT)\n                    shift = i + 1; // Get modification number for Shift key\n                if (state & ~SHRT_MAX)\n                {\n                    if (mod == 0)\n                        mod = i + 1;\n                    else\n                        mod = 0; // Two modifiers at the same time!\n                }\n                i++;\n            }\n            while (pgCharModifiers->pVkToBit[i].Vk != 0);\n\n            SEARCH_VK_IN_CONVERSION_TABLE(1)\n            SEARCH_VK_IN_CONVERSION_TABLE(2)\n            SEARCH_VK_IN_CONVERSION_TABLE(3)\n            SEARCH_VK_IN_CONVERSION_TABLE(4)\n            SEARCH_VK_IN_CONVERSION_TABLE(5)\n            SEARCH_VK_IN_CONVERSION_TABLE(6)\n            SEARCH_VK_IN_CONVERSION_TABLE(7)\n            SEARCH_VK_IN_CONVERSION_TABLE(8)\n            SEARCH_VK_IN_CONVERSION_TABLE(9)\n            SEARCH_VK_IN_CONVERSION_TABLE(10)\n\n            if (*deadChar != 0) // I see dead characters...\n            {\n                i = 0;\n                do\n                {\n                    baseChar = (WCHAR) pgDeadKey[i].dwBoth;\n                    diacritic = (WCHAR) (pgDeadKey[i].dwBoth >> 16);\n                    if ((baseChar == *outputChar) && (diacritic == *deadChar))\n                    {\n                        *deadChar = 0;\n                        *outputChar = (WCHAR) pgDeadKey[i].wchComposed;\n                    }\n                    i++;\n                }\n                while (pgDeadKey[i].dwBoth != 0);\n            }\n            return charCount;\n            }*/\n      }\n}\n"
  },
  {
    "path": "KeyNStroke/KeyboardRawEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    public enum KeyUpDown\n    {\n        Undefined,\n        Up,\n        Down\n    }\n\n    public class KeyboardRawEventArgs\n    {\n        public bool Shift;\n        public bool LShift;\n        public bool RShift;\n        public bool Ctrl;\n        public bool LCtrl;\n        public bool RCtrl;\n        public bool Caps;\n        public bool LWin;\n        public bool RWin;\n        public bool Alt;\n        public bool LAlt;\n        public bool RAlt; // Alt Gr\n        public bool Numlock;\n        public bool Scrollock;\n\n        public int vkCode { get { return Kbdllhookstruct.vkCode; } }\n        private Key key;\n        public Key Key { get {  return key; } }\n        public KeyUpDown Method = KeyUpDown.Undefined;\n\n        public bool Uppercase { get { return (Shift && !Caps) || (Caps && !Shift);  } }\n        public bool OnlyShiftOrCaps { get { return !Ctrl && !LWin && !RWin && !Alt;  } }\n        public bool NoModifiers { get { return !Ctrl && !LWin && !RWin && !Alt && !Shift; } }\n        public bool Win { get { return LWin || RWin; } }\n\n        public NativeMethodsKeyboard.KBDLLHOOKSTRUCT Kbdllhookstruct;\n        public byte[] keyState; // 256 bytes\n\n        public bool preventDefault = false;\n\n        public KeyboardRawEventArgs(NativeMethodsKeyboard.KBDLLHOOKSTRUCT Kbdllhookstruct)\n        {\n            this.Kbdllhookstruct = Kbdllhookstruct;\n            this.key = KeyInterop.KeyFromVirtualKey(this.vkCode); /* cache */\n        }\n    }\n\n    public delegate void KeyboardRawEventHandler(KeyboardRawEventArgs e);\n\n    public interface IKeyboardRawEventProvider : IDisposable\n    {\n        event KeyboardRawEventHandler KeyEvent;\n    }\n\n    public enum WindowsVirtualKey\n    {\n        [Description(\"Left mouse button\")]\n        VK_LBUTTON = 0x01,\n\n        [Description(\"Right mouse button\")]\n        VK_RBUTTON = 0x02,\n\n        [Description(\"Control-break processing\")]\n        VK_CANCEL = 0x03,\n\n        [Description(\"Middle mouse button (three-button mouse)\")]\n        VK_MBUTTON = 0x04,\n\n        [Description(\"X1 mouse button\")]\n        VK_XBUTTON1 = 0x05,\n\n        [Description(\"X2 mouse button\")]\n        VK_XBUTTON2 = 0x06,\n\n        [Description(\"BACKSPACE key\")]\n        VK_BACK = 0x08,\n\n        [Description(\"TAB key\")]\n        VK_TAB = 0x09,\n\n        [Description(\"CLEAR key\")]\n        VK_CLEAR = 0x0C,\n\n        [Description(\"ENTER key\")]\n        VK_RETURN = 0x0D,\n\n        [Description(\"SHIFT key\")]\n        VK_SHIFT = 0x10,\n\n        [Description(\"CTRL key\")]\n        VK_CONTROL = 0x11,\n\n        [Description(\"ALT key\")]\n        VK_MENU = 0x12,\n\n        [Description(\"PAUSE key\")]\n        VK_PAUSE = 0x13,\n\n        [Description(\"CAPS LOCK key\")]\n        VK_CAPITAL = 0x14,\n\n        [Description(\"IME Kana mode\")]\n        VK_KANA = 0x15,\n\n        [Description(\"IME Hanguel mode (maintained for compatibility; use VK_HANGUL)\")]\n        VK_HANGUEL = 0x15,\n\n        [Description(\"IME Hangul mode\")]\n        VK_HANGUL = 0x15,\n\n        [Description(\"IME Junja mode\")]\n        VK_JUNJA = 0x17,\n\n        [Description(\"IME final mode\")]\n        VK_FINAL = 0x18,\n\n        [Description(\"IME Hanja mode\")]\n        VK_HANJA = 0x19,\n\n        [Description(\"IME Kanji mode\")]\n        VK_KANJI = 0x19,\n\n        [Description(\"ESC key\")]\n        VK_ESCAPE = 0x1B,\n\n        [Description(\"IME convert\")]\n        VK_CONVERT = 0x1C,\n\n        [Description(\"IME nonconvert\")]\n        VK_NONCONVERT = 0x1D,\n\n        [Description(\"IME accept\")]\n        VK_ACCEPT = 0x1E,\n\n        [Description(\"IME mode change request\")]\n        VK_MODECHANGE = 0x1F,\n\n        [Description(\"SPACEBAR\")]\n        VK_SPACE = 0x20,\n\n        [Description(\"PAGE UP key\")]\n        VK_PRIOR = 0x21,\n\n        [Description(\"PAGE DOWN key\")]\n        VK_NEXT = 0x22,\n\n        [Description(\"END key\")]\n        VK_END = 0x23,\n\n        [Description(\"HOME key\")]\n        VK_HOME = 0x24,\n\n        [Description(\"LEFT ARROW key\")]\n        VK_LEFT = 0x25,\n\n        [Description(\"UP ARROW key\")]\n        VK_UP = 0x26,\n\n        [Description(\"RIGHT ARROW key\")]\n        VK_RIGHT = 0x27,\n\n        [Description(\"DOWN ARROW key\")]\n        VK_DOWN = 0x28,\n\n        [Description(\"SELECT key\")]\n        VK_SELECT = 0x29,\n\n        [Description(\"PRINT key\")]\n        VK_PRINT = 0x2A,\n\n        [Description(\"EXECUTE key\")]\n        VK_EXECUTE = 0x2B,\n\n        [Description(\"PRINT SCREEN key\")]\n        VK_SNAPSHOT = 0x2C,\n\n        [Description(\"INS key\")]\n        VK_INSERT = 0x2D,\n\n        [Description(\"DEL key\")]\n        VK_DELETE = 0x2E,\n\n        [Description(\"HELP key\")]\n        VK_HELP = 0x2F,\n\n        [Description(\"0 key\")]\n        K_0 = 0x30,\n\n        [Description(\"1 key\")]\n        K_1 = 0x31,\n\n        [Description(\"2 key\")]\n        K_2 = 0x32,\n\n        [Description(\"3 key\")]\n        K_3 = 0x33,\n\n        [Description(\"4 key\")]\n        K_4 = 0x34,\n\n        [Description(\"5 key\")]\n        K_5 = 0x35,\n\n        [Description(\"6 key\")]\n        K_6 = 0x36,\n\n        [Description(\"7 key\")]\n        K_7 = 0x37,\n\n        [Description(\"8 key\")]\n        K_8 = 0x38,\n\n        [Description(\"9 key\")]\n        K_9 = 0x39,\n\n        [Description(\"A key\")]\n        K_A = 0x41,\n\n        [Description(\"B key\")]\n        K_B = 0x42,\n\n        [Description(\"C key\")]\n        K_C = 0x43,\n\n        [Description(\"D key\")]\n        K_D = 0x44,\n\n        [Description(\"E key\")]\n        K_E = 0x45,\n\n        [Description(\"F key\")]\n        K_F = 0x46,\n\n        [Description(\"G key\")]\n        K_G = 0x47,\n\n        [Description(\"H key\")]\n        K_H = 0x48,\n\n        [Description(\"I key\")]\n        K_I = 0x49,\n\n        [Description(\"J key\")]\n        K_J = 0x4A,\n\n        [Description(\"K key\")]\n        K_K = 0x4B,\n\n        [Description(\"L key\")]\n        K_L = 0x4C,\n\n        [Description(\"M key\")]\n        K_M = 0x4D,\n\n        [Description(\"N key\")]\n        K_N = 0x4E,\n\n        [Description(\"O key\")]\n        K_O = 0x4F,\n\n        [Description(\"P key\")]\n        K_P = 0x50,\n\n        [Description(\"Q key\")]\n        K_Q = 0x51,\n\n        [Description(\"R key\")]\n        K_R = 0x52,\n\n        [Description(\"S key\")]\n        K_S = 0x53,\n\n        [Description(\"T key\")]\n        K_T = 0x54,\n\n        [Description(\"U key\")]\n        K_U = 0x55,\n\n        [Description(\"V key\")]\n        K_V = 0x56,\n\n        [Description(\"W key\")]\n        K_W = 0x57,\n\n        [Description(\"X key\")]\n        K_X = 0x58,\n\n        [Description(\"Y key\")]\n        K_Y = 0x59,\n\n        [Description(\"Z key\")]\n        K_Z = 0x5A,\n\n        [Description(\"Left Windows key (Natural keyboard)\")]\n        VK_LWIN = 0x5B,\n\n        [Description(\"Right Windows key (Natural keyboard)\")]\n        VK_RWIN = 0x5C,\n\n        [Description(\"Applications key (Natural keyboard)\")]\n        VK_APPS = 0x5D,\n\n        [Description(\"Computer Sleep key\")]\n        VK_SLEEP = 0x5F,\n\n        [Description(\"Numeric keypad 0 key\")]\n        VK_NUMPAD0 = 0x60,\n\n        [Description(\"Numeric keypad 1 key\")]\n        VK_NUMPAD1 = 0x61,\n\n        [Description(\"Numeric keypad 2 key\")]\n        VK_NUMPAD2 = 0x62,\n\n        [Description(\"Numeric keypad 3 key\")]\n        VK_NUMPAD3 = 0x63,\n\n        [Description(\"Numeric keypad 4 key\")]\n        VK_NUMPAD4 = 0x64,\n\n        [Description(\"Numeric keypad 5 key\")]\n        VK_NUMPAD5 = 0x65,\n\n        [Description(\"Numeric keypad 6 key\")]\n        VK_NUMPAD6 = 0x66,\n\n        [Description(\"Numeric keypad 7 key\")]\n        VK_NUMPAD7 = 0x67,\n\n        [Description(\"Numeric keypad 8 key\")]\n        VK_NUMPAD8 = 0x68,\n\n        [Description(\"Numeric keypad 9 key\")]\n        VK_NUMPAD9 = 0x69,\n\n        [Description(\"Multiply key\")]\n        VK_MULTIPLY = 0x6A,\n\n        [Description(\"Add key\")]\n        VK_ADD = 0x6B,\n\n        [Description(\"Separator key\")]\n        VK_SEPARATOR = 0x6C,\n\n        [Description(\"Subtract key\")]\n        VK_SUBTRACT = 0x6D,\n\n        [Description(\"Decimal key\")]\n        VK_DECIMAL = 0x6E,\n\n        [Description(\"Divide key\")]\n        VK_DIVIDE = 0x6F,\n\n        [Description(\"F1 key\")]\n        VK_F1 = 0x70,\n\n        [Description(\"F2 key\")]\n        VK_F2 = 0x71,\n\n        [Description(\"F3 key\")]\n        VK_F3 = 0x72,\n\n        [Description(\"F4 key\")]\n        VK_F4 = 0x73,\n\n        [Description(\"F5 key\")]\n        VK_F5 = 0x74,\n\n        [Description(\"F6 key\")]\n        VK_F6 = 0x75,\n\n        [Description(\"F7 key\")]\n        VK_F7 = 0x76,\n\n        [Description(\"F8 key\")]\n        VK_F8 = 0x77,\n\n        [Description(\"F9 key\")]\n        VK_F9 = 0x78,\n\n        [Description(\"F10 key\")]\n        VK_F10 = 0x79,\n\n        [Description(\"F11 key\")]\n        VK_F11 = 0x7A,\n\n        [Description(\"F12 key\")]\n        VK_F12 = 0x7B,\n\n        [Description(\"F13 key\")]\n        VK_F13 = 0x7C,\n\n        [Description(\"F14 key\")]\n        VK_F14 = 0x7D,\n\n        [Description(\"F15 key\")]\n        VK_F15 = 0x7E,\n\n        [Description(\"F16 key\")]\n        VK_F16 = 0x7F,\n\n        [Description(\"F17 key\")]\n        VK_F17 = 0x80,\n\n        [Description(\"F18 key\")]\n        VK_F18 = 0x81,\n\n        [Description(\"F19 key\")]\n        VK_F19 = 0x82,\n\n        [Description(\"F20 key\")]\n        VK_F20 = 0x83,\n\n        [Description(\"F21 key\")]\n        VK_F21 = 0x84,\n\n        [Description(\"F22 key\")]\n        VK_F22 = 0x85,\n\n        [Description(\"F23 key\")]\n        VK_F23 = 0x86,\n\n        [Description(\"F24 key\")]\n        VK_F24 = 0x87,\n\n        [Description(\"NUM LOCK key\")]\n        VK_NUMLOCK = 0x90,\n\n        [Description(\"SCROLL LOCK key\")]\n        VK_SCROLL = 0x91,\n\n        [Description(\"Left SHIFT key\")]\n        VK_LSHIFT = 0xA0,\n\n        [Description(\"Right SHIFT key\")]\n        VK_RSHIFT = 0xA1,\n\n        [Description(\"Left CONTROL key\")]\n        VK_LCONTROL = 0xA2,\n\n        [Description(\"Right CONTROL key\")]\n        VK_RCONTROL = 0xA3,\n\n        [Description(\"Left MENU key\")]\n        VK_LMENU = 0xA4,\n\n        [Description(\"Right MENU key\")]\n        VK_RMENU = 0xA5,\n\n        [Description(\"Browser Back key\")]\n        VK_BROWSER_BACK = 0xA6,\n\n        [Description(\"Browser Forward key\")]\n        VK_BROWSER_FORWARD = 0xA7,\n\n        [Description(\"Browser Refresh key\")]\n        VK_BROWSER_REFRESH = 0xA8,\n\n        [Description(\"Browser Stop key\")]\n        VK_BROWSER_STOP = 0xA9,\n\n        [Description(\"Browser Search key\")]\n        VK_BROWSER_SEARCH = 0xAA,\n\n        [Description(\"Browser Favorites key\")]\n        VK_BROWSER_FAVORITES = 0xAB,\n\n        [Description(\"Browser Start and Home key\")]\n        VK_BROWSER_HOME = 0xAC,\n\n        [Description(\"Volume Mute key\")]\n        VK_VOLUME_MUTE = 0xAD,\n\n        [Description(\"Volume Down key\")]\n        VK_VOLUME_DOWN = 0xAE,\n\n        [Description(\"Volume Up key\")]\n        VK_VOLUME_UP = 0xAF,\n\n        [Description(\"Next Track key\")]\n        VK_MEDIA_NEXT_TRACK = 0xB0,\n\n        [Description(\"Previous Track key\")]\n        VK_MEDIA_PREV_TRACK = 0xB1,\n\n        [Description(\"Stop Media key\")]\n        VK_MEDIA_STOP = 0xB2,\n\n        [Description(\"Play/Pause Media key\")]\n        VK_MEDIA_PLAY_PAUSE = 0xB3,\n\n        [Description(\"Start Mail key\")]\n        VK_LAUNCH_MAIL = 0xB4,\n\n        [Description(\"Select Media key\")]\n        VK_LAUNCH_MEDIA_SELECT = 0xB5,\n\n        [Description(\"Start Application 1 key\")]\n        VK_LAUNCH_APP1 = 0xB6,\n\n        [Description(\"Start Application 2 key\")]\n        VK_LAUNCH_APP2 = 0xB7,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key\")]\n        VK_OEM_1 = 0xBA,\n\n        [Description(\"For any country/region, the '+' key\")]\n        VK_OEM_PLUS = 0xBB,\n\n        [Description(\"For any country/region, the ',' key\")]\n        VK_OEM_COMMA = 0xBC,\n\n        [Description(\"For any country/region, the '-' key\")]\n        VK_OEM_MINUS = 0xBD,\n\n        [Description(\"For any country/region, the '.' key\")]\n        VK_OEM_PERIOD = 0xBE,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key\")]\n        VK_OEM_2 = 0xBF,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key\")]\n        VK_OEM_3 = 0xC0,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key\")]\n        VK_OEM_4 = 0xDB,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\\\\|' key\")]\n        VK_OEM_5 = 0xDC,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key\")]\n        VK_OEM_6 = 0xDD,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key\")]\n        VK_OEM_7 = 0xDE,\n\n        [Description(\"Used for miscellaneous characters; it can vary by keyboard.\")]\n        VK_OEM_8 = 0xDF,\n\n\n        [Description(\"Either the angle bracket key or the backslash key on the RT 102-key keyboard\")]\n        VK_OEM_102 = 0xE2,\n\n        [Description(\"IME PROCESS key\")]\n        VK_PROCESSKEY = 0xE5,\n\n\n        [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\")]\n        VK_PACKET = 0xE7,\n\n        [Description(\"Attn key\")]\n        VK_ATTN = 0xF6,\n\n        [Description(\"CrSel key\")]\n        VK_CRSEL = 0xF7,\n\n        [Description(\"ExSel key\")]\n        VK_EXSEL = 0xF8,\n\n        [Description(\"Erase EOF key\")]\n        VK_EREOF = 0xF9,\n\n        [Description(\"Play key\")]\n        VK_PLAY = 0xFA,\n\n        [Description(\"Zoom key\")]\n        VK_ZOOM = 0xFB,\n\n        [Description(\"PA1 key\")]\n        VK_PA1 = 0xFD,\n\n        [Description(\"Clear key\")]\n        VK_OEM_CLEAR = 0xFE,\n\n    } \n}\n"
  },
  {
    "path": "KeyNStroke/KeystrokeDisplay.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.KeystrokeDisplay\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"KeystrokeDisplay\"\n        Height=\"450\"\n        Width=\"800\"\n        Closing=\"Window_Closing\"\n        ShowInTaskbar=\"False\"\n        Topmost=\"True\"\n        AllowsTransparency=\"True\"\n        WindowStyle=\"None\"\n        ResizeMode=\"NoResize\"\n        MouseUp=\"Window_MouseUp\"\n        Loaded=\"Window_Loaded\"\n        Background=\"Transparent\">\n    <Grid x:Name=\"backgroundGrid\"\n          Background=\"Black\"\n          MouseDown=\"BackgroundGrid_MouseDown\">\n\n        <local:ResizeButton x:Name=\"buttonResizeWindow\"\n                            HorizontalAlignment=\"Right\"\n                            Margin=\"0,0,-1,-1\"\n                            VerticalAlignment=\"Bottom\"\n                            Width=\"38\"\n                            Height=\"38\"\n                            Panel.ZIndex=\"10\" />\n\n        <Button x:Name=\"buttonClose\"\n                Content=\"×\"\n                HorizontalAlignment=\"Right\"\n                Margin=\"0,-5,20,0\"\n                VerticalAlignment=\"Top\"\n                Width=\"45\"\n                FontSize=\"24\"\n                Height=\"38\"\n                Background=\"#FFD44A4A\"\n                Foreground=\"White\"\n                Panel.ZIndex=\"8\"\n                Click=\"ButtonClose_Click\" />\n        <Button x:Name=\"buttonSettings\"\n                Content=\"⚙\"\n                HorizontalAlignment=\"Right\"\n                Margin=\"0,-2,80,0\"\n                VerticalAlignment=\"Top\"\n                Width=\"38\"\n                FontSize=\"16\"\n                Height=\"38\"\n                Panel.ZIndex=\"8\"\n                Click=\"ButtonSettings_Click\" />\n        <Button x:Name=\"buttonLeaveSettingsMode\"\n                Content=\"Finish Move+Resize\"\n                HorizontalAlignment=\"Right\"\n                Margin=\"0,-2,140,0\"\n                VerticalAlignment=\"Top\"\n                Width=\"170\"\n                FontSize=\"16\"\n                Height=\"38\"\n                Panel.ZIndex=\"8\"\n                Click=\"ButtonLeaveSettingsMode_Click\" Foreground=\"White\" Background=\"#FF5CC358\" />\n        <Button x:Name=\"buttonLeavePasswordMode\"\n                HorizontalAlignment=\"Center\"\n                Margin=\"0,0,0,0\"\n                VerticalAlignment=\"Center\"\n                Width=\"338\"\n                FontSize=\"16\"\n                Height=\"119\"\n                Panel.ZIndex=\"8\"\n                Click=\"ButtonLeavePasswordMode_Click\" Foreground=\"White\" Background=\"#FFC3AA58\">\n            <TextBlock HorizontalAlignment=\"Left\" TextAlignment=\"Center\">\n                Password Protection Mode Enabled\n                <LineBreak/>\n                <Span FontSize=\"12\">To continue normally, click here or press<LineBreak/>\n                <Run x:Name=\"PasswordProtectionModeShortcut\" Text=\"\"/>\n                </Span>\n            </TextBlock>\n        </Button>\n        <Grid x:Name=\"innerPanel\"\n              HorizontalAlignment=\"Left\"\n              Height=\"185\"\n              Margin=\"105,96,0,0\"\n              VerticalAlignment=\"Top\"\n              Width=\"595\"\n              Background=\"#FFA7A7A7\"\n              MouseDown=\"InnerPanel_MouseDown\"\n              MouseMove=\"InnerPanel_MouseMove\"\n              MouseUp=\"InnerPanel_MouseUp\">\n            <StackPanel x:Name=\"labelStack\"\n                        Margin=\"0\"\n                        VerticalAlignment=\"Bottom\">\n            </StackPanel>\n            <local:ResizeButton x:Name=\"buttonResizeInnerPanel\"\n                                HorizontalAlignment=\"Right\"\n                                Margin=\"0,0,-1,-1\"\n                                VerticalAlignment=\"Bottom\"\n                                Width=\"38\"\n                                Height=\"38\"\n                                ResizeTarget=\"Parent\"\n                                Panel.ZIndex=\"5\" />\n        </Grid>\n\n    </Grid>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/KeystrokeDisplay.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Web;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\nusing System.Windows.Threading;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für KeystrokeDisplay.xaml\n    /// </summary>\n    public partial class KeystrokeDisplay : Window\n    {\n        readonly SettingsStore settings;\n        readonly IKeystrokeEventProvider k;\n        IntPtr windowHandle;\n        Brush OrigInnerPanelBackgroundColor;\n\n        public KeystrokeDisplay(IKeystrokeEventProvider k, SettingsStore s)\n        {\n            InitializeComponent();\n            InitializeAnimations();\n\n            this.k = k;\n\n            this.settings = s;\n            this.settings.EnableSettingsMode = false;\n            this.settings.EnablePasswordMode = false;\n            this.settings.PropertyChanged += SettingChanged;\n            this.settings.CallPropertyChangedForAllProperties();\n\n            this.buttonResizeWindow.Settings = s;\n            this.buttonResizeInnerPanel.Settings = s;\n\n            //addWelcomeInfo();\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            // Window handle is available\n            InitPeriodicTopmostTimer();\n            windowHandle = new WindowInteropHelper(this).Handle;\n\n            this.k.KeystrokeEvent += KeystrokeEvent;\n\n            OrigInnerPanelBackgroundColor = innerPanel.Background;\n            ActivateDisplayOnlyMode(true);\n            DeactivatePasswordProtectionMode(true);\n\n            if (settings.EnableWindowFade)\n            {\n                FadeOut();\n            }\n        }\n\n        #region periodically make TopMost\n        readonly DispatcherTimer makeTopMostTimer = new DispatcherTimer();\n        void InitPeriodicTopmostTimer()\n        {\n            makeTopMostTimer.Tick += (object sender, EventArgs e) =>\n            {\n                IntPtr handle = new WindowInteropHelper(this).Handle;\n                NativeMethodsWindow.SetWindowTopMost(handle);\n            };\n            makeTopMostTimer.Interval = TimeSpan.FromSeconds(1.0);\n\n            settings.PropertyChanged += (object sender, PropertyChangedEventArgs e) =>\n            {\n                if (e.PropertyName == \"PeriodicTopmost\")\n                {\n                    if (settings.PeriodicTopmost)\n                    {\n                        makeTopMostTimer.Start();\n                    }\n                    else\n                    {\n                        makeTopMostTimer.Stop();\n                    }\n                }\n            };\n        }\n        #endregion\n\n        #region keystroke handler\n\n        void KeystrokeEvent(KeystrokeEventArgs e)\n        {\n            if (settings == null) return;\n            string pressed = e.ShortcutIdentifier();\n            e.raw.preventDefault = e.raw.preventDefault || CheckForSettingsMode(pressed);\n            e.raw.preventDefault = e.raw.preventDefault || CheckForPasswordMode(pressed);\n\n            if (PasswordModeActivated || settings.EnablePasswordMode)\n            {\n                if (e.ShouldBeDisplayed)\n                {\n                    if (settings.EnableWindowFade && !SettingsModeActivated)\n                    {\n                        FadeIn();\n                    }\n                }\n                return;\n            }\n\n            if (e.ShouldBeDisplayed)\n            {\n                if (settings.EnableWindowFade && !SettingsModeActivated)\n                {\n                    FadeIn();\n                }\n\n                if (!e.RequiresNewLine\n                    && settings.KeystrokeMethod == KeystrokeMethodEnum.TextModeBackspaceCanDeleteText\n                    && NumberOfDeletionsAllowed > 0\n                    && LastHistoryLineIsText\n                    && !LastHistoryLineRequiredNewLineAfterwards\n                    && e.NoModifiers\n                    && e.Key == System.Windows.Input.Key.Back)\n                {\n                    if (labels.Count > 0)\n                    {\n                        Log.e(\"BS\", $\"delete last char -> {labels[labels.Count - 1].text}\");\n                    }\n                    Log.e(\"BS\", \"NumberOfDeletionsAllowed \" + NumberOfDeletionsAllowed.ToString());\n                    if (!RemoveLastChar())\n                    {\n                        // again\n                        Log.e(\"BS\", \" failed\");\n                        NumberOfDeletionsAllowed = 0;\n                        KeystrokeEvent(e);\n                        return;\n                    }\n                }\n                else if (e.RequiresNewLine\n                    || !settings.KeystrokeMethod.IsTextMode()\n                    || !AddingWouldFitInCurrentLine(e.ToString(true, false))\n                    || !LastHistoryLineIsText\n                    || LastHistoryLineRequiredNewLineAfterwards)\n                {\n                    if (settings.KeystrokeMethod == KeystrokeMethodEnum.ShortcutModeNoText && e.StrokeType == KeystrokeType.Text)\n                    {\n                        // nothing\n                    } else\n                    {\n                        string e_str = e.ToString(settings.KeystrokeMethod.IsTextMode(), false);\n                        AddNextLine(e_str);\n                        Log.e(\"BS\", $\"new line: {e_str} -> {labels[labels.Count - 1].text}\");\n                        NumberOfDeletionsAllowed = e.Deletable ? 1 : 0;\n                        Log.e(\"BS\", \"NumberOfDeletionsAllowed \" + NumberOfDeletionsAllowed.ToString());\n                    }\n                }\n                else\n                {\n                    string e_str = e.ToString(settings.KeystrokeMethod.IsTextMode(), false);\n                    AddToLine(e_str);\n                    Log.e(\"BS\", $\"add to line: {e_str} -> {labels[labels.Count-1].text}\");\n                    if (e.Deletable)\n                        NumberOfDeletionsAllowed += 1;\n                    else\n                        NumberOfDeletionsAllowed = 0;\n                    Log.e(\"BS\", \"NumberOfDeletionsAllowed \" + NumberOfDeletionsAllowed.ToString());\n                }\n\n                LastHistoryLineIsText = e.StrokeType == KeystrokeType.Text;\n                LastHistoryLineRequiredNewLineAfterwards = e.RequiresNewLineAfterwards;\n\n            }\n        }\n\n\n        #endregion\n\n        #region Opacity Animation\n\n        DoubleAnimation windowOpacityAnim;\n        Storyboard windowOpacitySB;\n\n        private void InitializeAnimations()\n        {\n            windowOpacityAnim = new DoubleAnimation\n            {\n                From = 0.0,\n                To = 1.0,\n                Duration = new Duration(TimeSpan.FromMilliseconds(200)),\n            };\n            windowOpacitySB = new Storyboard();\n            windowOpacitySB.Children.Add(windowOpacityAnim);\n            Storyboard.SetTarget(windowOpacityAnim, this);\n            Storyboard.SetTargetProperty(windowOpacityAnim, new PropertyPath(Window.OpacityProperty));\n        }\n\n        private void FadeOut()\n        {\n            if (!SettingsModeActivated && !PasswordModeActivated)\n            {\n                ToOpacity(0.0, true);\n            }\n        }\n\n        private void FadeIn()\n        {\n            // Opacity now via background color\n            ToOpacity(1.0, true);\n        }\n\n        private void FullOpacity()\n        {\n            ToOpacity(1.0, false);\n        }\n\n        private void ToOpacity(double targetOpacity, bool fade)\n        {\n            if (Math.Abs(Opacity - targetOpacity) > 0.0001)\n            {\n                if (fade)\n                {\n                    // Fixme: Animation is restarting for every single keypress on fade-in\n                    windowOpacitySB.Stop();\n                    windowOpacityAnim.From = this.Opacity;\n                    windowOpacityAnim.To = targetOpacity;\n                    Log.e(\"OPACITY\", $\"Restart Anim, from {Opacity} to {targetOpacity}.\");\n                    windowOpacitySB.Begin(this);\n                } else\n                {\n                    // https://docs.microsoft.com/de-de/dotnet/framework/wpf/graphics-multimedia/how-to-set-a-property-after-animating-it-with-a-storyboard\n                    this.BeginAnimation(Window.OpacityProperty, null); // Break connection between storyboard and property\n                    Opacity = targetOpacity;\n                    Log.e(\"OPACITY\", $\"Remov anim, to {targetOpacity}. {Opacity}\");\n                }\n            }\n        }\n        #endregion\n\n\n\n\n        #region settingsChanged\n\n        private void SettingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            if (settings == null) return;\n            switch (e.PropertyName)\n            {\n                case \"BackgroundColor\":\n                    backgroundGrid.Background = new SolidColorBrush(UIHelper.ToMediaColor(settings.BackgroundColor));\n                    break;\n                case \"WindowLocation\":\n                    this.Left = settings.WindowLocation.X;\n                    this.Top = settings.WindowLocation.Y;\n                    Log.e(\"KD\", String.Format(\"Apply X: {0}\", settings.WindowLocation.X));\n                    break;\n                case \"WindowSize\":\n                    this.Width = settings.WindowSize.Width;\n                    this.Height = settings.WindowSize.Height;\n                    break;\n                case \"PanelLocation\":\n                    this.innerPanel.Margin = new Thickness(settings.PanelLocation.X, settings.PanelLocation.Y, 0.0, 0.0);\n                    break;\n                case \"PanelSize\":\n                    this.innerPanel.Width = settings.PanelSize.Width;\n                    this.innerPanel.Height = settings.PanelSize.Height;\n                    break;\n                case \"LabelFont\":\n                case \"LabelColor\":\n                case \"LabelTextAlignment\":\n                case \"LineDistance\":\n                    UpdateLabelStyles();\n                    break;\n                case \"LabelTextDirection\":\n                    if (labelStack.VerticalAlignment == VerticalAlignment.Top && settings.LabelTextDirection == TextDirection.Up)\n                    {\n                        labelStack.VerticalAlignment = VerticalAlignment.Bottom;\n                        labelStack.Children.Clear();\n                        for(int i = 0; i < labels.Count; i++)\n                        {\n                            labelStack.Children.Add(labels[i].label);\n                        }\n                        UpdateLabelStyles();\n                    } else if (labelStack.VerticalAlignment == VerticalAlignment.Bottom && settings.LabelTextDirection == TextDirection.Down)\n                    {\n                        labelStack.VerticalAlignment = VerticalAlignment.Top;\n                        labelStack.Children.Clear();\n                        for (int i = labels.Count - 1; i >= 0; i--)\n                        {\n                            labelStack.Children.Add(labels[i].label);\n                        }\n                        UpdateLabelStyles();\n                    }\n                    break;\n                case \"HistoryLength\":\n                    TruncateHistory();\n                    break;\n                case \"EnableWindowFade\":\n                    if (settings.EnableWindowFade && labels.Count == 0)\n                    {\n                        FadeOut();\n                    }\n                    else\n                    {\n                        FadeIn();\n                    }\n                    break;\n                case \"KeystrokeHistorySettingsModeShortcut\":\n                    SetSettingsModeShortcut(settings.KeystrokeHistorySettingsModeShortcut);\n                    break;\n                case \"EnableSettingsMode\":\n                    if (settings.EnableSettingsMode)\n                    {\n                        ActivateSettingsMode();\n                    } else\n                    {\n                        ActivateDisplayOnlyMode(false);\n                    }\n                    break;\n                case \"KeystrokeHistoryPasswordModeShortcut\":\n                    SetPasswordModeShortcut(settings.KeystrokeHistoryPasswordModeShortcut);\n                    break;\n                case \"EnablePasswordMode\":\n                    if (settings.EnablePasswordMode)\n                    {\n                        ActivatePasswordProtectionMode();\n                    }\n                    else\n                    {\n                        DeactivatePasswordProtectionMode(false);\n                    }\n                    break;\n            }\n        }\n\n        #endregion\n\n\n        #region Settings Mode\n\n        public string SettingsModeShortcut;\n\n        public void SetSettingsModeShortcut(string shortcut)\n        {\n            if (ValidateShortcutSetting(shortcut))\n            {\n                SettingsModeShortcut = shortcut;\n            }\n            else\n            {\n                SettingsModeShortcut = settings.KeystrokeHistorySettingsModeShortcutDefault;\n            }\n        }\n\n        public static bool ValidateShortcutSetting(string shortcut)\n        {\n            if (shortcut == null)\n                return false;\n            if (shortcut.Length == 0)\n                return false;\n\n            // The last key must not be Ctrl/Alt/Win/Shift, and there must be multiple keys\n            string[] keys = shortcut.Split('+'); // There are spaces around the +, but Split() only accepts chars\n            if (keys.Length < 2)\n                return false;\n            if (keys.Last().Contains(\"Ctrl\"))\n                return false;\n            if (keys.Last().Contains(\"Alt\"))\n                return false;\n            if (keys.Last().Contains(\"Shift\"))\n                return false;\n            if (keys.Last().Contains(\"Win\"))\n                return false;\n\n            return true;\n        }\n\n        private bool CheckForSettingsMode(string pressed)\n        {\n            if (SettingsModeShortcut != null && pressed == SettingsModeShortcut)\n            {\n                settings.EnableSettingsMode = !settings.EnableSettingsMode;\n                return true;\n            }\n            return false;\n        }\n\n        bool SettingsModeActivated = false;\n\n        void ActivateDisplayOnlyMode(bool force)\n        {\n            if (SettingsModeActivated || force)\n            {\n                FadeIn();\n\n                if (!PasswordModeActivated)\n                {\n                    NativeMethodsGWL.ClickThrough(this.windowHandle);\n                }\n                NativeMethodsGWL.HideFromAltTab(this.windowHandle);\n\n                InnerPanelIsDragging = false;\n\n                buttonClose.Visibility = Visibility.Hidden;\n                buttonResizeInnerPanel.Visibility = Visibility.Hidden;\n                buttonResizeWindow.Visibility = Visibility.Hidden;\n                buttonSettings.Visibility = Visibility.Hidden;\n                buttonLeaveSettingsMode.Visibility = Visibility.Hidden;\n                innerPanel.Background = new SolidColorBrush(Color.FromArgb(0,0,0,0));\n                backgroundGrid.Background = new SolidColorBrush(UIHelper.ToMediaColor(settings.BackgroundColor));\n\n                SettingsModeActivated = false;\n\n                settings.SaveAll();\n            }\n        }\n\n        void ActivateSettingsMode()\n        {\n            if (!SettingsModeActivated)\n            {\n                NativeMethodsGWL.CatchClicks(this.windowHandle);\n\n                FullOpacity();\n\n                InnerPanelIsDragging = false;\n\n                buttonClose.Visibility = Visibility.Visible;\n                buttonResizeInnerPanel.Visibility = Visibility.Visible;\n                buttonResizeWindow.Visibility = Visibility.Visible;\n                buttonSettings.Visibility = Visibility.Visible;\n                buttonLeaveSettingsMode.Visibility = Visibility.Visible;\n                innerPanel.Background = OrigInnerPanelBackgroundColor;\n                backgroundGrid.Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));\n\n                foreach (LabelData d in labels)\n                {\n                    d.label.Visibility = Visibility.Hidden;\n                }\n\n                SettingsModeActivated = true;\n            }\n        }\n\n\n\n        #endregion\n\n        #region Password Protection Mode\n\n        public string PasswordModeShortcut;\n\n        public void SetPasswordModeShortcut(string shortcut)\n        {\n            if (ValidateShortcutSetting(shortcut))\n            {\n                PasswordModeShortcut = shortcut;\n            }\n            else\n            {\n                PasswordModeShortcut = settings.KeystrokeHistoryPasswordModeShortcutDefault;\n            }\n            PasswordProtectionModeShortcut.Text = PasswordModeShortcut;\n        }\n\n\n        private bool CheckForPasswordMode(string pressed)\n        {\n            if (PasswordModeShortcut != null && pressed == PasswordModeShortcut)\n            {\n                settings.EnablePasswordMode = !settings.EnablePasswordMode;\n                return true;\n            }\n            return false;\n        }\n\n\n        private void ButtonLeavePasswordMode_Click(object sender, RoutedEventArgs e)\n        {\n            settings.EnablePasswordMode = false;\n        }\n\n        bool PasswordModeActivated = false;\n\n        void ActivatePasswordProtectionMode()\n        {\n            if (!PasswordModeActivated)\n            {\n                NativeMethodsGWL.CatchClicks(this.windowHandle);\n\n                buttonLeavePasswordMode.Visibility = Visibility.Visible;\n                PasswordModeActivated = true;\n            }\n        }\n\n        void DeactivatePasswordProtectionMode(bool force)\n        {\n            if (PasswordModeActivated || force)\n            {\n                if (!SettingsModeActivated)\n                {\n                    NativeMethodsGWL.ClickThrough(this.windowHandle);\n                    if (settings.EnableWindowFade && labels.Count == 0)\n                    {\n                        FadeOut();\n                    }\n                }\n\n                buttonLeavePasswordMode.Visibility = Visibility.Hidden;\n                PasswordModeActivated = false;\n            }\n        }\n\n\n        #endregion\n\n        #region Dragging of Window and innerPanel\n\n        private void BackgroundGrid_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            if (this.SettingsModeActivated)\n            {\n                this.DragMove();\n                e.Handled = true;\n            }\n\n        }\n\n        private void Window_MouseUp(object sender, MouseButtonEventArgs e)\n        {\n            if (this.SettingsModeActivated)\n            {\n                settings.WindowLocation = new Point(this.Left, this.Top);\n            }\n        }\n\n        private bool InnerPanelIsDragging = false;\n        private Point InnerPanelDragStartCursorPosition;\n        private Point InnerPanelDragStartPosition;\n\n        private void InnerPanel_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            InnerPanelDragStartCursorPosition = e.GetPosition(this);\n            InnerPanelIsDragging = true;\n            InnerPanelDragStartPosition = new Point(innerPanel.Margin.Left, innerPanel.Margin.Top);\n            e.Handled = true;\n        }\n\n        private void InnerPanel_MouseMove(object sender, MouseEventArgs e)\n        {\n            if (InnerPanelIsDragging)\n            {\n                Point current = e.GetPosition(this);\n                Point diff = new Point(\n                    current.X - InnerPanelDragStartCursorPosition.X,\n                    current.Y - InnerPanelDragStartCursorPosition.Y);\n                innerPanel.Margin = new Thickness(InnerPanelDragStartPosition.X + diff.X,\n                    InnerPanelDragStartPosition.Y + diff.Y,\n                    0,\n                    0);\n            }\n            e.Handled = true;\n        }\n\n        private void InnerPanel_MouseUp(object sender, MouseButtonEventArgs e)\n        {\n            InnerPanelIsDragging = false;\n            settings.PanelLocation = new Point(innerPanel.Margin.Left, innerPanel.Margin.Top);\n        }\n\n\n        #endregion\n\n        #region Button Click  and Window Close Events\n\n        private void ButtonSettings_Click(object sender, RoutedEventArgs e)\n        {\n            ((App)Application.Current).showSettingsWindow();\n        }\n\n        private void ButtonClose_Click(object sender, RoutedEventArgs e)\n        {\n            System.Windows.Application.Current.Shutdown();\n        }\n\n        private void Window_Closing(object sender, CancelEventArgs e)\n        {\n\n        }\n\n        private void ButtonLeaveSettingsMode_Click(object sender, RoutedEventArgs e)\n        {\n            settings.EnableSettingsMode = false;\n        }\n\n        #endregion\n\n        #region display and animate Label\n\n        class LabelData\n        {\n            public Label label;\n            public string text;\n            public Storyboard storyboard;\n            public DispatcherTimer historyTimeout;\n        }\n\n        readonly List<LabelData> labels = new List<LabelData>(5);\n        bool LastHistoryLineIsText = false;\n        bool LastHistoryLineRequiredNewLineAfterwards = false;\n        int NumberOfDeletionsAllowed = 0;\n\n        void AddToLine(string chars)\n        {\n            LabelData pack = labels[labels.Count - 1];\n            if (pack.historyTimeout != null)\n            {\n                pack.historyTimeout.Stop();\n                if (settings.EnableHistoryTimeout)\n                {\n                    pack.historyTimeout.Interval = TimeSpan.FromSeconds(settings.HistoryTimeout);\n                    pack.historyTimeout.Start();\n                }\n            }\n            pack.text += chars;\n            pack.label.Content = pack.text.Replace(\"_\", \"__\");\n            //T.Refresh();\n        }\n\n        private bool RemoveLastChar()\n        {\n            if (labels.Count == 0)\n            {\n                return false;\n            }\n\n            LabelData pack = labels[labels.Count - 1];\n            var content = ((string)pack.label.Content);\n            if (content.Length == 0)\n                return false;\n            pack.text = pack.text.Substring(0, pack.text.Length - 1);\n            pack.label.Content = pack.text.Replace(\"_\", \"__\");\n            NumberOfDeletionsAllowed -= 1;\n            return true;\n        }\n\n        void AddNextLine(string chars)\n        {\n            Label next = new Label\n            {\n                Content = chars.Replace(\"_\", \"__\")\n            };\n            ApplyLabelStyle(next);\n\n            var pack = new LabelData\n            {\n                label = next,\n                text = chars,\n                storyboard = null,\n                historyTimeout = null,\n            };\n\n            if (settings.LabelAnimation == KeyNStroke.Style.Slide)\n            {\n                Storyboard showLabelSB = new Storyboard();\n                var fadeInAnimation = new DoubleAnimation\n                {\n                    From = 0,\n                    To = 1.0,\n                    Duration = new Duration(TimeSpan.FromMilliseconds(200)),\n                };\n                showLabelSB.Children.Add(fadeInAnimation);\n                Storyboard.SetTarget(fadeInAnimation, next);\n                Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(Label.OpacityProperty));\n\n                Thickness targetMargin = next.Margin; // from ApplyLabelStyle\n                if (settings.LabelTextDirection == TextDirection.Down)\n                {\n                    next.Margin = new Thickness(0, 0, 0, -next.Height);\n                }\n                else\n                {\n                    next.Margin = new Thickness(0, -next.Height, 0, 0);\n                }\n\n                var pushUpwardsAnimation = new ThicknessAnimation\n                {\n                    From = next.Margin,\n                    To = targetMargin,\n                    Duration = new Duration(TimeSpan.FromMilliseconds(200))\n                };\n                showLabelSB.Children.Add(pushUpwardsAnimation);\n                Storyboard.SetTarget(pushUpwardsAnimation, next);\n                Storyboard.SetTargetProperty(pushUpwardsAnimation, new PropertyPath(Label.MarginProperty));\n\n                pack.storyboard = showLabelSB;\n                pack.storyboard.Begin(pack.label);\n            }\n\n            if (settings.LabelTextDirection == TextDirection.Up)\n            {\n                labelStack.Children.Add(next);\n            } else\n            {\n                labelStack.Children.Insert(0, next);\n            }\n\n\n            if (settings.EnableHistoryTimeout)\n            {\n                pack.historyTimeout = FireOnce(settings.HistoryTimeout, () =>\n                {\n                    FadeOutLabel(pack);\n                });\n            }\n            labels.Add(pack);\n\n            TruncateHistory();\n        }\n\n        void TruncateHistory()\n        {\n            while (labels.Count > settings.HistoryLength)\n            {\n                var toRemove = labels[0];\n                Log.e(\"LABELREMOVAL\", $\"Truncate {toRemove.label.Content}. Currently in list: {labels.Count}\");\n                FadeOutLabel(toRemove);\n                labels.Remove(toRemove);\n            }\n        }\n\n        void FadeOutLabel(LabelData toRemove)\n        {\n            if (toRemove.historyTimeout != null)\n            {\n                toRemove.historyTimeout.Stop();\n            }\n            if (toRemove.storyboard != null)\n            {\n                toRemove.storyboard.Remove(toRemove.label);\n            }\n\n            if (settings.LabelAnimation == KeyNStroke.Style.Slide)\n            {\n                Storyboard hideLabelSB = new Storyboard();\n                var fadeOutAnimation = new DoubleAnimation\n                {\n                    From = toRemove.label.Opacity,\n                    To = 0,\n                    Duration = new Duration(TimeSpan.FromMilliseconds(200)),\n                };\n                hideLabelSB.Children.Add(fadeOutAnimation);\n                Storyboard.SetTarget(fadeOutAnimation, toRemove.label);\n                Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(Label.OpacityProperty));\n                fadeOutAnimation.Completed += (object sender, EventArgs e) =>\n                {\n                    Log.e(\"LABELREMOVAL\", $\"{toRemove.label.Content}: Fade out completed\");\n                    hideLabelSB.Remove(toRemove.label);\n                    labelStack.Children.Remove(toRemove.label);\n\n                    if (settings.EnableWindowFade && labels.Count == 0)\n                    {\n                        Log.e(\"LABELREMOVAL\", $\"{toRemove.label.Content}: Fade out completed -> no more labels -> Window wade out\");\n                        FadeOut();\n                    }\n                };\n                hideLabelSB.Begin(toRemove.label);\n                labels.Remove(toRemove);\n            } else\n            {\n                labelStack.Children.Remove(toRemove.label);\n                labels.Remove(toRemove);\n                if (settings.EnableWindowFade && labels.Count == 0)\n                {\n                    Log.e(\"LABELREMOVAL\", $\"{toRemove.label.Content}: Fade out completed -> no more labels -> Window wade out\");\n                    FadeOut();\n                }\n            }\n        }\n\n\n        /// <summary>\n        /// Fires an action once after \"seconds\"\n        /// </summary>\n        public static DispatcherTimer FireOnce(double timeout, Action onElapsed)\n        {\n            var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(timeout) };\n\n            var handler = new EventHandler((s, args) =>\n            {\n                if (timer.IsEnabled)\n                {\n                    onElapsed();\n                }\n                timer.Stop();\n            });\n\n            timer.Tick += handler;\n            timer.Start();\n            return timer;\n        }\n\n        bool AddingWouldFitInCurrentLine(string s)\n        {\n            if (labels.Count == 0)\n                return false;\n\n            var label = labels[labels.Count - 1].label;\n            var text = (String) label.Content + s;\n            var formattedText = new FormattedText(\n                text,\n                CultureInfo.CurrentCulture,\n                FlowDirection.LeftToRight,\n                new Typeface(label.FontFamily, label.FontStyle, label.FontWeight, label.FontStretch),\n                label.FontSize,\n                Brushes.Black,\n                new NumberSubstitution(),\n                1);\n\n            return formattedText.Width < label.ActualWidth - 20;\n        }\n\n        void ApplyLabelStyle(Label label)\n        {\n            label.Height = 120;\n            label.BeginAnimation(Label.MarginProperty, null);\n            if (settings.LabelTextDirection == TextDirection.Down)\n            {\n                label.Margin = new Thickness(0, 0, 0, -label.Height + settings.LineDistance);\n                label.VerticalContentAlignment = VerticalAlignment.Top;\n            }\n            else\n            {\n                label.Margin = new Thickness(0, -label.Height + settings.LineDistance, 0, 0);\n                label.VerticalContentAlignment = VerticalAlignment.Bottom;\n            }\n\n            label.BeginAnimation(Label.OpacityProperty, null);\n            label.Opacity = 1.0;\n            label.Foreground = new SolidColorBrush(UIHelper.ToMediaColor(settings.LabelColor));\n            label.FontSize = settings.LabelFont.Size;\n            label.FontFamily = settings.LabelFont.Family;\n            label.FontStretch = settings.LabelFont.Stretch;\n            label.FontStyle = settings.LabelFont.Style;\n            label.FontWeight = settings.LabelFont.Weight;\n\n            if (settings.LabelTextAlignment == TextAlignment.Left)\n            {\n                label.HorizontalContentAlignment = HorizontalAlignment.Left;\n            }\n            else if (settings.LabelTextAlignment == TextAlignment.Center)\n            {\n                label.HorizontalContentAlignment = HorizontalAlignment.Center;\n            }\n            else\n            {\n                label.HorizontalContentAlignment = HorizontalAlignment.Right;\n            }\n        }\n\n        void UpdateLabelStyles()\n        {\n            foreach (var pack in labels)\n            {\n                ApplyLabelStyle(pack.label);\n            }\n        }\n\n\n\n\n\n\n\n        #endregion\n\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/KeystrokeEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nnamespace KeyNStroke\n{\n    public enum KeystrokeType\n    {\n        Undefined,\n        Shortcut,\n        Text,\n        Modifiers\n    }\n\n    public class KeystrokeEventArgs\n    {\n        public string TextModeString; // for use in TextMode\n        public string ShortcutString; // will always contain a shortcut\n\n        public KeyboardRawEventArgs raw;\n\n        public bool Shift { get { return raw.Shift; } set { raw.Shift = value; } }\n        public bool LShift { get { return raw.LShift; } set { raw.LShift = value; } }\n        public bool RShift { get { return raw.RShift; } set { raw.RShift = value; } }\n        public bool Ctrl { get { return raw.Ctrl; } set { raw.Ctrl = value; } }\n        public bool LCtrl { get { return raw.LCtrl; } set { raw.LCtrl = value; } }\n        public bool RCtrl { get { return raw.RCtrl; } set { raw.RCtrl = value; } }\n        public bool Caps { get { return raw.Caps; } set { raw.Caps = value; } }\n        public bool LWin { get { return raw.LWin; } set { raw.LWin = value; } }\n        public bool RWin { get { return raw.RWin; } set { raw.RWin = value; } }\n        public bool Alt { get { return raw.Alt; } set { raw.Alt = value; } }\n        public bool LAlt { get { return raw.LAlt; } set { raw.LAlt = value; } }\n        public bool RAlt { get { return raw.RAlt; } set { raw.RAlt = value; } } // Alt Gr\n        public bool Numlock { get { return raw.Numlock; } set { raw.Numlock = value; } }\n        public bool Scrollock { get { return raw.Scrollock; } set { raw.Scrollock = value; } }\n\n        public Key Key { get { return raw.Key; } }\n        public KeyUpDown Method { get { return raw.Method; } }\n\n        public bool Uppercase { get { return raw.Uppercase; } }\n        public bool OnlyShiftOrCaps { get { return raw.OnlyShiftOrCaps; } }\n        public bool NoModifiers { get { return raw.NoModifiers; } }\n        public bool Win { get { return raw.Win; } }\n\n        public bool OrigShift;\n        public bool OrigLShift;\n        public bool OrigRShift;\n        public bool OrigCaps;\n\n        public bool IsAlpha;\n        public bool IsNumericFromNumpad;\n        public bool IsNumericFromNumbers;\n        public bool IsFunctionKey;\n        public bool IsNoUnicodekey;\n        public bool ModifierToggledEvent;\n\n        public KeystrokeType StrokeType = KeystrokeType.Undefined;\n        public bool ShouldBeDisplayed;\n        public bool RequiresNewLine;\n        public bool RequiresNewLineAfterwards;\n        public bool Deletable = false;\n\n        public bool IsNumeric { get { return IsNumericFromNumbers || IsNumericFromNumpad;  } }\n\n        public override string ToString()\n        {\n            return ToString(true);\n        }\n\n        public string ToString(bool textMode)\n        {\n            if (ShortcutString != null && (StrokeType == KeystrokeType.Shortcut || !textMode))\n            {\n                return ShortcutString;\n            }\n            else if (StrokeType == KeystrokeType.Text && textMode)\n            {\n                return TextModeString;\n            }\n            return \"BUG2\";\n        }\n\n        public string ToString(bool textMode, bool DoubleAmpersand)\n        {\n            return DoubleAmpersand ? ToString(textMode).Replace(\"&\", \"&&\") : ToString(textMode);\n        }\n\n        public string AsShortcutString()\n        {\n            List<string> output = ShortcutModifiersToList();\n            if (StrokeType == KeystrokeType.Text)\n            {\n                output.Add(TextModeString.ToUpper());\n            }\n            else\n            {\n                output.Add(TextModeString);\n            }\n            return string.Join(\" + \", output);\n        }\n\n        public List<string> ShortcutModifiersToList()\n        {\n            List<string> Modifiers = new List<string>();\n            if (OrigShift) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftShift));\n            if (Ctrl) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftCtrl));\n            if (Alt) Modifiers.Add(SpecialkeysParser.ToString(Key.LeftAlt));\n            if (Win) Modifiers.Add(SpecialkeysParser.ToString(Key.LWin));\n            return Modifiers;\n        }\n\n        public string ShortcutIdentifier()\n        {\n            if (StrokeType == KeystrokeType.Text)\n            {\n                return null;\n            }\n            else if(StrokeType == KeystrokeType.Shortcut)\n            {\n\n                List<string> output = new List<string>();\n\n                if (this.LCtrl)\n                {\n                    output.Add(\"LeftCtrl\");\n                }\n                if (this.RCtrl)\n                {\n                    output.Add(\"RightCtrl\");\n                }\n                // else if (this.Ctrl)\n                // {\n                    // output.Add(\"Ctrl\");\n                // }\n\n                if (this.LWin)\n                {\n                    output.Add(\"LeftWin\");\n                }\n                if (this.RWin)\n                {\n                    output.Add(\"RightWin\");\n                }\n\n                if (this.LAlt)\n                {\n                    output.Add(\"LeftAlt\");\n                }\n                if (this.RAlt)\n                {\n                    output.Add(\"RightAlt\");\n                }\n                // else if (this.Alt)\n                // {\n                    // output.Add(\"Alt\");\n                // }\n\n                if (this.LShift)\n                {\n                    output.Add(\"LeftShift\");\n                }\n                if (this.RShift)\n                {\n                    output.Add(\"RightShift\");\n                }\n                // else if (this.Shift)\n                // {\n                // output.Add(\"Shift\");\n                // }\n                string trimmed = TextModeString.Trim();\n                if (trimmed.Length == 0) // The Space key\n                {\n                    output.Add(TextModeString);\n                }\n                else\n                {\n                    output.Add(trimmed);\n                }\n                return string.Join(\" + \", output);\n            }\n            return null;\n        }\n\n\n        public KeystrokeEventArgs(KeyboardRawEventArgs e)\n        {\n            this.raw = e;\n            this.OrigShift = e.Shift;\n            this.OrigCaps = e.Caps;\n            this.OrigLShift = e.LShift;\n            this.OrigRShift = e.RShift;\n        }\n    }\n\n    public delegate void KeystrokeEventHandler(KeystrokeEventArgs e);\n\n    public interface IKeystrokeEventProvider\n    {\n        event KeystrokeEventHandler KeystrokeEvent;\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/KeystrokeParser.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Input;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    public class KeystrokeParser : IKeystrokeEventProvider\n    {\n        //KeysConverter Converter = new KeysConverter();\n\n        #region Constructor\n\n        public KeystrokeParser(IKeyboardRawEventProvider hook)\n        {\n            hook.KeyEvent += hook_KeyEvent;\n        }\n\n        #endregion\n\n        /// <summary>\n        /// Gets a KeyboardRawKeyEvent, parses it and forwards it via\n        /// the KeystrokeEvent\n        /// </summary>\n        /// <param name=\"e\"></param>\n        void hook_KeyEvent(KeyboardRawEventArgs raw_e)\n        {\n            KeystrokeEventArgs e = new KeystrokeEventArgs(raw_e);\n\n            e.IsAlpha = CheckIsAlpha(e.raw);\n            e.IsNumericFromNumpad = CheckIsNumericFromNumpad(e.raw);\n            e.IsNumericFromNumbers = CheckIsNumericFromNumbers(e.raw);\n            e.IsNoUnicodekey = CheckIsNoUnicodekey(e.raw);\n            e.IsFunctionKey = CheckIsFunctionKey(e.raw);\n            e.ModifierToggledEvent = CheckKeyIsModifier(e.raw);\n\n            Log.e(\"KP\", \" hook_KeyEvent(): Called. IsAlpha=\" + e.IsAlpha.ToString() + \" e.Key=\" + (e.Key).ToString());\n\n            if (e.Method == KeyUpDown.Down)\n            {\n                ApplyDeadKey(e);\n                if (e.IsAlpha && e.OnlyShiftOrCaps)\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): IsAlpha and OnlyShiftOrCaps => ParseChar\");\n                    e.TextModeString = ParseChar(e);\n                    e.ShouldBeDisplayed = true;\n                    e.StrokeType = KeystrokeType.Text;\n                    e.Deletable = true;\n                }\n                else if (e.IsNumeric && e.NoModifiers)\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.IsNumeric && e.NoModifiers => ParseNumeric\");\n                    e.TextModeString = ParseNumeric(e);\n                    e.ShouldBeDisplayed = true;\n                    e.StrokeType = KeystrokeType.Text;\n                    e.Deletable = true;\n                }\n                else if (e.ModifierToggledEvent) // key is modifier\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.ModifierToggledEvent => AddModifier(\" + e.Key.ToString() + \")\");\n                    e.ShouldBeDisplayed = false;\n                    AddModifier(e.Key, e);\n                    e.StrokeType = KeystrokeType.Modifiers;\n                }\n                else if (e.IsNoUnicodekey && e.NoModifiers)\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.IsNoUnicodekey && e.NoModifiers => ParseTexttViaSpecialkeysParser \");\n                    ParseTexttViaSpecialkeysParser(e);\n                    e.Deletable = IsDeletableSpecialKey(e.Key);\n                }\n                else if (e.IsNoUnicodekey && !e.NoModifiers) // Shortcut\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.IsNoUnicodekey && !e.NoModifiers => ParseShortcutViaSpecialkeysParser \");\n                    ParseShortcutViaSpecialkeysParser(e);\n                }\n                else if (e.NoModifiers) // Simple Key, but not alphanumeric (first try special then unicode)\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.NoModifiers => try SpecialkeysParser.ToString \");\n                    try\n                    {\n                        e.TextModeString = SpecialkeysParser.ToString(e.Key);\n                    }\n                    catch (NotImplementedException)\n                    {\n                        Log.e(\"KP\", \"  hook_KeyEvent(): failed, now   => try KeyboardLayoutParser.ParseViaToUnicode \");\n                        e.TextModeString = KeyboardLayoutParser.ParseViaToUnicode(e.raw);\n                        BackupDeadKey(e);\n                    }\n                    e.ShouldBeDisplayed = true;\n                    e.StrokeType = KeystrokeType.Text;\n                    e.RequiresNewLineAfterwards = e.Key == Key.Return;\n                }\n                else if (e.OnlyShiftOrCaps) //  special char, but only Shifted, eg ;:_ÖÄ'*ÜP\n                // (e.IsNoUnicodekey is always false here -> could be a unicode key combination)\n                {\n                    Log.e(\"KP\", \"  hook_KeyEvent(): e.OnlyShiftOrCaps => try KeyboardLayoutParser.ParseViaToUnicode \");\n                    e.TextModeString = KeyboardLayoutParser.ParseViaToUnicode(e.raw);\n                    BackupDeadKey(e);\n\n                    if (e.TextModeString != \"\")\n                    {\n                        e.ShouldBeDisplayed = true;\n                        e.StrokeType = KeystrokeType.Text;\n                        e.Deletable = true;\n                    }\n                    else\n                    {\n                        // Is no unicode key combination? maybe a shortcut then: Shift + F2\n                        Log.e(\"KP\", \"  hook_KeyEvent(): failed, now       => ParseShortcutViaSpecialkeysParser \");\n                        ParseShortcutViaSpecialkeysParser(e);\n                    }\n                }\n                else if (!e.NoModifiers && !e.OnlyShiftOrCaps) // Special Char with Strg + Alt\n                // or shortcut else\n                {\n\n                    // could be something like the german @ (Ctrl + Alt + Q)\n                    Log.e(\"KP\", \"  hook_KeyEvent(): !e.NoModifiers && !e.OnlyShiftOrCaps => (KeyboardLayoutParser.ParseViaToUnicode) (disabled)\");\n                    // Temporary disabled because ToUnicode returns more often values than it should\n                    e.TextModeString = \"\"; //KeyboardLayoutParser.ParseViaToUnicode(e);\n                    // all other special char keycodes do not use Shift\n                    if (e.TextModeString != \"\" && !e.Shift && !e.IsNoUnicodekey)\n                    {\n                        e.ShouldBeDisplayed = true;\n                        e.StrokeType = KeystrokeType.Text;\n                        e.Deletable = true;\n                    }\n                    else // Shortcut\n                    {\n                        Log.e(\"KP\", \"  hook_KeyEvent(): !e.NoModifiers && !e.OnlyShiftOrCapss => ParseShortcutViaSpecialkeysParser \");\n                        ParseShortcutViaSpecialkeysParser(e);\n                        string possibleChar = KeyboardLayoutParser.ParseViaToUnicode(e.raw);\n                        BackupDeadKey(e);\n                        if (possibleChar != \"\" && !CheckIsAlpha(possibleChar) \n                                        && !CheckIsNumeric(possibleChar)\n                                        && CheckIsASCII(possibleChar))\n                        {\n                            e.TextModeString += \" (\" + possibleChar + \")\";\n                        }\n                    }\n                }\n                e.ShortcutString = e.AsShortcutString();\n                Log.e(\"KP\", \"  hook_KeyEvent(): Result: TextmodeString=\" + e.TextModeString + \" ShortcutString=\" + e.ShortcutString);\n            }\n\n            if (e.Method == KeyUpDown.Up)\n            {\n                if (e.ModifierToggledEvent) // key is modifier\n                {\n                    e.ShouldBeDisplayed = false;\n                    RemoveModifier(e.Key, e);\n                    e.StrokeType = KeystrokeType.Modifiers;\n                }\n                Log.e(\"KP\", \"  hook_KeyEvent(): Key up. e.Key=\" + (e.Key).ToString());\n\n                // only react to modifiers on key up, nothing else\n            }\n\n            if (e.StrokeType != KeystrokeType.Undefined)\n            {\n                OnKeystrokeEvent(e);\n            }\n\n        }\n\n\n        private KeystrokeEventArgs lastDeadKeyEvent;\n\n        private void BackupDeadKey(KeystrokeEventArgs e)\n        {\n            if(e.TextModeString == \"DEADKEY\")\n            {\n                e.TextModeString = \"!\";\n                lastDeadKeyEvent = e;\n            }\n        }\n\n        private bool ApplyDeadKey(KeystrokeEventArgs e)\n        {\n            if(lastDeadKeyEvent != null)\n            {\n                Log.e(\"KP\", \"  ApplyDeadKey(): Process Previous Dead Key: \" + lastDeadKeyEvent.ToString());\n                lastDeadKeyEvent.TextModeString = KeyboardLayoutParser.ProcessDeadkeyWithNextKey(lastDeadKeyEvent.raw, e.raw);\n                if(lastDeadKeyEvent.TextModeString == \"\")\n                {\n                    lastDeadKeyEvent = null;\n                    return false;\n                }\n                OnKeystrokeEvent(lastDeadKeyEvent);\n                lastDeadKeyEvent = null;\n                return true;\n            }\n            return false;\n        }\n\n        private void ParseShortcutViaSpecialkeysParser(KeystrokeEventArgs e)\n        {\n            try\n            {\n                e.TextModeString = SpecialkeysParser.ToString(e.Key);\n            }\n            catch (NotImplementedException)\n            {\n                e.TextModeString = e.Key.ToString();\n            }\n            e.ShouldBeDisplayed = true;\n            e.StrokeType = KeystrokeType.Shortcut;\n            e.RequiresNewLine = true;\n            e.RequiresNewLineAfterwards = true;\n        }\n\n        private void ParseTexttViaSpecialkeysParser(KeystrokeEventArgs e)\n        {\n            try\n            {\n                e.TextModeString = SpecialkeysParser.ToString(e.Key);\n                e.ShouldBeDisplayed = true;\n                e.StrokeType = KeystrokeType.Text;\n                e.RequiresNewLineAfterwards = e.Key == Key.Return;\n            }\n            catch (NotImplementedException)\n            {\n\n            }\n        }\n\n        private void AddModifier(Key keys, KeystrokeEventArgs e)\n        {\n            switch (keys)\n            {\n                case Key.LeftCtrl: e.LCtrl = true; e.Ctrl = true; break;\n                case Key.RightCtrl: e.RCtrl = true; e.Ctrl = true; break;\n                case Key.LeftShift: e.LShift = true; e.Shift = true; break;\n                case Key.RightShift: e.RShift = true; e.Shift = true; break;\n                case Key.LWin: e.LWin = true; break;\n                case Key.RWin: e.RWin = true; break;\n                case Key.LeftAlt: e.LAlt = true; e.Alt = true; break;\n                case Key.RightAlt: e.RAlt = true; e.Alt = true; break;\n                case Key.NumLock: e.Numlock = true; break;\n                case Key.Scroll: e.Scrollock = true; break;\n                case Key.CapsLock: e.Caps = true; break;\n            }\n        }\n\n        private void RemoveModifier(Key keys, KeystrokeEventArgs e)\n        {\n            // If Left Shift is released, then only unset Shift if RShift is not set\n            switch (keys)\n            {\n                case Key.LeftCtrl: e.LCtrl = false; e.Ctrl = e.RCtrl; break;\n                case Key.RightCtrl: e.RCtrl = false; e.Ctrl = e.LCtrl; break;\n                case Key.LeftShift: e.LShift = false; e.Shift = e.RShift; break;\n                case Key.RightShift: e.RShift = false; e.Shift = e.LShift; break;\n                case Key.LWin: e.LWin = false; break;\n                case Key.RWin: e.RWin = false; break;\n                case Key.LeftAlt: e.LAlt = false; e.Alt = e.RAlt; break;\n                case Key.RightAlt: e.RAlt = false; e.Alt = e.LAlt; break;\n                case Key.NumLock: e.Numlock = false; break;\n                case Key.Scroll: e.Scrollock = false; break;\n                case Key.CapsLock: e.Caps = false; break;\n            }\n        }\n\n        private string ParseChar(KeystrokeEventArgs e)\n        {\n            string c = e.Key.ToString();\n            if (!e.Uppercase)\n                c = c.ToLower();\n\n            // We parse the Shift and Caps keys into the Upper/Lowercase\n            e.Shift = false;\n            e.Caps = false;\n            e.LShift = false;\n            e.RShift = false;\n            return c;\n        }\n\n        private string ParseNumeric(KeystrokeEventArgs e)\n        {\n            if (e.IsNumericFromNumbers)\n            {\n                return ((int)e.Key - (int)Key.D0).ToString();\n            }\n            else if (e.IsNumericFromNumpad)\n            {\n                return ((int)e.Key - (int)Key.NumPad0).ToString();\n            }\n            return \"BUG1\";\n        }\n\n        bool CheckIsAlpha(KeyboardRawEventArgs e)\n        {\n            return CheckIsAlpha(e.Key);\n        }\n\n        bool CheckIsAlpha(Key key)\n        {\n            return ((int)Key.A <= (int)key && (int)key <= (int)Key.Z);\n        }\n\n        bool CheckIsAlpha(string s)\n        {\n            return (s.Length == 1 && ( (s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z')));\n        }\n\n        bool CheckIsASCII(string s)\n        {\n            return (s.Length == 1 && ((s[0] >= 32 && s[0] <= 126) ));\n        }\n\n        bool CheckIsNumericFromNumpad(KeyboardRawEventArgs e)\n        {\n            return CheckIsNumericFromNumpad(e.Key);\n        }\n\n        bool CheckIsNumericFromNumpad(Key key)\n        {\n            return ((int)Key.NumPad0 <= (int)key && (int)key <= (int)Key.NumPad9);\n        }\n\n        bool CheckIsNumeric(string s)\n        {\n            return (s.Length == 1 && (s[0] >= '0' && s[0] <= '9'));\n        }\n        \n        bool CheckIsNumericFromNumbers(KeyboardRawEventArgs e)\n        {\n            return CheckIsNumericFromNumbers(e.Key);\n        }\n\n        bool CheckIsNumericFromNumbers(Key key)\n        {\n            return ((int)Key.D0 <= (int)key && (int)key <= (int)Key.D9);\n        }\n\n        bool CheckIsFunctionKey(KeyboardRawEventArgs e)\n        {\n            return ((int)Key.F1 <= (int)e.Key && (int)e.Key <= (int)Key.F24);\n        }\n\n        /// <summary>\n        /// If this function returns true, there will be no attemt to decode the key press\n        /// via ToUnicode to reveal Strg+Alt+E = € .\n        /// </summary>\n        /// <param name=\"e\"></param>\n        /// <returns></returns>\n        bool CheckIsNoUnicodekey(KeyboardRawEventArgs e)\n        {\n            Key[] NoUnicodeKeys = { Key.Cancel, Key.Tab, Key.LineFeed, Key.Clear, \n                   Key.Enter, Key.Return, \n                   Key.Pause, Key.CapsLock, Key.Capital, Key.KanaMode,\n                   Key.JunjaMode, Key.FinalMode, Key.KanjiMode, Key.HanjaMode,\n                   Key.Escape, Key.ImeConvert, Key.ImeNonConvert, Key.ImeAccept,\n                   Key.ImeModeChange, Key.Space, Key.Prior, Key.PageUp, Key.Next, \n                   Key.PageDown, Key.End, Key.Home, Key.Left, Key.Up, Key.Right, \n                   Key.Down, Key.Select, Key.Print, Key.Execute, Key.PrintScreen,\n                   Key.Snapshot, Key.Insert, Key.Delete, Key.Help,\n                   Key.LWin, Key.RWin, Key.Apps, Key.Sleep,\n                   Key.Multiply, Key.Add, Key.Separator, Key.Subtract, Key.Decimal, \n                   Key.Divide, Key.F1, Key.F2, Key.F3, Key.F4, Key.F5, Key.F6, \n                   Key.F7, Key.F8, Key.F9, Key.F10, Key.F11, Key.F12, Key.F13,\n                   Key.F14, Key.F15, Key.F16, Key.F17, Key.F18, Key.F19, Key.F20, \n                   Key.F21, Key.F22, Key.F23, Key.F24, Key.NumLock, Key.Scroll, \n                   Key.LeftShift, Key.RightShift, Key.LeftCtrl, Key.RightCtrl,\n                   Key.LeftAlt, Key.RightAlt, Key.BrowserBack, Key.BrowserForward, \n                   Key.BrowserRefresh, Key.BrowserStop, Key.BrowserSearch, \n                   Key.BrowserFavorites, Key.BrowserHome, Key.VolumeMute, Key.VolumeDown, \n                   Key.VolumeUp, Key.MediaNextTrack, Key.MediaPreviousTrack, Key.MediaStop, \n                   Key.MediaPlayPause, Key.LaunchMail, Key.SelectMedia, Key.LaunchApplication1, \n                   Key.LaunchApplication2, Key.ImeProcessed,\n                   0, Key.Attn, Key.CrSel, Key.ExSel, Key.EraseEof, Key.Play,\n                   Key.Zoom, Key.NoName, Key.Pa1, Key.OemClear };\n            return NoUnicodeKeys.Contains(e.Key);\n        }\n\n        private bool IsDeletableSpecialKey(Key key)\n        {\n            Key[] DeletableKeys = { \n                   Key.Multiply, Key.Add, Key.Subtract, Key.Decimal, \n                   Key.Divide, Key.Space};\n            return DeletableKeys.Contains(key);\n        }\n\n\n        bool CheckKeyIsModifier(KeyboardRawEventArgs e)\n        {\n            Key[] ModifierKeys = { Key.LeftShift, Key.RightShift,\n                                   Key.LeftCtrl, Key.RightCtrl,\n                                   Key.LeftAlt, Key.RightAlt,\n                                   Key.LWin, Key.RWin,\n                                   Key.NumLock, Key.Scroll,\n                                   Key.CapsLock};\n            return ModifierKeys.Contains(e.Key);\n        }\n\n        #region Event Forwarding\n\n        public event KeystrokeEventHandler KeystrokeEvent;\n\n        private void OnKeystrokeEvent(KeystrokeEventArgs e)\n        {\n            if (KeystrokeEvent != null)\n            {\n                KeystrokeEvent(e);\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/LabeledSlider.py",
    "content": "import numpy as np\nimport matplotlib.pyplot as plt\nimport math\nfrom matplotlib.widgets import Slider\n\n\n#      LabeledSlider.Value                                                  X\n#                                                                          XX\n#        f(x) ^                                                    +     XX\n#             |                                                    |    XX\n#             |                                                    |  XX\n#   ymax   +--+                                              +------XX------+\n#             |                                                    XX\n#             |                                                   X|\n#             |                                                 XX |\n#             |                                                X   +\n#             |                                              XX\n#             |                                            XXX\n#             |                                           XX\n#             |                                        XXX\n#             |                                      XXX\n#             |                                    XX\n#             |                         +       XXX\n#             |                         |    XXXX\n#             |                         | XXXX\n#ys(ysper) +--+                   +---XXXX--+\n#             |                    XXXX |\n#             |                XXXXXX   |\n#             |         XXXXXXXX        |\n#    ymin  +--XXXXXXXXXXX               |\n#             |                         +\n#             |\n#             |\n#          +----------------------------+--------------------------+----------------------->  inner Slider.Value\n#             |                         |                          |                       x\n#             +                         +                          +\n#             x0=0                      xs=x1/2                    x1\n#\n#\n#   y = f(x) = a + b * exp(c * x)\n#\n#   Calculate a, b, c from the constants ymin, ys, ymax, x1 so that f(x) fulfills (1), (2) and (3).\n#\n#    (1)       f(x = x0 = 0)      = ymin\n#    (2)       f(x = xs = x1 / 2) = ys\n#    (3)       f(x = x1)          = ymax\n#\n#  xs is not free to chose but set to x1/2 so that while solving the equations, we magically get a quadratic equation.\n#\n#                              (1)                ymin = a + b\n#                              (2)                ys   = a + b * exp(c * x1 / 2)\n#                              (3)                ymax = a + b * exp(c * x1)\n#    \n#    (1) for a                 (4)                a    = ymin - b\n#    (4) in (2)                (5)                ys   = ymin - b + b * exp(c * x1 / 2)\n#    (4) in (3)                (6)                ymax = ymin - b + b * exp(c * x1)\n#    -ymin                     (6)         ymax - ymin = b * (exp(c * x1) - 1)\n#    solve (5) for b           (7)                b    = (ys - ymin) / (exp(c * x1 / 2) - 1)\n#    (7) in (6)                (8)         ymax - ymin = (ys - ymin) / (exp(c * x1 / 2) - 1) * (exp(c * x1) - 1) \n#    *exp(..), -rhs            (8)         (ymax - ymin) * (exp(c * x1 / 2) - 1) - (ys - ymin) * (exp(c * x1) - 1) = 0\n#    set                       (9)                   d = (ymin - ymax) / (ys - ymin)\n#    substitute (9) in (8)     (10)             exp(c * x1    )      + d * e(c * x1 / 2) + (-1 - d) = 0\n#    in first exp: /2*2        (10)             exp(c * x1 / 2) ** 2 + d * e(c * x1 / 2) + (-1 - d) = 0\n#    set                       (11)             v = exp(c * x1 / 2)\n#    substitute (11) in (10)   (12)\t\t\t\tv               ** 2 + d * v             + (-1 - d) == 0\n#    solve quadratic eq (12)   (13)             v1,2 = - (d/2) +- sqrt( (d/2)**2 - (-1-d) )\n#    solve (11) for c          (14)             c = 2 * ln(g) / x1\n#\n#    calculate v1 with (13) and (9). Use (14), (7), (4) to calculate a,b,c\n\n\nfig, ax = plt.subplots()\nplt.subplots_adjust(bottom=0.25)\n\n\n\n\nymax = 120.\nymin = 3.\n#ys=\nysper = 0.2 # value from 0 to 1 that gets mapped to the intervall [ymin+1%, ((ymin+ymax/2)-1%] and used as ys\n\nx0=0.\nx1=1000.\nxs=x1/2\n\n\ndef pq(p, q):\n\tr1 = (-(p/2))\n\tr2 = np.sqrt((p/2)**2 - q)\n\treturn (r1 + r2, r1 - r2)\n\ndef calc(ysper):\n\tysmin = ymin * 1.01\n\tysmax = ((ymin+ymax)/2) * 0.99\n\tys = ysmin + ysper * (ysmax - ysmin)\n\n\td=(ymin-ymax)/(ys-ymin)\n\tv1, v2 = pq(d, -1-d)\n\n\tc = 2 * np.log(v1) / x1\n\tb = (ys - ymin) / (np.exp(c*x1/2) - 1)\n\ta = ymin - b\n\n\tt = np.exp(c*x1) + 1 + d * np.exp(c*x1/2) - d\n\tt1 = (ymax-ymin) * (np.exp(c*x1/2)-1) - (ys-ymin) * (np.exp(c*x1)-1)\n\n\tprint(f\"v1={v1:.3} v2={v2:.3} a={a:.3} b={b:.3} c={c:.3} f(x0)=a+b={a+b:.3}=={ymin:.3}\"\n\t\t  f\" f(xs)=a+bexp(c*x1/2)={a+b*np.exp(c*x1/2):.3}=={ys:.3}\"\n\t\t  f\" f(x1)=a+bexp(c*x1)={a+b*np.exp(c*x1):.3}=={ymax:.3}\")\n\n\tx = np.arange(x0, x1, (x1-x0)/1000)\n\ty = a + b * np.exp(c*x)\n\n\treturn x, y, ys\n\nx, y, ys = calc(ysper)\nl1, = plt.plot(x, y)\n\nplt.plot([x0, x1], [ymax, ymax])\nplt.plot([x0, x1], [ymin, ymin])\nl2, = plt.plot([x0, x1], [ys, ys])\n\nplt.plot([x0, x0], [0, ymax])\nplt.plot([x1, x1], [0, ymax])\nplt.plot([xs, xs], [0, ymax])\n\n\ndef update(ysper):\n\tx, y, ys = calc(ysper)\n\tl1.set_ydata(y)\n\tl1.set_xdata(x)\n\tl2.set_ydata([ys, ys])\n\naxys = plt.axes([0.25, 0.1, 0.65, 0.03])\nsys = Slider(axys, 'ys %', 0, 1.0, valinit=ysper)\n\nsys.on_changed(update)\n\nplt.show()"
  },
  {
    "path": "KeyNStroke/LabeledSlider.xaml",
    "content": "﻿<UserControl x:Class=\"KeyNStroke.LabeledSlider\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:KeyNStroke\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"40\" d:DesignWidth=\"800\">\n    <Grid x:Name=\"layout_root\">\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"/>\n            <ColumnDefinition Width=\"*\"/>\n        </Grid.ColumnDefinitions>\n        <Label Target=\"{Binding ElementName=slider}\" Content=\"{Binding Title}\" Margin=\"0,-3,0,0\"/>\n        <Slider x:Name=\"slider\" \n                Grid.Column=\"1\"\n                Maximum=\"100\"\n                Minimum=\"0\"\n                Value=\"50\"\n                ValueChanged=\"Slider_ValueChanged\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "KeyNStroke/LabeledSlider.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace KeyNStroke\n{\n\n    /// <summary>\n    /// Interaktionslogik für LabeledSlider.xaml\n    /// </summary>\n    public partial class LabeledSlider : UserControl\n    {\n\n        private bool interalValueUpdate = false;\n\n        public LabeledSlider()\n        {\n            InitializeComponent();\n            Log.e(\"BIN\", \"Set Data context in labeled slider\");\n            layout_root.DataContext = this;\n\n            var dpd = DependencyPropertyDescriptor.FromProperty(ValueProperty, typeof(LabeledSlider));\n            dpd.AddValueChanged(this, (object sender, EventArgs e) => {\n                Log.e(\"SLIDER\", $\"{Title}: Value={Value}\");\n                ValueToSlider();\n            });\n\n            var dpd1 = DependencyPropertyDescriptor.FromProperty(MinimumProperty, typeof(LabeledSlider));\n            dpd1.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); });\n\n            var dpd2 = DependencyPropertyDescriptor.FromProperty(MaximumProperty, typeof(LabeledSlider));\n            dpd2.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); });\n\n            var dpd3 = DependencyPropertyDescriptor.FromProperty(HalfWayProperty, typeof(LabeledSlider));\n            dpd3.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider(); });\n\n            var dpd4 = DependencyPropertyDescriptor.FromProperty(LogarithmicProperty, typeof(LabeledSlider));\n            dpd4.AddValueChanged(this, (object sender, EventArgs e) => { recalcParams(); ValueToSlider();  });\n\n        }\n\n\n        [Browsable(true)]\n        public double HalfWay\n        {\n            get { return (double)GetValue(HalfWayProperty); }\n            set { SetValue(HalfWayProperty, value); }\n        }\n        public static readonly DependencyProperty HalfWayProperty =\n            DependencyProperty.Register(\n            \"HalfWay\", typeof(double),\n            typeof(LabeledSlider)\n            );\n\n        [Browsable(true)]\n        public bool Logarithmic\n        {\n            get { return (bool)GetValue(LogarithmicProperty); }\n            set { SetValue(LogarithmicProperty, value); }\n        }\n        public static readonly DependencyProperty LogarithmicProperty =\n            DependencyProperty.Register(\n            \"Logarithmic\", typeof(bool),\n            typeof(LabeledSlider)\n            );\n\n        [Browsable(true)]\n        public String Title\n        {\n            get { return (String) GetValue(TitleProperty); }\n            set { SetValue(TitleProperty, value); }\n        }\n        public static readonly DependencyProperty TitleProperty =\n            DependencyProperty.Register(\n            \"Title\", typeof(String),\n            typeof(LabeledSlider)\n            );\n\n        [Browsable(true)]\n        public double Minimum\n        {\n            get { return (double) GetValue(MinimumProperty); }\n            set { SetValue(MinimumProperty, value); }\n        }\n        public static readonly DependencyProperty MinimumProperty =\n            DependencyProperty.Register(\"Minimum\", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0));\n\n\n        [Browsable(true)]\n        public double Maximum\n        {\n            get { return (double) GetValue(MaximumProperty); }\n            set { SetValue(MaximumProperty, value); }\n        }\n        public static readonly DependencyProperty MaximumProperty =\n            DependencyProperty.Register(\"Maximum\", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0));\n\n\n        [Browsable(true)]\n        public double Value\n        {\n            get { return (double) GetValue(ValueProperty); }\n            set { SetValue(ValueProperty, value); }\n        }\n        public static readonly DependencyProperty ValueProperty =\n            DependencyProperty.Register(\"Value\", typeof(double), typeof(LabeledSlider), new PropertyMetadata(0.0));\n\n\n\n        \n        private double pq(double p, double q) {\n            double r1 = -(p / 2);\n            double r2 = Math.Sqrt(Math.Pow(p / 2, 2) - q);\n            return r1 + r2;\n        }\n\n        private double a;\n        private double b;\n        private double c;\n\n        double m;\n        \n        private void recalcParams()\n        {\n            double ymin = Minimum;\n            double ymax = Maximum;\n            double ysper = Math.Min(1.0, Math.Max(0.0, HalfWay));\n            double x1 = slider.Maximum;\n\n            if (ymax == ymin)\n            {\n                ymax = 10 + ymin;\n            }\n\n            double ysmin = ymin * 1.01;\n            double ysmax = ((ymin + ymax) / 2) * 0.99;\n            double ys = ysmin + ysper * (ysmax - ysmin);\n            double d = (ymin - ymax) / (ys - ymin);\n\n            double v1 = pq(d, -1 - d);\n            c = 2 * Math.Log(v1) / x1;\n            b = (ys - ymin) / (Math.Exp(c * x1 / 2) - 1);\n            a = ymin - b;\n\n            // linear\n            m = (ymax - ymin) / x1;\n            Log.e(\"SLIDER\", $\"{Title}: ymax={ymax} ymin={ymin} x1={x1} m={m}\");\n            if (m == 0)\n            {\n                m = 0.1;\n            }\n        }\n\n        private void ValueToSlider()\n        {\n            if (!interalValueUpdate)\n            {\n                interalValueUpdate = true;\n                double y = Math.Min(Maximum, Math.Max(Minimum, Value));\n                if (Logarithmic)\n                {\n                    slider.Value = Math.Log((y - a) / b) / c;\n                }\n                else\n                {\n                    slider.Value = (y - Minimum) / m;\n                }\n                interalValueUpdate = false;\n            }\n        }\n\n        private void SliderToValue()\n        {\n            if (!interalValueUpdate)\n            {\n                interalValueUpdate = true;\n                if (Logarithmic)\n                {\n                    Value = a + b * Math.Exp(c * slider.Value);\n                }\n                else\n                {\n                    Value = Minimum + m * slider.Value;\n                    Log.e(\"SLIDER\", $\"{Title}: {Value} = {Minimum} {m} * {slider.Value}\");\n                }\n                interalValueUpdate = false;\n            }\n        }\n\n        public event RoutedPropertyChangedEventHandler<double> ValueChanged;\n\n        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            SliderToValue();\n\n            RoutedPropertyChangedEventArgs<double> eint = null;\n            if (e != null)\n            {\n                eint = new RoutedPropertyChangedEventArgs<double>(e.OldValue, e.NewValue, e.RoutedEvent);\n            }\n            if (ValueChanged != null)\n            {\n                ValueChanged.Invoke(sender, eint);\n            }\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Log.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace KeyNStroke\n{\n    public class Log\n    {\n        static string TagFilter = \"\";\n        static string[] allowed = new string[]{};\n        public static void SetTagFilter(string tagfilter)\n        {\n            TagFilter = tagfilter;\n            allowed = TagFilter.Split(new char[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries);\n        }\n\n        static bool filter(string tag)\n        {\n            return tag == TagFilter || TagFilter == \"\" || allowed.Contains(tag);\n        }\n\n        public static void e(string tag, string msg)\n        {\n            if(filter(tag))\n            {\n                Console.WriteLine(tag.ToUpper() + \" \" + msg);\n            }\n        }\n\n        public static void e(string msg)\n        {\n            e(\"ERR\", msg);\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/MouseHook.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n\n    /// <summary>\n    /// Low level mouse event interception class\n    /// </summary>\n    public class MouseHook : IDisposable, IMouseRawEventProvider\n    {\n\n        #region Initializion\n\n        int doubleClickTime;\n        SettingsStore s;\n\n        /// <summary>\n        /// Set up the mouse hook\n        /// </summary>\n        public MouseHook(SettingsStore s)\n        {\n            this.s = s;\n            doubleClickTime = NativeMethodsMouse.GetDoubleClickTime();\n            Log.e(\"ME\", \"doubleClickTime \" + doubleClickTime.ToString());\n            RegisterMouseHook();\n        }\n\n        #endregion\n\n        #region Hook Add/Remove\n\n\n        //Variables used in the call to SetWindowsHookEx\n        private NativeMethodsMouse.HookHandlerDelegate windowsHookExProc;\n        private IntPtr windowsHookExID = IntPtr.Zero;\n\n        //Variables used in the call to SetWinEventHook\n        private NativeMethodsEvents.WinEventDelegate setWinEventHookProc;\n        private IntPtr setWinEventHookID = IntPtr.Zero;\n\n\n        /// <summary>\n        /// Registers the function WindowsHookExCallback for the global mouse events winapi. \n        /// Also registers a WinEventHook to check if the cursor is hidden.\n        /// </summary>\n        private void RegisterMouseHook()\n        {\n            // Register WindowsHookExCallback\n            if (windowsHookExID == IntPtr.Zero) {\n                windowsHookExProc = new NativeMethodsMouse.HookHandlerDelegate(WindowsHookExCallback);\n                using (Process curProcess = Process.GetCurrentProcess())\n                {\n                    using (ProcessModule curModule = curProcess.MainModule)\n                    {\n                        windowsHookExID = NativeMethodsMouse.SetWindowsHookEx(NativeMethodsMouse.WH_MOUSE_LL, windowsHookExProc,\n                            NativeMethodsKeyboard.GetModuleHandle(curModule.ModuleName), 0);\n                    }\n                }\n            }\n\n            // Register WinEventCallback\n            // https://devblogs.microsoft.com/oldnewthing/20151116-00/?p=92091\n            if (setWinEventHookID == IntPtr.Zero) {\n                setWinEventHookProc = new NativeMethodsEvents.WinEventDelegate(WinEventCallback);\n                setWinEventHookID = NativeMethodsEvents.SetWinEventHook(\n                    NativeMethodsEvents.WinEvents.EVENT_OBJECT_SHOW,\n                    NativeMethodsEvents.WinEvents.EVENT_OBJECT_NAMECHANGE,\n                    IntPtr.Zero,\n                    setWinEventHookProc,\n                    0, // ProcessId = 0 and ThreadId = 0 -> global events\n                    0,\n                    NativeMethodsEvents.WinEventFlags.WINEVENT_SKIPOWNPROCESS\n                    );\n            }\n        }\n        \n        /// <summary>\n        /// Unregisters the winapi hook for global mouse events\n        /// </summary>\n        private void UnregisterMouseHook()\n        {\n            if (windowsHookExID != IntPtr.Zero)\n            {\n                NativeMethodsMouse.UnhookWindowsHookEx(windowsHookExID);\n                windowsHookExID = IntPtr.Zero;\n            }\n            if (setWinEventHookID != IntPtr.Zero)\n            {\n                NativeMethodsEvents.UnhookWinEvent(setWinEventHookID);\n                setWinEventHookID = IntPtr.Zero;\n            }\n        }\n\n        #endregion\n\n\n        #region Event Handling\n\n        MouseRawEventArgs lastDownEvent;\n\n        /// <summary>\n        /// Processes the key event captured by the hook.\n        /// </summary>\n        private IntPtr WindowsHookExCallback(int nCode, \n                                    UIntPtr wParam,\n                                    ref NativeMethodsMouse.MSLLHOOKSTRUCT lParam)\n        {\n            if (nCode < 0)\n            {\n                return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam);\n            }\n            else\n            {\n                if (nCode == NativeMethodsMouse.HC_ACTION)\n                {\n                    MouseRawEventArgs args = new MouseRawEventArgs(lParam);\n                    args.ParseWparam(wParam);\n                    CheckDoubleClick(args);\n\n                    Log.e(\"ME\", String.Format(\"MOUSE: Button:{0} Action:{1} Orig:{2}\",\n                        args.Button.ToString(), args.Action.ToString(),\n                        args.Event.ToString()));\n\n                    OnMouseEvent(args);\n                    if (args.preventDefault)\n                    {\n                        return IntPtr.Add(IntPtr.Zero, 1);\n                    }\n                    else\n                    {\n                        return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam);\n                    }\n                }\n                else\n                {\n                    return NativeMethodsMouse.CallNextHookEx(windowsHookExID, nCode, wParam, ref lParam);\n                }\n            }\n        }\n\n        private void CheckDoubleClick(MouseRawEventArgs args)\n        {\n            if (lastDownEvent != null && args.Action == MouseAction.Down)\n            {\n                if (args.Button == lastDownEvent.Button\n                    && args.Msllhookstruct.time <= lastDownEvent.Msllhookstruct.time + doubleClickTime\n                    && args.Msllhookstruct.pt.Equals(lastDownEvent.Msllhookstruct.pt))\n                {\n                    args.Action = MouseAction.DblClk;\n                    Log.e(\"ME\", \"DBLCLK\");\n                }\n            }\n\n            if (args.Action == MouseAction.Down)\n                lastDownEvent = args;\n        }\n\n        private void WinEventCallback(IntPtr hWinEventHook, NativeMethodsEvents.WinEvents eventType, IntPtr hwnd, uint idObject, uint idChild, uint dwEventThread, uint dwmsEventTime)\n        {\n            if (hwnd == IntPtr.Zero &&\n                idObject == (uint)NativeMethodsEvents.ObjIds.OBJID_CURSOR &&\n                idChild == NativeMethodsEvents.CHILDID_SELF)\n            {\n                switch (eventType)\n                {\n                    case NativeMethodsEvents.WinEvents.EVENT_OBJECT_HIDE:\n                        OnCursorEvent(false);\n                        break;\n                    case NativeMethodsEvents.WinEvents.EVENT_OBJECT_SHOW:\n                        OnCursorEvent(true);\n                        break;\n                    case NativeMethodsEvents.WinEvents.EVENT_OBJECT_NAMECHANGE:\n                        if (s.CursorIndicatorHideIfCustomCursor)\n                        {\n                            NativeMethodsMouse.CURSORINFO info = NativeMethodsMouse.GetCursorInfoWrapper();\n                            Log.e(\"CURSOR\", $\"EVENT_OBJECT_NAMECHANGE flags={info.flags} hCursor={info.hCursor}\");\n                            if (info.flags == NativeMethodsMouse.CURSOR_HIDDEN || ((uint)info.hCursor) >= 0x100000) /* limit guessed */\n                            {\n                                OnCursorEvent(false);\n                            }\n                            else\n                            {\n                                OnCursorEvent(true);\n                            }\n                        }\n                        break;\n                }\n            }\n        }\n\n        #endregion\n\n        #region Event Forwarding\n\n        /// <summary>\n        /// Fires if mouse is moved or clicked.\n        /// </summary>\n        public event MouseRawEventHandler MouseEvent;\n        \n        /// <summary>\n        /// Raises the MouseEvent event.\n        /// </summary>\n        /// <param name=\"e\">An instance of MouseRawEventArgs</param>\n        public void OnMouseEvent(MouseRawEventArgs e)\n        {\n            if (MouseEvent != null)\n                MouseEvent(e);\n        }\n\n        /// <summary>\n        /// Fires if cursor is shown or hidden.\n        /// </summary>\n        public event CursorEventHandler CursorEvent;\n\n        /// <summary>\n        /// Raises the CursorEvent event.\n        /// </summary>\n        /// <param name=\"visible\">True if the cursor is visible</param>\n        public void OnCursorEvent(bool visible)\n        {\n            if (CursorEvent != null)\n                CursorEvent(visible);\n        }\n        #endregion\n\n\n        #region Finalizing and Disposing\n\n        ~MouseHook()\n        {\n            Log.e(\"HOOK\", \"~MouseHook\");\n            UnregisterMouseHook();\n        }\n\n        /// <summary>\n        /// Releases the mouse hook.\n        /// </summary>\n        public void Dispose()\n        {\n            Log.e(\"HOOK\", \"Dispose MouseHook\");\n            UnregisterMouseHook();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/MouseRawEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    public enum MouseEventType\n    {\n        MOUSEMOVE,\n        LBUTTONDOWN,\n        LBUTTONUP,\n        LBUTTONDBLCLK,\n        RBUTTONDOWN,\n        RBUTTONUP,\n        RBUTTONDBLCLK,\n        MBUTTONDOWN,\n        MBUTTONUP,\n        MBUTTONDBLCLK,\n        MOUSEWHEEL1,\n        XBUTTONDOWN,\n        XBUTTONUP,\n        XBUTTONDBLCLK,\n        MOUSEHWHEEL2\n    }\n\n    public enum MouseButton\n    {\n        LButton,\n        RButton,\n        MButton,\n        XButton,\n        None\n    }\n\n    public enum MouseAction\n    {\n        Up,\n        Down,\n        DblClk,\n        Move,\n        Wheel,\n    }\n\n    public class MouseRawEventArgs\n    {\n        public NativeMethodsMouse.MSLLHOOKSTRUCT Msllhookstruct;\n        public MouseEventType Event;\n        public MouseButton Button;\n        public MouseAction Action;\n        public int wheelDelta = 0;\n        public bool preventDefault = false;\n\n        public MouseRawEventArgs(NativeMethodsMouse.MSLLHOOKSTRUCT msllhookstruct)\n        {\n            this.Msllhookstruct = msllhookstruct;\n        }\n\n        public NativeMethodsMouse.POINT Position\n        {\n            get { return Msllhookstruct.pt; }\n        }\n\n        public void ParseWparam(UIntPtr wParam)\n        {\n            switch((int) wParam)\n            {\n            case NativeMethodsMouse.WM_MOUSEMOVE:\n                Event = MouseEventType.MOUSEMOVE;\n                Button = MouseButton.None;\n                Action = MouseAction.Move;\n                break;\n            case NativeMethodsMouse.WM_LBUTTONDOWN:\n                Event = MouseEventType.LBUTTONDOWN;\n                Button = MouseButton.LButton;\n                Action = MouseAction.Down;\n                break;\n            case NativeMethodsMouse.WM_LBUTTONUP:\n                Event = MouseEventType.LBUTTONUP;\n                Button = MouseButton.LButton;\n                Action = MouseAction.Up;\n                break;\n            case NativeMethodsMouse.WM_LBUTTONDBLCLK:\n                Event = MouseEventType.LBUTTONDBLCLK;\n                Button = MouseButton.LButton;\n                Action = MouseAction.DblClk;\n                break;\n            case NativeMethodsMouse.WM_RBUTTONDOWN:\n                Event = MouseEventType.RBUTTONDOWN;\n                Button = MouseButton.RButton;\n                Action = MouseAction.Down;\n                break;\n            case NativeMethodsMouse.WM_RBUTTONUP:\n                Event = MouseEventType.RBUTTONUP;\n                Button = MouseButton.RButton;\n                Action = MouseAction.Up;\n                break;\n            case NativeMethodsMouse.WM_RBUTTONDBLCLK:\n                Event = MouseEventType.RBUTTONDBLCLK;\n                Button = MouseButton.RButton;\n                Action = MouseAction.DblClk;\n                break;\n            case NativeMethodsMouse.WM_MBUTTONDOWN:\n                Event = MouseEventType.MBUTTONDOWN;\n                Button = MouseButton.MButton;\n                Action = MouseAction.Down;\n                break;\n            case NativeMethodsMouse.WM_MBUTTONUP:\n                Event = MouseEventType.MBUTTONUP;\n                Button = MouseButton.MButton;\n                Action = MouseAction.Up;\n                break;\n            case NativeMethodsMouse.WM_MBUTTONDBLCLK:\n                Event = MouseEventType.MBUTTONDBLCLK;\n                Button = MouseButton.MButton;\n                Action = MouseAction.DblClk;\n                break;\n            case NativeMethodsMouse.WM_XBUTTONDOWN:\n                Event = MouseEventType.XBUTTONDOWN;\n                Button = MouseButton.XButton;\n                Action = MouseAction.Down;\n                break;\n            case NativeMethodsMouse.WM_XBUTTONUP:\n                Event = MouseEventType.XBUTTONUP;\n                Button = MouseButton.XButton;\n                Action = MouseAction.Up;\n                break;\n            case NativeMethodsMouse.WM_XBUTTONDBLCLK:\n                Event = MouseEventType.XBUTTONDBLCLK;\n                Button = MouseButton.XButton;\n                Action = MouseAction.DblClk;\n                break;\n            case NativeMethodsMouse.WM_MOUSEWHEEL1:\n                Event = MouseEventType.MOUSEWHEEL1;\n                Button = MouseButton.None;\n                Action = MouseAction.Wheel;\n                    \n                unchecked {\n                    wheelDelta = BitConverter.ToInt16(BitConverter.GetBytes(Msllhookstruct.mouseData), 2);\n                }\n                break;\n            case NativeMethodsMouse.WM_MOUSEHWHEEL2:\n                Event = MouseEventType.MOUSEHWHEEL2;\n                Button = MouseButton.None;\n                Action = MouseAction.Wheel;\n                unchecked {\n                    wheelDelta = BitConverter.ToInt16(BitConverter.GetBytes(Msllhookstruct.mouseData), 2);\n                }\n                break;\n            default:\n                Log.e(\"ME\", \"Unknown Mouse Event: \" + wParam.ToString());\n                break;\n            }\n        }\n    }\n\n    public delegate void MouseRawEventHandler(MouseRawEventArgs raw_e);\n    public delegate void CursorEventHandler(bool visible);\n\n    public interface IMouseRawEventProvider : IDisposable\n    {\n        event MouseRawEventHandler MouseEvent;\n        event CursorEventHandler CursorEvent;\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsDC.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features?WT.mc_id=DT-MVP-5003235#layered-windows\n    // https://docs.microsoft.com/en-us/windows/win32/gdi/about-device-contexts\n\n    // GDI: Graphics Device Interface\n    //      = Some API to paint on screens, printers, ...\n\n    // DC: DeviceContext\n    //     - https://docs.microsoft.com/de-de/windows/win32/gdi/about-bitmaps\n    //     - Contains a set of graphic objects:\n    //        - Bitmaps or Paths or Rectangles or Polygons or Text\n    //          with associated Fonts, Brushes, Pens, Color Palettes (logical palettes)\n    //        - Each graphic object is contained at most once, and is otherwise defaulted or null\n    //        - SelectObject replaces the object of the selected type in the DC\n    //              https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject\n    //              - This function returns the previously selected object of the specified type.\n    //                An application should always replace a new object with the original, \n    //                default object after it has finished drawing with the new object.\n    //              - An application cannot select a single bitmap into more than one DC at a time.\n\n\n\n    // GDI Bitmaps need to be created from .NET Bitmaps.. -> todo: cache or measure\n    //      https://docs.microsoft.com/de-de/dotnet/api/system.drawing.bitmap.gethbitmap?view=netframework-4.8\n    //      GDI Bitmaps consist of: \n    //          A header that describes the resolution of the device on which the rectangle of pixels was created, the dimensions of the rectangle, the size of the array of bits, and so on.\n    //          A logical palette.\n    //          An array of bits that defines the relationship between pixels in the bitmapped image and entries in the logical palette.\n\n\n    // https://docs.microsoft.com/de-de/windows/win32/gdi/bitmaps?redirectedfrom=MSDN\n\n    public class NativeMethodsDC\n    {\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"windowHandle\">Window Handle</param>\n        /// <param name=\"pos\">Position of window</param>\n        /// <param name=\"bitmap\">Bitmap to set</param>\n        /// <param name=\"opacity\">Better performance with 1.0</param>\n        public static void SetBitmapForWindow(IntPtr windowHandle, Point pos, Bitmap bitmap, float opacity)\n        {\n            // Does this bitmap contain an alpha channel?\n            if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)\n            {\n                throw new ApplicationException(\"The bitmap must be 32bpp with alpha-channel.\");\n            }\n\n            // Get device contexts\n            IntPtr screenDc = GetDC(IntPtr.Zero);\n            IntPtr memDc = CreateCompatibleDC(screenDc);\n            IntPtr hBitmap = IntPtr.Zero;\n            IntPtr hOldBitmap = IntPtr.Zero;\n\n            opacity = Math.Min(opacity, 1.0f);\n            opacity = Math.Max(opacity, 0.0f);\n\n            try\n            {\n                // Get handle to the new bitmap and select it into the current \n                // device context. (keep the old bitmap because the documentation\n                // wants us to restore the Bitmap after drawing)\n                hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));\n                hOldBitmap = SelectObject(memDc, hBitmap);\n\n                // Set parameters for layered window update.\n                SIZE newSize = new SIZE(bitmap.Width, bitmap.Height);\n                POINT sourceLocation = new POINT(0, 0);\n                POINT newLocation = new POINT(pos.X, pos.Y); // new Point(this.Left, this.Top)\n                BLENDFUNCTION blend = new BLENDFUNCTION();\n                blend.BlendOp = AC_SRC_OVER;\n                blend.BlendFlags = 0;\n                blend.SourceConstantAlpha = (byte) (int) (opacity * 255);\n                blend.AlphaFormat = AC_SRC_ALPHA;\n\n                Log.e(\"LAYWIN\", \"Draw to \" + pos.ToString() + \" and set size to \" + bitmap.Width.ToString() + \" \" + bitmap.Height.ToString());\n\n                // Update the window.\n                UpdateLayeredWindow(\n                    windowHandle,    // Handle to the layered window\n                    screenDc,        // Handle to the screen DC\n                    ref newLocation, // New screen position of the layered window\n                    ref newSize,     // New size of the layered window\n                    memDc,           // Handle to the layered window surface DC\n                    ref sourceLocation, // Location of the layer in the DC\n                    0,               // Color key of the layered window\n                    ref blend,       // Transparency of the layered window\n                    ULW_ALPHA        // Use blend as the blend function\n                    );\n            }\n            finally\n            {\n                // Release device context.\n                ReleaseDC(IntPtr.Zero, screenDc);\n                if (hBitmap != IntPtr.Zero)\n                {\n                    SelectObject(memDc, hOldBitmap);\n                    DeleteObject(hBitmap);\n                }\n                DeleteDC(memDc);\n            }\n        }\n\n\n        // Source: MSDN Code Gallery\n        // https://code.msdn.microsoft.com/windowsapps/CSWinFormLayeredWindow-23cdc375\n        // https://stackoverflow.com/questions/33530623/c-sharp-windows-form-transparent-background-image\n\n        const Int32 WS_EX_LAYERED = 0x80000;\n        const Int32 HTCAPTION = 0x02;\n        const Int32 WM_NCHITTEST = 0x84;\n        const Int32 ULW_ALPHA = 0x02;\n        const byte AC_SRC_OVER = 0x00;\n        const byte AC_SRC_ALPHA = 0x01;\n\n        [StructLayout(LayoutKind.Sequential)]\n        struct POINT\n        {\n            public Int32 x;\n            public Int32 y;\n\n            public POINT(Int32 x, Int32 y)\n            { this.x = x; this.y = y; }\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        struct SIZE\n        {\n            public Int32 cx;\n            public Int32 cy;\n\n            public SIZE(Int32 cx, Int32 cy)\n            { this.cx = cx; this.cy = cy; }\n        }\n\n        [StructLayout(LayoutKind.Sequential, Pack = 1)]\n        struct ARGB\n        {\n            public byte Blue;\n            public byte Green;\n            public byte Red;\n            public byte Alpha;\n        }\n\n        [StructLayout(LayoutKind.Sequential, Pack = 1)]\n        struct BLENDFUNCTION\n        {\n            public byte BlendOp;\n            public byte BlendFlags;\n            public byte SourceConstantAlpha;\n            public byte AlphaFormat;\n        }\n\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"hwnd\">A handle to a layered window. A layered window is created by \n        /// specifying WS_EX_LAYERED when creating the window with the CreateWindowEx \n        /// function.</param>\n        /// <param name=\"hdcDst\">A handle to a DC for the screen. This handle is obtained by \n        /// specifying NULL when calling the function. It is used for palette color matching \n        /// when the window contents are updated. If hdcDst isNULL, the default palette will \n        /// be used.\n        ///\n        /// If hdcSrc is NULL, hdcDst must be NULL.</param>\n        /// <param name=\"pptDst\">A pointer to a structure that specifies the new screen \n        /// position of the layered window. If the current position is not changing,\n        /// pptDst can be NULL.</param>\n        /// <param name=\"psize\">A pointer to a structure that specifies the new size of \n        /// the layered window. If the size of the window is not changing, psize can be \n        /// NULL. If hdcSrc is NULL, psize must be NULL.</param>\n        /// <param name=\"hdcSrc\">A handle to a DC for the surface that defines the layered\n        /// window. This handle can be obtained by calling the CreateCompatibleDC function.\n        /// If the shape and visual context of the window are not changing, hdcSrc can be\n        /// NULL.</param>\n        /// <param name=\"pptSrc\">A pointer to a structure that specifies the location of \n        /// the layer in the device context. If hdcSrc is NULL, pptSrc should be \n        /// NULL.</param>\n        /// <param name=\"crKey\">A structure that specifies the color key to be used when\n        /// composing the layered window.</param>\n        /// <param name=\"pblend\">A pointer to a structure that specifies the transparency\n        /// value to be used when composing the layered window.</param>\n        /// <param name=\"dwFlags\">This parameter can be one of the following values.\n        /// - ULW_ALPHA: Use pblend as the blend function. If the display mode is 256 \n        ///              colors or less, the effect of this value is the same as the\n        ///              effect of ULW_OPAQUE.\n        /// - ULW_COLORKEY:\tUse crKey as the transparency color.\n        /// - ULW_OPAQUE:  Draw an opaque layered window.\n        /// - ULW_EX_NORESIZE: Force the UpdateLayeredWindowIndirect function to fail\n        ///                    if the current window size does not match the size \n        ///                    in the psize.\n        /// If hdcSrc is NULL, dwFlags should be zero.\n        /// </param>\n        /// <returns>\n        /// If the function succeeds, the return value is nonzero.\n        ///\n        /// If the function fails, the return value is zero.To get extended error \n        /// information, call GetLastError.\n        /// </returns>\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,\n            ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pptSrc,\n            Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);\n\n        [DllImport(\"gdi32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        static extern IntPtr CreateCompatibleDC(IntPtr hDC);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        static extern IntPtr GetDC(IntPtr hWnd);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);\n\n        [DllImport(\"gdi32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        static extern bool DeleteDC(IntPtr hdc);\n\n        [DllImport(\"gdi32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);\n\n        [DllImport(\"gdi32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        static extern bool DeleteObject(IntPtr hObject);\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsEvents.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace KeyNStroke\n{\n    public class NativeMethodsEvents\n    {\n        [DllImport(\"user32.dll\")]\n        static public extern IntPtr SetWinEventHook(WinEvents eventMin, WinEvents eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, WinEventFlags dwFlags);\n\n        [DllImport(\"user32.dll\")]\n        static public extern bool UnhookWinEvent(IntPtr hWinEventHook);\n\n        public delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvents eventType, IntPtr hwnd, uint idObject, uint idChild, uint dwEventThread, uint dwmsEventTime);\n\n        public enum WinEventFlags : uint\n        {\n            WINEVENT_OUTOFCONTEXT = 0x0000, // Events are ASYNC\n            WINEVENT_SKIPOWNTHREAD = 0x0001, // Don't call back for events on installer's thread\n            WINEVENT_SKIPOWNPROCESS = 0x0002, // Don't call back for events on installer's process\n            WINEVENT_INCONTEXT = 0x0004, // Events are SYNC, this causes your dll to be injected into every process\n        }\n\n        public enum WinEvents : uint\n        {\n            /** The range of WinEvent constant values specified by the Accessibility Interoperability Alliance (AIA) for use across the industry.\n              * For more information, see Allocation of WinEvent IDs. */\n            EVENT_AIA_START = 0xA000,\n            EVENT_AIA_END = 0xAFFF,\n\n            /** The lowest and highest possible event values. */\n            EVENT_MIN = 0x00000001,\n            EVENT_MAX = 0x7FFFFFFF,\n\n            /** An object's KeyboardShortcut property has changed. Server applications send this event for their accessible objects. */\n            EVENT_OBJECT_ACCELERATORCHANGE = 0x8012,\n\n            /** Sent when a window is cloaked. A cloaked window still exists, but is invisible to the user. */\n            EVENT_OBJECT_CLOAKED = 0x8017,\n\n            /** A window object's scrolling has ended. Unlike EVENT_SYSTEM_SCROLLEND, this event is associated with the scrolling window.\n              * Whether the scrolling is horizontal or vertical scrolling, this event should be sent whenever the scroll action is completed. * The hwnd parameter of the WinEventProc callback function describes the scrolling window; the idObject parameter is OBJID_CLIENT, * and the idChild parameter is CHILDID_SELF. */\n            EVENT_OBJECT_CONTENTSCROLLED = 0x8015,\n\n            /** An object has been created. The system sends this event for the following user interface elements: caret, header control,\n              * list-view control, tab control, toolbar control, tree view control, and window object. Server applications send this event * for their accessible objects. * Before sending the event for the parent object, servers must send it for all of an object's child objects. * Servers must ensure that all child objects are fully created and ready to accept IAccessible calls from clients before * the parent object sends this event. * Because a parent object is created after its child objects, clients must make sure that an object's parent has been created * before calling IAccessible::get_accParent, particularly if in-context hook functions are used. */\n            EVENT_OBJECT_CREATE = 0x8000,\n\n            /** An object's DefaultAction property has changed. The system sends this event for dialog boxes. Server applications send\n              * this event for their accessible objects. */\n            EVENT_OBJECT_DEFACTIONCHANGE = 0x8011,\n\n            /** An object's Description property has changed. Server applications send this event for their accessible objects. */\n            EVENT_OBJECT_DESCRIPTIONCHANGE = 0x800D,\n\n            /** An object has been destroyed. The system sends this event for the following user interface elements: caret, header control,\n              * list-view control, tab control, toolbar control, tree view control, and window object. Server applications send this event for * their accessible objects. * Clients assume that all of an object's children are destroyed when the parent object sends this event. * After receiving this event, clients do not call an object's IAccessible properties or methods. However, the interface pointer * must remain valid as long as there is a reference count on it (due to COM rules), but the UI element may no longer be present. * Further calls on the interface pointer may return failure errors; to prevent this, servers create proxy objects and monitor * their life spans. */\n            EVENT_OBJECT_DESTROY = 0x8001,\n\n            /** The user started to drag an element. The hwnd, idObject, and idChild parameters of the WinEventProc callback function\n              * identify the object being dragged. */\n            EVENT_OBJECT_DRAGSTART = 0x8021,\n\n            /** The user has ended a drag operation before dropping the dragged element on a drop target. The hwnd, idObject, and idChild\n              * parameters of the WinEventProc callback function identify the object being dragged. */\n            EVENT_OBJECT_DRAGCANCEL = 0x8022,\n\n            /** The user dropped an element on a drop target. The hwnd, idObject, and idChild parameters of the WinEventProc callback\n              * function identify the object being dragged. */\n            EVENT_OBJECT_DRAGCOMPLETE = 0x8023,\n\n            /** The user dragged an element into a drop target's boundary. The hwnd, idObject, and idChild parameters of the WinEventProc\n              * callback function identify the drop target. */\n            EVENT_OBJECT_DRAGENTER = 0x8024,\n\n            /** The user dragged an element out of a drop target's boundary. The hwnd, idObject, and idChild parameters of the WinEventProc\n              * callback function identify the drop target. */\n            EVENT_OBJECT_DRAGLEAVE = 0x8025,\n\n            /** The user dropped an element on a drop target. The hwnd, idObject, and idChild parameters of the WinEventProc callback\n              * function identify the drop target. */\n            EVENT_OBJECT_DRAGDROPPED = 0x8026,\n\n            /** The highest object event value. */\n            EVENT_OBJECT_END = 0x80FF,\n\n            /** An object has received the keyboard focus. The system sends this event for the following user interface elements:\n              * list-view control, menu bar, pop-up menu, switch window, tab control, tree view control, and window object. * Server applications send this event for their accessible objects. * The hwnd parameter of the WinEventProc callback function identifies the window that receives the keyboard focus. */\n            EVENT_OBJECT_FOCUS = 0x8005,\n\n            /** An object's Help property has changed. Server applications send this event for their accessible objects. */\n            EVENT_OBJECT_HELPCHANGE = 0x8010,\n\n            /** An object is hidden. The system sends this event for the following user interface elements: caret and cursor.\n              * Server applications send this event for their accessible objects. * When this event is generated for a parent object, all child objects are already hidden. * Server applications do not send this event for the child objects. * Hidden objects include the STATE_SYSTEM_INVISIBLE flag; shown objects do not include this flag. The EVENT_OBJECT_HIDE event * also indicates that the STATE_SYSTEM_INVISIBLE flag is set. Therefore, servers do not send the EVENT_STATE_CHANGE event in * this case. */\n            EVENT_OBJECT_HIDE = 0x8003,\n\n            /** A window that hosts other accessible objects has changed the hosted objects. A client might need to query the host\n              * window to discover the new hosted objects, especially if the client has been monitoring events from the window. * A hosted object is an object from an accessibility framework (MSAA or UI Automation) that is different from that of the host. * Changes in hosted objects that are from the same framework as the host should be handed with the structural change events, * such as EVENT_OBJECT_CREATE for MSAA. For more info see comments within winuser.h. */\n            EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED = 0x8020,\n\n            /** An IME window has become hidden. */\n            EVENT_OBJECT_IME_HIDE = 0x8028,\n\n            /** An IME window has become visible. */\n            EVENT_OBJECT_IME_SHOW = 0x8027,\n\n            /** The size or position of an IME window has changed. */\n            EVENT_OBJECT_IME_CHANGE = 0x8029,\n\n            /** An object has been invoked; for example, the user has clicked a button. This event is supported by common controls and is\n              * used by UI Automation. * For this event, the hwnd, ID, and idChild parameters of the WinEventProc callback function identify the item that is invoked. */\n            EVENT_OBJECT_INVOKED = 0x8013,\n\n            /** An object that is part of a live region has changed. A live region is an area of an application that changes frequently\n              * and/or asynchronously. */\n            EVENT_OBJECT_LIVEREGIONCHANGED = 0x8019,\n\n            /** An object has changed location, shape, or size. The system sends this event for the following user interface elements:\n              * caret and window objects. Server applications send this event for their accessible objects. * This event is generated in response to a change in the top-level object within the object hierarchy; it is not generated for any * children that the object might have. For example, if the user resizes a window, the system sends this notification for the window, * but not for the menu bar, title bar, scroll bar, or other objects that have also changed. * The system does not send this event for every non-floating child window when the parent moves. However, if an application explicitly * resizes child windows as a result of resizing the parent window, the system sends multiple events for the resized children. * If an object's State property is set to STATE_SYSTEM_FLOATING, the server sends EVENT_OBJECT_LOCATIONCHANGE whenever the object changes * location. If an object does not have this state, servers only trigger this event when the object moves in relation to its parent. * For this event notification, the idChild parameter of the WinEventProc callback function identifies the child object that has changed. */\n            EVENT_OBJECT_LOCATIONCHANGE = 0x800B,\n\n            /** An object's Name property has changed. The system sends this event for the following user interface elements: check box,\n              * cursor, list-view control, push button, radio button, status bar control, tree view control, and window object. Server * * applications send this event for their accessible objects. */\n            EVENT_OBJECT_NAMECHANGE = 0x800C,\n\n            /** An object has a new parent object. Server applications send this event for their accessible objects. */\n            EVENT_OBJECT_PARENTCHANGE = 0x800F,\n\n            /** A container object has added, removed, or reordered its children. The system sends this event for the following user\n              * interface elements: header control, list-view control, toolbar control, and window object. Server applications send this * event as appropriate for their accessible objects. * For example, this event is generated by a list-view object when the number of child elements or the order of the elements changes. * This event is also sent by a parent window when the Z-order for the child windows changes. */\n            EVENT_OBJECT_REORDER = 0x8004,\n\n            /** The selection within a container object has changed. The system sends this event for the following user interface elements:\n              * list-view control, tab control, tree view control, and window object. Server applications send this event for their accessible * objects. * This event signals a single selection: either a child is selected in a container that previously did not contain any selected children, * or the selection has changed from one child to another. * The hwnd and idObject parameters of the WinEventProc callback function describe the container; the idChild parameter identifies the object * that is selected. If the selected child is a window that also contains objects, the idChild parameter is OBJID_WINDOW. */\n            EVENT_OBJECT_SELECTION = 0x8006,\n\n            /** A child within a container object has been added to an existing selection. The system sends this event for the following user\n              * interface elements: list box, list-view control, and tree view control. Server applications send this event for their accessible * objects. * The hwnd and idObject parameters of the WinEventProc callback function describe the container. The idChild parameter is the child that * is added to the selection. */\n            EVENT_OBJECT_SELECTIONADD = 0x8007,\n\n            /** An item within a container object has been removed from the selection. The system sends this event for the following user\n              * interface elements: list box, list-view control, and tree view control. Server applications send this event for their accessible * objects. * This event signals that a child is removed from an existing selection. * The hwnd and idObject parameters of the WinEventProc callback function describe the container; the idChild parameter identifies * the child that has been removed from the selection. */\n            EVENT_OBJECT_SELECTIONREMOVE = 0x8008,\n\n            /** Numerous selection changes have occurred within a container object. The system sends this event for list boxes; server\n              * applications send it for their accessible objects. * This event is sent when the selected items within a control have changed substantially. The event informs the client * that many selection changes have occurred, and it is sent instead of several * EVENT_OBJECT_SELECTIONADD or EVENT_OBJECT_SELECTIONREMOVE events. The client * queries for the selected items by calling the container object's IAccessible::get_accSelection method and * enumerating the selected items. For this event notification, the hwnd and idObject parameters of the WinEventProc callback * function describe the container in which the changes occurred. */\n            EVENT_OBJECT_SELECTIONWITHIN = 0x8009,\n\n            /** A hidden object is shown. The system sends this event for the following user interface elements: caret, cursor, and window\n              * object. Server applications send this event for their accessible objects. * Clients assume that when this event is sent by a parent object, all child objects are already displayed. * Therefore, server applications do not send this event for the child objects. * Hidden objects include the STATE_SYSTEM_INVISIBLE flag; shown objects do not include this flag. * The EVENT_OBJECT_SHOW event also indicates that the STATE_SYSTEM_INVISIBLE flag is cleared. Therefore, servers * do not send the EVENT_STATE_CHANGE event in this case. */\n            EVENT_OBJECT_SHOW = 0x8002,\n\n            /** An object's state has changed. The system sends this event for the following user interface elements: check box, combo box,\n              * header control, push button, radio button, scroll bar, toolbar control, tree view control, up-down control, and window object. * Server applications send this event for their accessible objects. * For example, a state change occurs when a button object is clicked or released, or when an object is enabled or disabled. * For this event notification, the idChild parameter of the WinEventProc callback function identifies the child object whose state has changed. */\n            EVENT_OBJECT_STATECHANGE = 0x800A,\n\n            /** The conversion target within an IME composition has changed. The conversion target is the subset of the IME composition\n              * which is actively selected as the target for user-initiated conversions. */\n            EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED = 0x8030,\n\n            /** An object's text selection has changed. This event is supported by common controls and is used by UI Automation.\n              * The hwnd, ID, and idChild parameters of the WinEventProc callback function describe the item that is contained in the updated text selection. */\n            EVENT_OBJECT_TEXTSELECTIONCHANGED = 0x8014,\n\n            /** Sent when a window is uncloaked. A cloaked window still exists, but is invisible to the user. */\n            EVENT_OBJECT_UNCLOAKED = 0x8018,\n\n            /** An object's Value property has changed. The system sends this event for the user interface elements that include the scroll\n              * bar and the following controls: edit, header, hot key, progress bar, slider, and up-down. Server applications send this event * for their accessible objects. */\n            EVENT_OBJECT_VALUECHANGE = 0x800E,\n\n            /** The range of event constant values reserved for OEMs. For more information, see Allocation of WinEvent IDs. */\n            EVENT_OEM_DEFINED_START = 0x0101,\n            EVENT_OEM_DEFINED_END = 0x01FF,\n\n            /** An alert has been generated. Server applications should not send this event. */\n            EVENT_SYSTEM_ALERT = 0x0002,\n\n            /** A preview rectangle is being displayed. */\n            EVENT_SYSTEM_ARRANGMENTPREVIEW = 0x8016,\n\n            /** A window has lost mouse capture. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_CAPTUREEND = 0x0009,\n\n            /** A window has received mouse capture. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_CAPTURESTART = 0x0008,\n\n            /** A window has exited context-sensitive Help mode. This event is not sent consistently by the system. */\n            EVENT_SYSTEM_CONTEXTHELPEND = 0x000D,\n\n            /** A window has entered context-sensitive Help mode. This event is not sent consistently by the system. */\n            EVENT_SYSTEM_CONTEXTHELPSTART = 0x000C,\n\n            /** The active desktop has been switched. */\n            EVENT_SYSTEM_DESKTOPSWITCH = 0x0020,\n\n            /** A dialog box has been closed. The system sends this event for standard dialog boxes; servers send it for custom dialog boxes.\n              * This event is not sent consistently by the system. */\n            EVENT_SYSTEM_DIALOGEND = 0x0011,\n\n            /** A dialog box has been displayed. The system sends this event for standard dialog boxes, which are created using resource\n              * templates or Win32 dialog box functions. Servers send this event for custom dialog boxes, which are windows that function as * dialog boxes but are not created in the standard way. * This event is not sent consistently by the system. */\n            EVENT_SYSTEM_DIALOGSTART = 0x0010,\n\n            /** An application is about to exit drag-and-drop mode. Applications that support drag-and-drop operations must send this event;\n              * the system does not send this event. */\n            EVENT_SYSTEM_DRAGDROPEND = 0x000F,\n\n            /** An application is about to enter drag-and-drop mode. Applications that support drag-and-drop operations must send this\n              * event because the system does not send it. */\n            EVENT_SYSTEM_DRAGDROPSTART = 0x000E,\n\n            /** The highest system event value. */\n            EVENT_SYSTEM_END = 0x00FF,\n\n            /** The foreground window has changed. The system sends this event even if the foreground window has changed to another window\n              * in the same thread. Server applications never send this event. * For this event, the WinEventProc callback function's hwnd parameter is the handle to the window that is in the * foreground, the idObject parameter is OBJID_WINDOW, and the idChild parameter is CHILDID_SELF. */\n            EVENT_SYSTEM_FOREGROUND = 0x0003,\n\n            /** A pop-up menu has been closed. The system sends this event for standard menus; servers send it for custom menus.\n              * When a pop-up menu is closed, the client receives this message, and then the EVENT_SYSTEM_MENUEND event. * This event is not sent consistently by the system. */\n            EVENT_SYSTEM_MENUPOPUPEND = 0x0007,\n\n            /** A pop-up menu has been displayed. The system sends this event for standard menus, which are identified by HMENU, and are\n              * created using menu-template resources or Win32 menu functions. Servers send this event for custom menus, which are user * interface elements that function as menus but are not created in the standard way. This event is not sent consistently by the system. */\n            EVENT_SYSTEM_MENUPOPUPSTART = 0x0006,\n\n            /** A menu from the menu bar has been closed. The system sends this event for standard menus; servers send it for custom menus.\n              * For this event, the WinEventProc callback function's hwnd, idObject, and idChild parameters refer to the control * that contains the menu bar or the control that activates the context menu. The hwnd parameter is the handle to the window * that is related to the event. The idObject parameter is OBJID_MENU or OBJID_SYSMENU for a menu, or OBJID_WINDOW for a * pop-up menu. The idChild parameter is CHILDID_SELF. */\n            EVENT_SYSTEM_MENUEND = 0x0005,\n\n            /** A menu item on the menu bar has been selected. The system sends this event for standard menus, which are identified\n              * by HMENU, created using menu-template resources or Win32 menu API elements. Servers send this event for custom menus, * which are user interface elements that function as menus but are not created in the standard way. * For this event, the WinEventProc callback function's hwnd, idObject, and idChild parameters refer to the control * that contains the menu bar or the control that activates the context menu. The hwnd parameter is the handle to the window * related to the event. The idObject parameter is OBJID_MENU or OBJID_SYSMENU for a menu, or OBJID_WINDOW for a pop-up menu. * The idChild parameter is CHILDID_SELF.The system triggers more than one EVENT_SYSTEM_MENUSTART event that does not always * correspond with the EVENT_SYSTEM_MENUEND event. */\n            EVENT_SYSTEM_MENUSTART = 0x0004,\n\n            /** A window object is about to be restored. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_MINIMIZEEND = 0x0017,\n\n            /** A window object is about to be minimized. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_MINIMIZESTART = 0x0016,\n\n            /** The movement or resizing of a window has finished. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_MOVESIZEEND = 0x000B,\n\n            /** A window is being moved or resized. This event is sent by the system, never by servers. */\n            EVENT_SYSTEM_MOVESIZESTART = 0x000A,\n\n            /** Scrolling has ended on a scroll bar. This event is sent by the system for standard scroll bar controls and for\n              * scroll bars that are attached to a window. Servers send this event for custom scroll bars, which are user interface * elements that function as scroll bars but are not created in the standard way. * The idObject parameter that is sent to the WinEventProc callback function is OBJID_HSCROLL for horizontal scroll bars, and * OBJID_VSCROLL for vertical scroll bars. */\n            EVENT_SYSTEM_SCROLLINGEND = 0x0013,\n\n            /** Scrolling has started on a scroll bar. The system sends this event for standard scroll bar controls and for scroll\n              * bars attached to a window. Servers send this event for custom scroll bars, which are user interface elements that * function as scroll bars but are not created in the standard way. * The idObject parameter that is sent to the WinEventProc callback function is OBJID_HSCROLL for horizontal scrolls bars, * and OBJID_VSCROLL for vertical scroll bars. */\n            EVENT_SYSTEM_SCROLLINGSTART = 0x0012,\n\n            /** A sound has been played. The system sends this event when a system sound, such as one for a menu,\n              * is played even if no sound is audible (for example, due to the lack of a sound file or a sound card). * Servers send this event whenever a custom UI element generates a sound. * For this event, the WinEventProc callback function receives the OBJID_SOUND value as the idObject parameter. */\n            EVENT_SYSTEM_SOUND = 0x0001,\n\n            /** The user has released ALT+TAB. This event is sent by the system, never by servers.\n              * The hwnd parameter of the WinEventProc callback function identifies the window to which the user has switched. * If only one application is running when the user presses ALT+TAB, the system sends this event without a corresponding * EVENT_SYSTEM_SWITCHSTART event. */\n            EVENT_SYSTEM_SWITCHEND = 0x0015,\n\n            /** The user has pressed ALT+TAB, which activates the switch window. This event is sent by the system, never by servers.\n              * The hwnd parameter of the WinEventProc callback function identifies the window to which the user is switching. * If only one application is running when the user presses ALT+TAB, the system sends an EVENT_SYSTEM_SWITCHEND event without a * corresponding EVENT_SYSTEM_SWITCHSTART event. */\n            EVENT_SYSTEM_SWITCHSTART = 0x0014,\n\n            /** The range of event constant values reserved for UI Automation event identifiers. For more information,\n              * see Allocation of WinEvent IDs. */\n            EVENT_UIA_EVENTID_START = 0x4E00,\n            EVENT_UIA_EVENTID_END = 0x4EFF,\n\n            /**\n              * The range of event constant values reserved for UI Automation property-changed event identifiers. * For more information, see Allocation of WinEvent IDs. */\n            EVENT_UIA_PROPID_START = 0x7500,\n            EVENT_UIA_PROPID_END = 0x75FF\n        }\n        public enum ObjIds : uint {\n            OBJID_WINDOW = 0x00000000,\n            OBJID_SYSMENU = 0xFFFFFFFF,\n            OBJID_TITLEBAR = 0xFFFFFFFE,\n            OBJID_MENU = 0xFFFFFFFD,\n            OBJID_CLIENT = 0xFFFFFFFC,\n            OBJID_VSCROLL = 0xFFFFFFFB,\n            OBJID_HSCROLL = 0xFFFFFFFA,\n            OBJID_SIZEGRIP = 0xFFFFFFF9,\n            OBJID_CARET = 0xFFFFFFF8,\n            OBJID_CURSOR = 0xFFFFFFF7,\n            OBJID_ALERT = 0xFFFFFFF6,\n            OBJID_SOUND = 0xFFFFFFF5,\n            OBJID_QUERYCLASSNAMEIDX = 0xFFFFFFF4,\n            OBJID_NATIVEOM = 0xFFFFFFF0\n        }\n\n        public const uint CHILDID_SELF = 0;\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsGWL.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Runtime.InteropServices;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Each Window has some associated memory with holds flags for window behaviour\n    /// The bytes that hold these informations are numbered as nIndex with the values GWL.GWL_****\n    /// These Methods can be used to define a click through behaviour\n    /// </summary>\n    public class NativeMethodsGWL\n    {\n        /// <summary>\n        /// Activate Click Through\n        /// </summary>\n        /// <param name=\"Handle\">The Forms myform.Handle</param>\n        public static void ClickThrough(IntPtr Handle)\n        {\n            uint current_val = GetWindowLong(Handle, GWL.GWL_EXSTYLE);\n            uint changed_val = Convert.ToUInt32(current_val | (uint)WindowStyles.WS_EX_LAYERED | (uint)WindowStyles.WS_EX_TRANSPARENT);\n            SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val);\n        }\n\n        /// <summary>\n        /// Deactivate Click Through\n        /// </summary>\n        /// <param name=\"Handle\">The Forms myform.Handle</param>\n        public static void CatchClicks(IntPtr Handle)\n        {\n            uint current_val = GetWindowLong(Handle, GWL.GWL_EXSTYLE);\n            uint changed_val = Convert.ToUInt32(current_val & ~((uint)WindowStyles.WS_EX_LAYERED)) & ~((uint)WindowStyles.WS_EX_TRANSPARENT);\n            SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val);\n        }\n\n        public static void HideFromAltTab(IntPtr Handle)\n        {\n            int current_val = (int)GetWindowLong(Handle, GWL.GWL_EXSTYLE);\n            uint changed_val = Convert.ToUInt32(current_val | (uint)WindowStyles.WS_EX_TOOLWINDOW);\n            SetWindowLong(Handle, GWL.GWL_EXSTYLE, changed_val);\n        }\n \n\n        /// <summary>\n        /// These are the possible longs which can be get and set via GetWindowLong and ... \n        /// </summary>\n        public enum GWL : int\n        {\n            GWL_WNDPROC = (-4),\n            GWL_HINSTANCE = (-6),\n            GWL_HWNDPARENT = (-8),\n            GWL_STYLE = (-16),\n            GWL_EXSTYLE = (-20),\n            GWL_USERDATA = (-21),\n            GWL_ID = (-12)\n        }\n\n\n        /// <summary>\n        /// These are the Flags for the Extended Style Bytes (nIndex=GWL_EXTSTYLE)\n        /// </summary>\n        [Flags]\n        public enum WindowStyles : uint\n        {\n            //Extended Window Styles\n\n            WS_EX_DLGMODALFRAME = 0x00000001,\n            WS_EX_NOPARENTNOTIFY = 0x00000004,\n            WS_EX_TOPMOST = 0x00000008,\n            WS_EX_ACCEPTFILES = 0x00000010,\n            WS_EX_TRANSPARENT = 0x00000020,\n            WS_EX_LAYERED = 0x00080000,\n\n            WS_EX_TOOLWINDOW = 0x00000080,\n        }\n\n        // Choose 32/64 bit variant of function\n\n        /// <summary>\n        /// returns long bytes at nIndex\n        /// </summary>\n        /// <param name=\"hWnd\"></param>\n        /// <param name=\"nIndex\"></param>\n        /// <returns>the LONG value</returns>\n        public static uint GetWindowLong(IntPtr hWnd, GWL nIndex)\n        {\n            if (IntPtr.Size == 4)\n            {\n                return (uint)GetWindowLong32(hWnd, (int)nIndex);\n            }\n            return (uint)GetWindowLongPtr64(hWnd, (int)nIndex);\n        }\n\n        /// <summary>\n        /// sets the LONG value at nIndex to nwNewLong\n        /// </summary>\n        /// <param name=\"hWnd\"></param>\n        /// <param name=\"nIndex\"></param>\n        /// <param name=\"dwNewLong\"></param>\n        /// <returns>dunno</returns>\n        public static int SetWindowLong(IntPtr hWnd, GWL nIndex, uint dwNewLong)\n        {\n            if (IntPtr.Size == 4)\n            {\n                return SetWindowLong32(hWnd, (int)nIndex, dwNewLong);\n            }\n            return SetWindowLongPtr64(hWnd, (int)nIndex, dwNewLong);\n        }\n\n        // Different Functions for Win32 and 64 bit\n\n        [DllImport(\"user32.dll\", EntryPoint = \"GetWindowLong\", CharSet = CharSet.Auto)]\n        private static extern int GetWindowLong32(IntPtr hWnd, int nIndex);\n\n        [DllImport(\"user32.dll\", EntryPoint = \"GetWindowLongPtr\", CharSet = CharSet.Auto)]\n        private static extern int GetWindowLongPtr64(IntPtr hWnd, int nIndex);\n\n        [DllImport(\"user32.dll\", EntryPoint = \"SetWindowLong\", CharSet = CharSet.Auto)]\n        private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, uint dwNewLong);\n\n        [DllImport(\"user32.dll\", EntryPoint = \"SetWindowLongPtr\", CharSet = CharSet.Auto)]\n        private static extern int SetWindowLongPtr64(IntPtr hWnd, int nIndex, uint dwNewLong);\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsKeyboard.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace KeyNStroke\n{\n    [ComVisibleAttribute(false),\n    System.Security.SuppressUnmanagedCodeSecurity()]\n    public class NativeMethodsKeyboard\n    {\n        // Structure returned by the hook whenever a key is pressed\n        [StructLayout(LayoutKind.Sequential)]\n        public struct KBDLLHOOKSTRUCT\n        {\n            public int vkCode;\n            public int scanCode;\n            public int flags;\n            public int time;\n            public int dwExtraInfo;\n        }\n\n        public delegate IntPtr HookHandlerDelegate(int nCode, \n                                                     IntPtr wParam, \n                                                     ref KBDLLHOOKSTRUCT lParam);\n\n\n        [DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr GetModuleHandle(string lpModuleName);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr SetWindowsHookEx(int idHook,\n            HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        public static extern bool UnhookWindowsHookEx(IntPtr hhk);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,\n            IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]\n        public static extern UInt16 GetKeyState(int keyCode);\n\n\n        [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Unicode)]\n        public static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nSize);\n\n        [DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Auto)]\n        public static extern uint MapVirtualKey(uint uCode, uint uMapType);\n        public const uint MAPVK_VK_TO_CHAR = 2;\n\n        /// <summary>\n        /// The ToAscii function translates the specified virtual-key code and keyboard \n        /// state to the corresponding character or characters. The function translates the code \n        /// using the input language and physical keyboard layout identified by the keyboard layout handle.\n        /// </summary>\n        /// <param name=\"uVirtKey\">\n        /// [in] Specifies the virtual-key code to be translated. \n        /// </param>\n        /// <param name=\"uScanCode\">\n        /// [in] Specifies the hardware scan code of the key to be translated. \n        /// The high-order bit of this value is set if the key is up (not pressed). \n        /// </param>\n        /// <param name=\"lpbKeyState\">\n        /// [in] Pointer to a 256-byte array that contains the current keyboard state. \n        /// Each element (byte) in the array contains the state of one key. \n        /// If the high-order bit of a byte is set, the key is down (pressed). \n        /// The low bit, if set, indicates that the key is toggled on. In this function, \n        /// only the toggle bit of the CAPS LOCK key is relevant. The toggle state \n        /// of the NUM LOCK and SCROLL LOCK keys is ignored.\n        /// </param>\n        /// <param name=\"lpwTransKey\">\n        /// [out] Pointer to the buffer that receives the translated character or characters. \n        /// </param>\n        /// <param name=\"fuState\">\n        /// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. \n        /// </param>\n        /// <returns>\n        /// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values. \n        /// Value Meaning \n        /// 0 The specified virtual key has no translation for the current state of the keyboard. \n        /// 1 One character was copied to the buffer. \n        /// 2 Two characters were copied to the buffer. This usually happens when a dead-key character \n        /// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified \n        /// virtual key to form a single character. \n        /// </returns>\n        /// <remarks>\n        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp\n        /// </remarks>\n        [DllImport(\"user32\")]\n        public static extern int ToAscii(\n            int uVirtKey,\n            int uScanCode,\n            byte[] lpbKeyState,\n            byte[] lpwTransKey,\n            int fuState);\n\n    /*            int WINAPI ToAscii(\n  _In_      UINT uVirtKey,\n  _In_      UINT uScanCode,\n  _In_opt_  const BYTE *lpKeyState,\n  _Out_     LPWORD lpChar,\n  _In_      UINT uFlags\n);*/\n        [DllImport(\"user32\", SetLastError = true, CharSet = CharSet.Unicode)]\n        public static extern int ToUnicode(\n            int uVirtKey,\n            int uScanCode,\n            byte[] lpbKeyState,\n            [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff,\n            int cchBuff,\n            uint wFlags);\n        /*\n        int WINAPI ToUnicode(\n  _In_      UINT wVirtKey,\n  _In_      UINT wScanCode,\n  _In_opt_  const BYTE *lpKeyState,\n  _Out_     LPWSTR pwszBuff,\n  _In_      int cchBuff,\n  _In_      UINT wFlags\n);*/\n\n\n        /// <summary>\n        /// The GetKeyboardState function copies the status of the 256 virtual keys to the \n        /// specified buffer. \n        /// </summary>\n        /// <param name=\"pbKeyState\">\n        /// [in] Pointer to a 256-byte array that contains keyboard key states. \n        /// </param>\n        /// <returns>\n        /// If the function succeeds, the return value is nonzero.\n        /// If the function fails, the return value is zero. To get extended error information, call GetLastError. \n        /// </returns>\n        /// <remarks>\n        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp\n        /// </remarks>\n        [DllImport(\"user32\")]\n        public static extern int GetKeyboardState(byte[] pbKeyState);\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsMouse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Runtime.InteropServices;\n\nnamespace KeyNStroke\n{\n    [ComVisibleAttribute(false),\n    System.Security.SuppressUnmanagedCodeSecurity()]\n    public class NativeMethodsMouse\n    {\n\n        public const int WM_MOUSEMOVE = 0x200;\n        public const int WM_LBUTTONDOWN = 0x201;\n        public const int WM_LBUTTONUP = 0x202;\n        public const int WM_LBUTTONDBLCLK = 0x203;\n        public const int WM_RBUTTONDOWN = 0x204;\n        public const int WM_RBUTTONUP = 0x205;\n        public const int WM_RBUTTONDBLCLK = 0x206;\n        public const int WM_MBUTTONDOWN = 0x207;\n        public const int WM_MBUTTONUP = 0x208;\n        public const int WM_MBUTTONDBLCLK = 0x209;\n        public const int WM_MOUSEWHEEL1 = 0x20A;\n        public const int WM_XBUTTONDOWN = 0x20B;\n        public const int WM_XBUTTONUP = 0x20C;\n        public const int WM_XBUTTONDBLCLK = 0x20D;\n        public const int WM_MOUSEHWHEEL2 = 0x20E;\n\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr SetWindowsHookEx(int idHook,\n            HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        public static extern bool UnhookWindowsHookEx(IntPtr hhk);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,\n            UIntPtr wParam, ref MSLLHOOKSTRUCT lParam);\n        \n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\n        public static extern int GetDoubleClickTime();\n\n        public const int WH_MOUSE_LL = 14;\n        public const int HC_ACTION = 0; // The wParam and lParam parameters contain information about a mouse message.\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct POINT\n        {\n            public Int32 X;\n            public Int32 Y;\n\n            public POINT(int x, int y)\n            {\n                this.X = x;\n                this.Y = y;\n            }\n\n            public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }\n\n            public static implicit operator System.Drawing.Point(POINT p)\n            {\n                return new System.Drawing.Point(p.X, p.Y);\n            }\n\n            public static implicit operator POINT(System.Drawing.Point p)\n            {\n                return new POINT(p.X, p.Y);\n            }\n\n            public static implicit operator System.Windows.Point(POINT p)\n            {\n                return new System.Windows.Point(p.X, p.Y);\n            }\n\n            public bool Equals(POINT p2)\n            {\n                return X == p2.X && Y == p2.Y;\n            }\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct MSLLHOOKSTRUCT {\n            public POINT pt;\n            public UInt32 mouseData; // be careful, this must be ints, not uints (was wrong before I changed it...). regards, cmew.\n            public UInt32 flags;\n            public UInt32 time;\n            public UIntPtr dwExtraInfo;\n        }\n\n        public delegate IntPtr HookHandlerDelegate(int nCode,\n                                             UIntPtr wParam,\n                                             ref MSLLHOOKSTRUCT lParam);\n\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern bool GetCursorPos(ref NativeMethodsMouse.POINT lpPoint);\n\n        public static POINT CursorPosition\n        {\n            get\n            {\n                POINT pos = new POINT(0, 0);\n                GetCursorPos(ref pos);\n                return pos;\n            }\n        }\n\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct CURSORINFO {\n            public UInt32 cbSize;\n            public UInt32 flags;\n            public UIntPtr hCursor;\n            public POINT ptScreenPos;\n        }\n        public const UInt32 CURSOR_HIDDEN = 0x00000000;\n        public const UInt32 CURSOR_SHOWING = 0x00000001;\n        public const UInt32 CURSOR_SUPPRESSED = 0x00000002;\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern bool GetCursorInfo(ref CURSORINFO pci);\n\n        public static CURSORINFO GetCursorInfoWrapper()\n        {\n            CURSORINFO info = new CURSORINFO();\n            info.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf<CURSORINFO>();\n            GetCursorInfo(ref info);\n            return info;\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/NativeMethodsWindow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace KeyNStroke\n{\n    public class NativeMethodsWindow\n    {\n        public const UInt32 S_OK = 0x00000000;\n\n        /// <summary>\n        /// Sets the Topmost property for the window Handle\n        /// </summary>\n        /// <param name=\"Handle\">The Forms myform.Handle</param>\n        public static void SetWindowTopMost(IntPtr Handle) {\n            SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);\n        }\n\n        /// <summary>\n        /// Sets the Position property for the window Handle\n        /// </summary>\n        /// <param name=\"Handle\">The Forms myform.Handle</param>\n        public static void SetWindowPosition(IntPtr Handle, int x, int y)\n        {\n            SetWindowPos(Handle, HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);\n        }\n\n        /// <summary>\n        /// Sets the Size property for the window Handle\n        /// </summary>\n        /// <param name=\"Handle\">The Forms myform.Handle</param>\n        public static void SetWindowSize(IntPtr Handle, int width, int height)\n        {\n            SetWindowPos(Handle, HWND_TOPMOST, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\n        }\n\n        public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);\n        public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);\n        public static readonly IntPtr HWND_TOP = new IntPtr(0);\n        public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);\n        public const UInt32 SWP_NOSIZE = 0x0001;\n        public const UInt32 SWP_NOMOVE = 0x0002;\n        public const UInt32 SWP_NOACTIVATE = 0x0010;\n        public const UInt32 SWP_NOOWNERZORDER = 0x0200;\n\n        [DllImport(\"user32.dll\")]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);\n\n        public enum MonitorOptions : uint\n        {\n            MONITOR_DEFAULTTONULL = 0x00000000,\n            MONITOR_DEFAULTTOPRIMARY = 0x00000001,\n            MONITOR_DEFAULTTONEAREST = 0x00000002\n        }\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr MonitorFromPoint(NativeMethodsMouse.POINT pt, MonitorOptions dwFlags);\n\n        public enum DpiType : uint\n        {\n            MDT_EFFECTIVE_DPI = 0,\n            MDT_ANGULAR_DPI = 1,\n            MDT_RAW_DPI = 2,\n            MDT_DEFAULT = 3\n        }\n\n        [DllImport(\"Shcore.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern uint GetDpiForMonitor(IntPtr hmonitor, DpiType dpiType, ref uint dpiX, ref uint dpiY);\n\n        public enum ProcessDpiAwareness : uint\n        {\n            PROCESS_DPI_UNAWARE = 0,\n            PROCESS_SYSTEM_DPI_AWARE = 1,\n            PROCESS_PER_MONITOR_DPI_AWARE = 2\n        };\n\n        [DllImport(\"Shcore.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern uint GetProcessDpiAwareness(IntPtr hprocess, ref ProcessDpiAwareness value);\n\n        /*#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)\n        #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)\n        #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)\n        #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)\n        #define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED    ((DPI_AWARENESS_CONTEXT)-5)*/\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern IntPtr GetThreadDpiAwarenessContext();\n\n        public enum DpiAwareness : uint\n        {\n            DPI_AWARENESS_INVALID,\n            DPI_AWARENESS_UNAWARE,\n            DPI_AWARENESS_SYSTEM_AWARE,\n            DPI_AWARENESS_PER_MONITOR_AWARE\n        };\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern DpiAwareness GetAwarenessFromDpiAwarenessContext(IntPtr hDpiAwarenessContext);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern uint GetDpiFromDpiAwarenessContext(IntPtr hDpiAwarenessContext);\n\n        \n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto)]\n        [return: MarshalAs(UnmanagedType.Bool)]\n        static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);\n\n\n        delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData);\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct Rect\n        {\n            public int left;\n            public int top;\n            public int right;\n            public int bottom;\n        }\n\n        static public List<uint> GetAllUsedDpis()\n        {\n            List<uint> dpis = new List<uint>();\n\n            EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,\n                delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)\n                {\n                    uint adpiX = 0, adpiY = 0;\n                    uint result = GetDpiForMonitor(hMonitor, DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY);\n                    if (result == S_OK)\n                    {\n                        dpis.Add(adpiX);\n                    }\n                    return true; // continue enumeration\n                }, IntPtr.Zero);\n            return dpis;\n        }\n\n        static public void PrintDpiAwarenessInfo()\n        {\n            NativeMethodsMouse.POINT cursorPosition = new NativeMethodsMouse.POINT(0, 0);\n            NativeMethodsMouse.GetCursorPos(ref cursorPosition);\n            IntPtr monitor = MonitorFromPoint(cursorPosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);\n\n            // Angular DPI seems to work best\n            uint edpiX = 0, edpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_EFFECTIVE_DPI, ref edpiX, ref edpiY);\n            uint adpiX = 0, adpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_ANGULAR_DPI, ref adpiX, ref adpiY);\n            uint rdpiX = 0, rdpiY = 0;\n            NativeMethodsWindow.GetDpiForMonitor(monitor, NativeMethodsWindow.DpiType.MDT_RAW_DPI, ref rdpiX, ref rdpiY);\n\n\n            ProcessDpiAwareness dpiawareness = 0;\n            GetProcessDpiAwareness(IntPtr.Zero, ref dpiawareness);\n\n            IntPtr hContext = GetThreadDpiAwarenessContext();\n            DpiAwareness threaddpiawareness = GetAwarenessFromDpiAwarenessContext(hContext);\n            uint threadDpi = GetDpiFromDpiAwarenessContext(hContext);\n            \n            Log.e(\"DPI\", $\"DPI: {edpiX},{edpiY}/{adpiX},{adpiY}/{rdpiX},{rdpiY}, Monitor: {(int)monitor}, Awareness: {dpiawareness}/{threaddpiawareness}/{threadDpi}\");\n            \n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// Allgemeine Informationen über eine Assembly werden über die folgenden\n// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,\n// die einer Assembly zugeordnet sind.\n[assembly: AssemblyTitle(\"Key'n'Stroke\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"Key'n'Stroke\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2021\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly\n// für COM-Komponenten unsichtbar.  Wenn Sie auf einen Typ in dieser Assembly von\n// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf \"True\" festlegen.\n[assembly: ComVisible(false)]\n\n//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie\n//<UICulture>ImCodeVerwendeteKultur</UICulture> in der .csproj-Datei\n//in einer <PropertyGroup> fest.  Wenn Sie in den Quelldateien beispielsweise Deutsch\n//(Deutschland) verwenden, legen Sie <UICulture> auf \\\"de-DE\\\" fest.  Heben Sie dann die Auskommentierung\n//des nachstehenden NeutralResourceLanguage-Attributs auf.  Aktualisieren Sie \"en-US\" in der nachstehenden Zeile,\n//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher\n                                     //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,\n                                     // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)\n    ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs\n                                              //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,\n                                              // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)\n)]\n\n\n// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:\n//\n//      Hauptversion\n//      Nebenversion\n//      Buildnummer\n//      Revision\n//\n// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,\n// übernehmen, indem Sie \"*\" eingeben:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n//[assembly: AssemblyVersion(\"1.2.0.0\")]\n//[assembly: AssemblyFileVersion(\"1.2.0.0\")]\n"
  },
  {
    "path": "KeyNStroke/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     Dieser Code wurde von einem Tool generiert.\n//     Laufzeitversion:4.0.30319.42000\n//\n//     Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn\n//     der Code erneut generiert wird.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace KeyNStroke.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.\n    /// </summary>\n    // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert\n    // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.\n    // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen\n    // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"16.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"KeyNStroke.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle\n        ///   Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "KeyNStroke/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     Dieser Code wurde von einem Tool generiert.\n//     Laufzeitversion:4.0.30319.42000\n//\n//     Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn\n//     der Code erneut generiert wird.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace KeyNStroke.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"16.5.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "KeyNStroke/ReadShortcut.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.ReadShortcut\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"Shortcut Detection\" Height=\"181.516\" Width=\"314.996\" Loaded=\"Window_Loaded\">\n    <Grid Margin=\"0,0,417.333,281.667\">\n        <TextBlock Margin=\"28,24,-287,-72\" TextWrapping=\"Wrap\">\n            Press shortcut for <Run x:Name=\"run_purpose\" />\n        </TextBlock>\n        <Button Content=\"Cancel\" HorizontalAlignment=\"Left\" Height=\"29\" Margin=\"28,106,-287,-135\" VerticalAlignment=\"Top\" Width=\"259\" Click=\"Button_Click\"/>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/ReadShortcut.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für ReadShortcut.xaml\n    /// </summary>\n    public partial class ReadShortcut : Window\n    {\n        IKeystrokeEventProvider k;\n\n        string shortcut;\n        public string Shortcut\n        {\n            get { return shortcut; }\n        }\n\n        public ReadShortcut(IKeystrokeEventProvider k, string purpose)\n        {\n            InitializeComponent();\n            this.k = k;\n            this.run_purpose.Text = purpose;\n        }\n\n        private void Button_Click(object sender, RoutedEventArgs e)\n        {\n            Close();\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            this.k.KeystrokeEvent += k_KeystrokeEvent;\n\n        }\n\n        void k_KeystrokeEvent(KeystrokeEventArgs e)\n        {\n            string idf = e.ShortcutIdentifier();\n            if (idf != null)\n            {\n                shortcut = idf;\n                Close();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/ResizeButton.xaml",
    "content": "﻿<UserControl x:Class=\"KeyNStroke.ResizeButton\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:KeyNStroke\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"38\" d:DesignWidth=\"38\">\n    <Button Content=\"⇲\"\n            Margin=\"0,0,0,0\"\n            FontSize=\"24\"\n            PreviewMouseDown=\"resizeWindowButton_MouseDown\"\n            />\n</UserControl>\n"
  },
  {
    "path": "KeyNStroke/ResizeButton.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing System.Windows.Threading;\n\nnamespace KeyNStroke\n{\n\n\n    /// <summary>\n    /// Interaktionslogik für WindowResizeButton.xaml\n    /// </summary>\n    public partial class ResizeButton : UserControl\n    {\n\n        private Point dragStartCursorPosition;\n        private Point lastTickCursorPosition;\n        private Point currentCursorPosition;\n        private Size dragStartSize;\n        private Vector dragStartWindowSizeReal;\n        private Vector minSize;\n\n        private Window window;\n        private IntPtr windowHandle;\n        private FrameworkElement CurrentParent;\n        private MouseHook mouseHook = null;\n\n        public ResizeButton()\n        {\n            InitializeComponent();\n        }\n\n        private SettingsStore settings;\n\n        public SettingsStore Settings\n        {\n            set { settings = value; }\n            get { return settings; }\n        }\n\n        public ResizeTarget ResizeTarget\n        {\n            get { return (ResizeTarget)this.GetValue(ResizeTargetProperty); }\n            set { this.SetValue(ResizeTargetProperty, value); }\n        }\n        public static readonly DependencyProperty ResizeTargetProperty = DependencyProperty.Register(\n          \"ResizeTarget\", typeof(ResizeTarget), typeof(ResizeButton), new PropertyMetadata(ResizeTarget.Window));\n\n        private void resizeWindowButton_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            if (mouseHook == null)\n            {\n                if (ResizeTarget == ResizeTarget.Window)\n                {\n                    window = Window.GetWindow(this);\n                    windowHandle = new WindowInteropHelper(window).Handle;\n                    dragStartSize = new Size(window.Width, window.Height);\n                    dragStartWindowSizeReal = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.Transform(new Vector(dragStartSize.Width, dragStartSize.Height));\n                    minSize = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.Transform(new Vector(this.Width, this.Height));\n                }\n                else\n                {\n                    CurrentParent = (FrameworkElement) Parent;\n                    dragStartSize = new Size(CurrentParent.Width, CurrentParent.Height);\n                    minSize = new Vector(this.Width, this.Height);\n                }\n                mouseHook = new MouseHook(settings);\n                mouseHook.MouseEvent += MouseHook_MouseEvent;\n\n                lastTickCursorPosition = currentCursorPosition = dragStartCursorPosition = NativeMethodsMouse.CursorPosition;\n            }\n        }\n\n        private void MouseHook_MouseEvent(MouseRawEventArgs raw_e)\n        {\n            if (raw_e.Event == MouseEventType.LBUTTONUP || raw_e.Event == MouseEventType.RBUTTONUP)\n            {\n                mouseHook.MouseEvent -= MouseHook_MouseEvent;\n                mouseHook.Dispose();\n                CurrentParent = null;\n                window = null;\n                mouseHook = null;\n                return;\n            }\n            else\n            {\n                Dispatcher.BeginInvoke((Action) tick,\n                    DispatcherPriority.Normal);\n            }\n            \n        }\n\n        private void tick()\n        {\n            currentCursorPosition = NativeMethodsMouse.CursorPosition;\n\n            if (currentCursorPosition != lastTickCursorPosition)\n            {\n                lastTickCursorPosition = currentCursorPosition;\n            \n                Vector diffReal = new Vector((currentCursorPosition.X - dragStartCursorPosition.X), (currentCursorPosition.Y - dragStartCursorPosition.Y));\n                Vector diffDpiIndependend = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice.Transform(diffReal);\n\n                if (window != null)\n                {\n                    // This renders once after setting Width and another time after setting Height, causing a visual staircase effect while resizing.\n                    //Window.Width = dragStartSize.Width + diffDpiIndependend.X;\n                    //Window.Height = dragStartSize.Height + diffDpiIndependend.Y;\n\n                    // As a fix: Use native SetWindowPos() API for smoother resize. The window will automatically redraw itself.\n                    int width = (int)Math.Max(minSize.X, (dragStartWindowSizeReal.X + diffReal.X));\n                    int height = (int)Math.Max(minSize.Y, (dragStartWindowSizeReal.Y + diffReal.Y));\n                    NativeMethodsWindow.SetWindowSize(windowHandle, width, height);\n\n                    settings.SetWindowSizeWithoutOnSettingChangedEvent(new System.Drawing.Size((int)(dragStartSize.Width + diffDpiIndependend.X), (int)(dragStartSize.Height + diffDpiIndependend.Y)));\n                }\n\n                if (CurrentParent != null)\n                {\n                    CurrentParent.Width = Math.Max(minSize.X, dragStartSize.Width + diffDpiIndependend.X);\n                    CurrentParent.Height = Math.Max(minSize.Y, dragStartSize.Height + diffDpiIndependend.Y);\n\n                    settings.SetPanelSizeWithoutOnSettingChangedEvent(new System.Drawing.Size((int)(dragStartSize.Width + diffDpiIndependend.X), (int)(dragStartSize.Height + diffDpiIndependend.Y)));\n                }\n            }\n        }\n    }\n    public enum ResizeTarget\n    {\n        Window,\n        Parent\n    }\n\n}\n"
  },
  {
    "path": "KeyNStroke/Resources/updateKey.pub.xml",
    "content": "<RSAKeyValue><Modulus>pKQQkG41DRCmvzfZbQUleBei4oBubdxsxyqM9LbKMI42EysS6Cpgsc4M90T9c7+nhLqPmqHTsU0pPJoABJe80NnaPvqUK16fIApn0jPjCYTTdnMUS7+Gzq8rBzwdgv0J6cSc5GMPDqnpKcpn1Jl5sX6RvSTH8r9MBgHAu+jMheU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
  },
  {
    "path": "KeyNStroke/Settings1.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.Settings1\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:uc=\"clr-namespace:KeyNStroke\"\n        xmlns:diag=\"clr-namespace:System.Diagnostics;assembly=WindowsBase\"\n        xmlns:fontcolorpicker=\"clr-namespace:WpfColorFontDialog;assembly=WpfColorFontDialog\"\n        xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n        mc:Ignorable=\"d\"\n        Title=\"Settings\"\n        Height=\"auto\"\n        Width=\"700\"\n        SizeToContent=\"Height\"\n        Closing=\"Window_Closing\">\n    <Window.Resources>\n        <Style TargetType=\"RadioButton\">\n            <Setter Property=\"Margin\"\n                    Value=\"5,5,0,0\" />\n        </Style>\n        <Style TargetType=\"Button\">\n            <Setter Property=\"Margin\"\n                    Value=\"0,5,0,5\" />\n            <Setter Property=\"Height\"\n                    Value=\"23\" />\n        </Style>\n        <Style TargetType=\"CheckBox\">\n            <Setter Property=\"Margin\"\n                    Value=\"5,10,0,0\" />\n        </Style>\n        <Style TargetType=\"uc:LabeledSlider\">\n            <Setter Property=\"Margin\"\n                    Value=\"0,5,0,0\" />\n        </Style>\n        <uc:EnumBooleanConverter x:Key=\"EnumBooleanConverter\" />\n        <uc:FloatPercentageConverter x:Key=\"FloatPercentageConverter\" />\n        <uc:MediaColorDrawingColorConverter x:Key=\"MediaColorDrawingColorConverter\" />\n        <BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\n    </Window.Resources>\n    <Grid Margin=\"5,0\"\n          x:Name=\"layout_root\">\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"auto\" />\n            <RowDefinition Height=\"50\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"100*\" />\n            <ColumnDefinition Width=\"100*\" />\n            <ColumnDefinition Width=\"100*\" />\n            <ColumnDefinition Width=\"100*\" />\n        </Grid.ColumnDefinitions>\n        <TabControl Grid.Row=\"0\"\n                    Grid.Column=\"0\"\n                    Grid.ColumnSpan=\"4\"\n                    Margin=\"0,5,0,0\">\n            <TabItem Header=\"Keystroke History\">\n                <Grid>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                    </Grid.ColumnDefinitions>\n                    <StackPanel Grid.Column=\"0\"\n                                Margin=\"5\">\n                        <CheckBox x:Name=\"cb_enableKeystrokeDisplay\"\n                                  IsChecked=\"{Binding EnableKeystrokeHistory}\">Show Keystroke History</CheckBox>\n                        <StackPanel Margin=\"0,0,0,5\">\n                            <ComboBox x:Name=\"cb_KeystrokeMethod\"\n                                      Margin=\"10\"\n                                      ItemsSource=\"{uc:EnumBindingSourceExtention uc:KeystrokeMethodEnum}\"\n                                      SelectedValue=\"{Binding KeystrokeMethod, Mode=TwoWay}\"\n                                      SelectedValuePath=\"Tag\" />\n                            <Button x:Name=\"button_textfont\"\n                                    Click=\"OnButtonTextFontClick\"\n                                    Grid.ColumnSpan=\"3\"\n                                    Margin=\"10\">Font</Button>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Label VerticalAlignment=\"Center\">Text Color</Label>\n                                <xctk:ColorPicker x:Name=\"colorpicker_text_color\"\n                                                  ColorMode=\"ColorCanvas\"\n                                                  Margin=\"10,0,0,0\"\n                                                  Width=\"80\"\n                                                  SelectedColor=\"{Binding LabelColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                            </StackPanel>\n                            <StackPanel Orientation=\"Horizontal\"\n                                        Margin=\"0,3,0,0\">\n                                <Label VerticalAlignment=\"Center\">Background Color</Label>\n                                <xctk:ColorPicker x:Name=\"colorpicker_background_color\"\n                                                  ColorMode=\"ColorCanvas\"\n                                                  Margin=\"10,0,0,0\"\n                                                  Width=\"80\"\n                                                  SelectedColor=\"{Binding BackgroundColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                            </StackPanel>\n                            <TextBlock TextWrapping=\"Wrap\"\n                                       Margin=\"5,3,0,0\">Set the window opacity via the \"A\" channel of the background color.</TextBlock>\n                            <uc:LabeledSlider x:Name=\"slider_vertdist\"\n                                              Title=\"Line Distance\"\n                                              Minimum=\"10.0\"\n                                              Maximum=\"100.0\"\n                                              Value=\"{Binding LineDistance, Mode=TwoWay}\" />\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Label>No of lines in history</Label>\n                                <xctk:IntegerUpDown x:Name=\"slider_historycount\"\n                                                    Minimum=\"1\"\n                                                    Maximum=\"50\"\n                                                    Increment=\"1\"\n                                                    Value=\"{Binding HistoryLength, Mode=TwoWay}\"\n                                                    Width=\"60\" />\n                            </StackPanel>\n                            <WrapPanel Margin=\"0,0,0,0\">\n                                <CheckBox x:Name=\"cb_enableHistoryTimeout\"\n                                          IsChecked=\"{Binding EnableHistoryTimeout}\">\n                                    <TextBlock>\n                                Lines disappear after\n                                <TextBlock Margin=\"3,0,0,0\"\n                                           Text=\"{Binding ElementName=slider_history_timeout, Path=Value, Mode=OneWay, StringFormat=N1}\" />s\n                            </TextBlock>\n                                </CheckBox>\n                            </WrapPanel>\n                            <uc:LabeledSlider x:Name=\"slider_history_timeout\"\n                                              Margin=\"20,5,10,0\"\n                                              Title=\"\"\n                                              Maximum=\"120.0\"\n                                              Minimum=\"0.5\"\n                                              Logarithmic=\"True\"\n                                              HalfWay=\"0.08\"\n                                              Value=\"{Binding HistoryTimeout, Mode=TwoWay}\" />\n                            <CheckBox x:Name=\"cb_hideWindow\"\n                                      IsChecked=\"{Binding EnableWindowFade}\">Hide window when empty</CheckBox>\n                            <CheckBox x:Name=\"cb_periodictopmost\"\n                                      IsChecked=\"{Binding PeriodicTopmost}\">Put on top periodically</CheckBox>\n                        </StackPanel>\n                    </StackPanel>\n                    <StackPanel Margin=\"5\"\n                                Grid.Column=\"1\">\n                        <GroupBox Header=\"Alignment\">\n                            <StackPanel Margin=\"0,0,5,0\">\n                                <RadioButton x:Name=\"rb_align_left\"\n                                             Content=\"left\"\n                                             GroupName=\"align\"\n                                             IsChecked=\"{Binding LabelTextAlignment, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:TextAlignment.Left}}\" />\n                                <RadioButton x:Name=\"rb_align_center\"\n                                             Content=\"center\"\n                                             GroupName=\"align\"\n                                             HorizontalAlignment=\"Center\"\n                                             IsChecked=\"{Binding LabelTextAlignment, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:TextAlignment.Center}}\" />\n                                <RadioButton x:Name=\"rb_align_right\"\n                                             Content=\"right\"\n                                             GroupName=\"align\"\n                                             HorizontalAlignment=\"Right\"\n                                             IsChecked=\"{Binding LabelTextAlignment, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:TextAlignment.Right}}\" />\n                            </StackPanel>\n                        </GroupBox>\n                        <GroupBox Header=\"Move Direction\">\n                            <StackPanel>\n                                <RadioButton x:Name=\"rb_td_up\"\n                                             Content=\"up\"\n                                             GroupName=\"td\"\n                                             IsChecked=\"{Binding LabelTextDirection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:TextDirection.Up}}\" />\n                                <RadioButton x:Name=\"rb_td_down\"\n                                             Content=\"down\"\n                                             GroupName=\"td\"\n                                             IsChecked=\"{Binding LabelTextDirection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:TextDirection.Down}}\" />\n                            </StackPanel>\n                        </GroupBox>\n                        <GroupBox Header=\"Animation\">\n                            <StackPanel>\n                                <RadioButton x:Name=\"rb_style_noani\"\n                                             Content=\"no animation\"\n                                             GroupName=\"style\"\n                                             IsChecked=\"{Binding LabelAnimation, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:Style.NoAnimation}}\" />\n                                <RadioButton x:Name=\"rb_style_slide\"\n                                             Content=\"slide\"\n                                             GroupName=\"style\"\n                                             IsChecked=\"{Binding LabelAnimation, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:Style.Slide}}\" />\n                            </StackPanel>\n                        </GroupBox>\n                    </StackPanel>\n                    <StackPanel Margin=\"5\"\n                                Grid.Column=\"2\">\n                        <GroupBox Header=\"Keyboard Shortcuts\">\n                            <StackPanel>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"5,3,0,0\">\n                                Resize/move mode</TextBlock>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"15,3,0,0\">\n                                <Run Text=\"{Binding KeystrokeHistorySettingsModeShortcut}\" />\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ChangeResizeMoveShortcut\">Change</Hyperlink>&#160;&#160;&#160;&#160;&#160;&#160;\n                                <Hyperlink Click=\"Hyperlink_TriggerResizeMoveShortcut\">Trigger</Hyperlink>\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ResetResizeMoveShortcut\">Reset to <Run x:Name=\"SettingsModeShortcutDefault\"></Run></Hyperlink></TextBlock>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"5,3,0,0\">\n                                Passwort protection mode</TextBlock>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"15,3,0,0\">\n                                <Run Text=\"{Binding KeystrokeHistoryPasswordModeShortcut}\" />\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ChangePasswordModeShortcut\">Change</Hyperlink>&#160;&#160;&#160;&#160;&#160;&#160;\n                                <Hyperlink Click=\"Hyperlink_TriggerPasswordModeShortcut\">Trigger</Hyperlink>\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ResetPasswordModeShortcut\">Reset to <Run x:Name=\"PasswordModeShortcutDefault\"></Run></Hyperlink></TextBlock>\n                            </StackPanel>\n                        </GroupBox>\n                    </StackPanel>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"Cursor and Clicks\">\n                <Grid>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"*\" />\n                        <ColumnDefinition Width=\"*\" />\n                        <ColumnDefinition Width=\"*\" />\n                    </Grid.ColumnDefinitions>\n                    <GroupBox Header=\"Cursor\"\n                              Grid.Column=\"0\"\n                              Margin=\"5\">\n                        <StackPanel>\n                            <CheckBox x:Name=\"cb_cursorindicator\"\n                                      IsChecked=\"{Binding EnableCursorIndicator}\">Indicate cursor position</CheckBox>\n                            <uc:LabeledSlider x:Name=\"slider_ci_opacity\"\n                                              Title=\"Opacity\"\n                                              Minimum=\"0.1\"\n                                              Maximum=\"1\"\n                                              Value=\"{Binding CursorIndicatorOpacity, Mode=TwoWay}\" />\n                            <uc:LabeledSlider x:Name=\"slider_ci_size\"\n                                              Title=\"Size\"\n                                              Minimum=\"3.0\"\n                                              Maximum=\"300.0\"\n                                              Value=\"{Binding CursorIndicatorSize, Mode=TwoWay}\" />\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Label VerticalAlignment=\"Center\">Color</Label>\n                                <xctk:ColorPicker x:Name=\"colorpicker_ci_color\"\n                                                  ColorMode=\"ColorCanvas\"\n                                                  Margin=\"10,0,0,0\"\n                                                  Width=\"80\"\n                                                  SelectedColor=\"{Binding CursorIndicatorColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                            </StackPanel>\n                            <CheckBox x:Name=\"cb_cursorindicator_flashonclick\"\n                                      IsChecked=\"{Binding CursorIndicatorFlashOnClick}\">Change color on click</CheckBox>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Label VerticalAlignment=\"Center\">Click color</Label>\n                                <xctk:ColorPicker x:Name=\"colorpicker_ci_clickcolor\"\n                                                  ColorMode=\"ColorCanvas\"\n                                                  Margin=\"10,0,0,0\"\n                                                  Width=\"80\"\n                                                  IsEnabled=\"{Binding CursorIndicatorFlashOnClick, Mode=OneWay}\"\n                                                  SelectedColor=\"{Binding CursorIndicatorClickColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                            </StackPanel>\n                            <CheckBox x:Name=\"cb_cursorindicator_drawedge\"\n                                      IsChecked=\"{Binding CursorIndicatorDrawEdge}\">Draw circle edge</CheckBox>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Label VerticalAlignment=\"Center\">Edge color</Label>\n                                <xctk:ColorPicker x:Name=\"colorpicker_ci_edgecolor\"\n                                                  ColorMode=\"ColorCanvas\"\n                                                  Margin=\"10,0,0,0\"\n                                                  Width=\"80\"\n                                                  IsEnabled=\"{Binding CursorIndicatorDrawEdge, Mode=OneWay}\"\n                                                  SelectedColor=\"{Binding CursorIndicatorEdgeColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                            </StackPanel>\n                            <uc:LabeledSlider x:Name=\"slider_ci_edgestrokethickness\"\n                                              Title=\"Edge Thickness\"\n                                              Minimum=\"0.0\"\n                                              Maximum=\"20.0\"\n                                              Value=\"{Binding CursorIndicatorEdgeStrokeThickness, Mode=TwoWay}\"\n                                              IsEnabled=\"{Binding CursorIndicatorDrawEdge, Mode=OneWay}\" />\n                            <CheckBox x:Name=\"cb_cursorindicator_hide_if_custom_cursor\"\n                                      IsChecked=\"{Binding CursorIndicatorHideIfCustomCursor}\"\n                                      Height=\"50\"\n                                      Margin=\"5,10,-0.333,0\">\n                                <TextBlock TextWrapping=\"Wrap\"><Run Text=\"Hide if cursor icon is non-standard (Firefox video full-screen workaround)\" /></TextBlock>\n                            </CheckBox>\n                        </StackPanel>\n                    </GroupBox>\n                    <GroupBox Header=\"Mouse Clicks\"\n                              Grid.Column=\"1\"\n                              Margin=\"5\">\n                        <StackPanel Margin=\"0,0,0,5\">\n                            <RadioButton x:Name=\"rb_bi_disable\"\n                                         Content=\"Do not indicate clicks\"\n                                         GroupName=\"bi\"\n                                         IsChecked=\"{Binding ButtonIndicator, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:ButtonIndicatorType.Disabled}}\" />\n                            <RadioButton x:Name=\"rb_bi_icon\"\n                                         Content=\"Show icon on click\"\n                                         GroupName=\"bi\"\n                                         IsChecked=\"{Binding ButtonIndicator, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static uc:ButtonIndicatorType.PicsAroundCursor}}\" />\n                            <uc:LabeledSlider x:Name=\"slider_bi_size\"\n                                              Title=\"Size\"\n                                              Minimum=\"0.1\"\n                                              Maximum=\"2\"\n                                              Value=\"{Binding ButtonIndicatorScaling, Mode=TwoWay}\" />\n                            <uc:LabeledSlider x:Name=\"slider_bi_distance\"\n                                              Title=\"Distance\"\n                                              Minimum=\"10\"\n                                              Maximum=\"100\"\n                                              Value=\"{Binding ButtonIndicatorPositionDistance, Mode=TwoWay}\" />\n                            <uc:LabeledSlider x:Name=\"slider_bi_angle\"\n                                              Title=\"Direction\"\n                                              Maximum=\"65\"\n                                              Value=\"{Binding ButtonIndicatorPositionAngle, Mode=TwoWay}\" />\n                            <CheckBox x:Name=\"cb_bi_modifiers\"\n                                      IsChecked=\"{Binding ButtonIndicatorShowModifiers}\">Show modifiers (Ctrl, Shift, ...)</CheckBox>\n                            <CheckBox x:Name=\"cb_bi_customicons\"\n                                      IsChecked=\"{Binding ButtonIndicatorUseCustomIcons}\">\n                                <TextBlock>Use custom icons.</TextBlock>\n                            </CheckBox>\n                            <Button x:Name=\"button_bi_export_builtin\"\n                                    Click=\"OnButtonExportBuiltinIcons\"\n                                    Margin=\"15,5,5,5\">Export built-in icons</Button>\n                            <Button x:Name=\"button_bi_select_folder\"\n                                    Click=\"OnButtonCustomIconsSelectFolder\"\n                                    Margin=\"15,0,5,5\">Select Folder with custom icons</Button>\n                            <TextBlock x:Name=\"textblockCustomIconInfoBuiltin\"\n                                       Margin=\"15,0,5,5\"\n                                       TextWrapping=\"Wrap\">\n                        Currently using built-in icons.</TextBlock>\n                            <TextBlock x:Name=\"textblockCustomIconInfoCustom\"\n                                       Margin=\"15,0,5,5\"\n                                       TextWrapping=\"Wrap\">\n                            Currently using icons from\n                            <LineBreak />\n                            <Run x:Name=\"runCustomIconFolderInfo\"\n                                 Text=\"{Binding ButtonIndicatorCustomIconsFolder}\" />\n                            <LineBreak />\n                            <Hyperlink Click=\"OnClickCustomIconsRefresh\">Refresh cache</Hyperlink></TextBlock>\n                        </StackPanel>\n                    </GroupBox>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"Drawing\">\n                <Grid>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                    </Grid.ColumnDefinitions>\n                    <StackPanel Grid.Column=\"0\"\n                                Margin=\"5\">\n                        <GroupBox Header=\"Draw Lines\">\n                            <StackPanel>\n                                <CheckBox x:Name=\"cb_al_enable\"\n                                          IsChecked=\"{Binding EnableAnnotateLine}\">Enable Line drawing</CheckBox>\n                                <StackPanel Orientation=\"Horizontal\">\n                                    <Label VerticalAlignment=\"Center\">Line Color</Label>\n                                    <xctk:ColorPicker x:Name=\"colorpicker_al_color\"\n                                                      ColorMode=\"ColorCanvas\"\n                                                      Margin=\"10,0,0,0\"\n                                                      Width=\"80\"\n                                                      SelectedColor=\"{Binding AnnotateLineColor, Mode=TwoWay, Converter={StaticResource MediaColorDrawingColorConverter}}\" />\n                                </StackPanel>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"5,5,5,0\">\n                                <Run FontWeight=\"Bold\">How-To use:</Run> Press the 'Trigger Line Draw' shortcut (<Run Text=\"{Binding AnnotateLineShortcut}\" />). Then click and drag to draw a line. Click again to clear the line. The shortcut needs to be pressed again for the next line.</TextBlock>\n                            </StackPanel>\n                        </GroupBox>\n                    </StackPanel>\n                    <StackPanel Grid.Column=\"2\"\n                                Margin=\"5\">\n                    <GroupBox Header=\"Keyboard Shortcuts\">\n                        <StackPanel>\n                            <TextBlock TextWrapping=\"Wrap\"\n                                       Margin=\"5,3,0,0\">\n                                Trigger Line Draw</TextBlock>\n                            <TextBlock TextWrapping=\"Wrap\"\n                                       Margin=\"15,3,0,0\">\n                                <Run Text=\"{Binding AnnotateLineShortcut}\" />\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ChangeAnnotateLineShortcut\">Change</Hyperlink>&#160;&#160;&#160;&#160;&#160;&#160;\n                                <Hyperlink Click=\"Hyperlink_TriggerAnnotateLineShortcut\">Trigger</Hyperlink>\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ResetAnnotateLineShortcut\">Reset to <Run x:Name=\"AnnotateLineShortcutDefault\"></Run></Hyperlink></TextBlock>\n                        </StackPanel>\n                    </GroupBox>\n                    </StackPanel>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"Startup and Standby\">\n                <Grid>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                        <ColumnDefinition Width=\"100*\" />\n                    </Grid.ColumnDefinitions>\n                    <StackPanel Grid.Column=\"0\"\n                                Margin=\"5\">\n                        <GroupBox Header=\"Startup\">\n                            <StackPanel>\n                                <CheckBox x:Name=\"cb_standby_enable\"\n                                          IsChecked=\"{Binding Standby}\">Standby mode</CheckBox>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"5,5,5,0\">\n                                In standby mode, all Key'n'Stroke functionality will be temporarily disabled, even if it is enabled in the respective settings.</TextBlock>\n                                <CheckBox x:Name=\"cb_standbyonstartup_enable\"\n                                          IsChecked=\"{Binding StartInStandby}\">Startup in standby</CheckBox>\n                                <CheckBox x:Name=\"cb_welcomeonstartup_enable\"\n                                          IsChecked=\"{Binding WelcomeOnStartup}\">\n                                    <TextBlock TextWrapping=\"Wrap\">Show <Hyperlink Click=\"Hyperlink_WelcomeWindow\">\n                                        welcome window</Hyperlink> on startup.</TextBlock>\n                                </CheckBox>\n                            </StackPanel>\n                        </GroupBox>\n                    </StackPanel>\n                    <StackPanel Grid.Column=\"1\"\n                                Margin=\"5\">\n                        <GroupBox Header=\"Updates\">\n                            <TextBlock TextWrapping=\"Wrap\"\n                                       Margin=\"5,5,5,0\">\n                                Use the update button in the <Hyperlink Click=\"Hyperlink_WelcomeWindow\">welcome window</Hyperlink> to search for updates.</TextBlock>\n                        </GroupBox>\n                    </StackPanel>\n                    <StackPanel Grid.Column=\"2\"\n                                Margin=\"5\">\n                        <GroupBox Header=\"Keyboard Shortcuts\">\n                            <StackPanel>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"5,3,0,0\">\n                                Toggle standby mode</TextBlock>\n                                <TextBlock TextWrapping=\"Wrap\"\n                                           Margin=\"15,3,0,0\">\n                                <Run Text=\"{Binding StandbyShortcut}\" />\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ChangeStandbyShortcut\">Change</Hyperlink>&#160;&#160;&#160;&#160;&#160;&#160;\n                                <Hyperlink Click=\"Hyperlink_TriggerStandbyShortcut\">Trigger</Hyperlink>\n                                <LineBreak />\n                                <Hyperlink Click=\"Hyperlink_ResetStandbyShortcut\">Reset to <Run x:Name=\"StandbyShortcutDefault\"></Run></Hyperlink></TextBlock>\n                            </StackPanel>\n                        </GroupBox>\n                    </StackPanel>\n                </Grid>\n            </TabItem>\n        </TabControl>\n        <StackPanel Grid.Column=\"0\"\n                    Grid.Row=\"1\"\n                    Grid.ColumnSpan=\"2\"\n                    Orientation=\"Horizontal\"\n                    Margin=\"10,0,10,10\">\n            <Button x:Name=\"bn_reset_position\"\n                    Margin=\"0,0,10,0\"\n                    Padding=\"5,0,5,0\"\n                    VerticalAlignment=\"Bottom\"\n                    Click=\"Bn_reset_position_Click\">Reset position</Button>\n            <Button x:Name=\"bn_reset_all\"\n                    Margin=\"10,0,10,0\"\n                    Padding=\"5,0,5,0\"\n                    VerticalAlignment=\"Bottom\"\n                    Click=\"bn_reset_all_Click\">Reset all</Button>\n            <TextBlock Margin=\"10,0,10,0\"\n                       VerticalAlignment=\"Bottom\"\n                       Visibility=\"{Binding Standby, Converter={StaticResource BooleanToVisibilityConverter}}\">\n                <Hyperlink Click=\"Hyperlink_DisableStandbyMode\">Disable Standby Mode!</Hyperlink>\n            </TextBlock>\n        </StackPanel>\n        <Button x:Name=\"button_exit\"\n                Grid.Column=\"2\"\n                Grid.Row=\"1\"\n                VerticalAlignment=\"Bottom\"\n                Margin=\"10,0,10,10\"\n                Click=\"button_exit_Click\">Exit Application</Button>\n        <Button x:Name=\"button_close\"\n                Grid.Column=\"3\"\n                Grid.Row=\"1\"\n                VerticalAlignment=\"Bottom\"\n                Margin=\"10,0,10,10\"\n                Click=\"Button_close_Click\">Close Settings</Button>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/Settings1.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\nusing WpfColorFontDialog;\n\nnamespace KeyNStroke\n{\n    public class EnumBooleanConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //MessageBox.Show(\"Convert \" + value.ToString() + \" tt: \" + targetType.ToString() + \" par \" + parameter.ToString());\n            return value.Equals(parameter);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //MessageBox.Show(\"Convert Back \" + value.ToString() + \" tt: \" + targetType.ToString() + \" par \" + parameter.ToString());\n            return ((bool)value) ? parameter : Binding.DoNothing;\n        }\n    }\n\n    public class FloatPercentageConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //Log.e(\"CNV\", \"Convert \" + value.ToString() + \" tt: \" + targetType.ToString());\n            return (int) ((float) value * 100f);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //Log.e(\"CNV\", \"Convert Back \" + value.ToString() + \" tt: \" + targetType.ToString());\n            return (int)value / 100f;\n        }\n    }\n\n    public class MediaColorDrawingColorConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //Log.e(\"CNV\", \"Convert \" + value.ToString() + \" tt: \" + targetType.ToString());\n            return UIHelper.ToMediaColor((System.Drawing.Color) value);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            //Log.e(\"CNV\", \"Convert Back \" + value.ToString() + \" tt: \" + targetType.ToString());\n            return UIHelper.ToDrawingColor((System.Windows.Media.Color) value);\n        }\n    }\n\n    /// <summary>\n    /// Interaktionslogik für Settings1.xaml\n    /// </summary>\n    public partial class Settings1 : Window\n    {\n        public Settings1(SettingsStore s, IKeystrokeEventProvider k)\n        {\n            InitializeComponent();\n            settings = s;\n            this.k = k;\n            Log.e(\"BIN\", \"Set Data context in settings window\");\n            layout_root.DataContext = settings;\n            SettingsModeShortcutDefault.Text = settings.KeystrokeHistorySettingsModeShortcutDefault;\n            PasswordModeShortcutDefault.Text = settings.KeystrokeHistoryPasswordModeShortcutDefault;\n            AnnotateLineShortcutDefault.Text = settings.AnnotateLineShortcutDefault;\n            StandbyShortcutDefault.Text = settings.StandbyShortcutDefault;\n            s.PropertyChanged += S_PropertyChanged;\n            s.CallPropertyChangedForAllProperties();\n        }\n\n        SettingsStore settings;\n        IKeystrokeEventProvider k;\n\n        protected override void OnClosed(EventArgs e)\n        {\n            base.OnClosed(e);\n            ((App)Application.Current).onSettingsWindowClosed();\n        }\n\n        private void RadioButton_Checked(object sender, RoutedEventArgs e)\n        {\n\n        }\n\n        private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\n        {\n            UrlOpener.OpenGithub();\n        }\n\n        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)\n        {\n            settings.SaveAll();\n        }\n\n        private void Bn_reset_position_Click(object sender, RoutedEventArgs e)\n        {\n            settings.PanelLocation = settings.PanelLocationDefault;\n            settings.PanelSize = settings.PanelSizeDefault;\n            settings.WindowLocation = settings.WindowLocationDefault;\n            settings.WindowSize = settings.WindowSizeDefault;\n        }\n\n        private void bn_reset_all_Click(object sender, RoutedEventArgs e)\n        {\n            settings.ResetAll();\n        }\n\n        private void Button_close_Click(object sender, RoutedEventArgs e)\n        {\n            Close();\n        }\n\n        private void button_exit_Click(object sender, RoutedEventArgs e)\n        {\n            System.Windows.Application.Current.Shutdown();\n        }\n\n        private void OnButtonTextFontClick(object sender, RoutedEventArgs e)\n        {\n            ColorFontDialog dialog = new ColorFontDialog(true, true, false);\n            dialog.Font = settings.LabelFont;\n            dialog.Loaded += FontDialogLoaded;\n            if (dialog.ShowDialog() == true)\n            {\n                FontInfo font = dialog.Font;\n                if (font != null)\n                {\n                    settings.LabelFont = font;\n                }\n            }\n        }\n\n        private void FontDialogLoaded(object sender, RoutedEventArgs e)\n        {\n            // run after the init funtions of the colordialog itself\n            ColorFontDialog dialog = (ColorFontDialog)sender;\n            TextBox sampleText = UIHelper.FindChild<TextBox>((DependencyObject)dialog.Content, \"txtSampleText\");\n            sampleText.Background = new SolidColorBrush(UIHelper.ToMediaColor(settings.BackgroundColor));\n            sampleText.Foreground = new SolidColorBrush(UIHelper.ToMediaColor(settings.LabelColor));\n        }\n\n        #region Shortcut - ChangeResizeMove\n\n        private void Hyperlink_ChangeResizeMoveShortcut(object sender, RoutedEventArgs e)\n        {\n            ReadShortcut rs = new ReadShortcut(k, \"enabling and disabling the move+resize mode for the keystroke history window.\");\n            rs.ShowDialog();\n            if (rs.Shortcut != null)\n            {\n                settings.KeystrokeHistorySettingsModeShortcut = rs.Shortcut;\n            }\n        }\n\n        private void Hyperlink_ResetResizeMoveShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.KeystrokeHistorySettingsModeShortcut = settings.KeystrokeHistorySettingsModeShortcutDefault;\n        }\n\n        private void Hyperlink_TriggerResizeMoveShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.EnableSettingsMode = !settings.EnableSettingsMode;\n        }\n\n        #endregion\n\n        #region Shortcut - PasswordMode\n\n        private void Hyperlink_ChangePasswordModeShortcut(object sender, RoutedEventArgs e)\n        {\n            ReadShortcut rs = new ReadShortcut(k, \" toggling password protection mode.\");\n            rs.ShowDialog();\n            if (rs.Shortcut != null)\n            {\n                settings.KeystrokeHistoryPasswordModeShortcut= rs.Shortcut;\n            }\n        }\n\n        private void Hyperlink_ResetPasswordModeShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.KeystrokeHistoryPasswordModeShortcut = settings.KeystrokeHistoryPasswordModeShortcutDefault;\n        }\n\n        private void Hyperlink_TriggerPasswordModeShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.EnablePasswordMode = !settings.EnablePasswordMode;\n        }\n\n        #endregion\n\n        #region Shortcut - AnnotateLine\n\n        private void Hyperlink_ChangeAnnotateLineShortcut(object sender, RoutedEventArgs e)\n        {\n            ReadShortcut rs = new ReadShortcut(k, \" triggering line draw.\");\n            rs.ShowDialog();\n            if (rs.Shortcut != null)\n            {\n                settings.AnnotateLineShortcut = rs.Shortcut;\n            }\n        }\n\n        private void Hyperlink_ResetAnnotateLineShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.AnnotateLineShortcut = settings.AnnotateLineShortcutDefault;\n        }\n\n        private void Hyperlink_TriggerAnnotateLineShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.AnnotateLineShortcutTrigger = true;\n        }\n\n\n        #endregion\n\n        #region Shortcut - Toggle Standby\n\n        private void Hyperlink_ChangeStandbyShortcut(object sender, RoutedEventArgs e)\n        {\n            ReadShortcut rs = new ReadShortcut(k, \" toggling standby mode.\");\n            rs.ShowDialog();\n            if (rs.Shortcut != null)\n            {\n                settings.StandbyShortcut = rs.Shortcut;\n            }\n        }\n\n        private void Hyperlink_ResetStandbyShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.StandbyShortcut = settings.StandbyShortcutDefault;\n        }\n\n        private void Hyperlink_TriggerStandbyShortcut(object sender, RoutedEventArgs e)\n        {\n            settings.Standby = !settings.Standby;\n        }\n\n        #endregion\n\n        #region Custom Icons\n\n\n        private void S_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)\n        {\n            switch(e.PropertyName)\n            {\n                case \"ButtonIndicatorUseCustomIcons\":\n                    if (settings.ButtonIndicatorUseCustomIcons)\n                    {\n                        textblockCustomIconInfoBuiltin.Visibility = Visibility.Collapsed;\n                        textblockCustomIconInfoCustom.Visibility = Visibility.Visible;\n                    } \n                    else\n                    {\n                        textblockCustomIconInfoBuiltin.Visibility = Visibility.Visible;\n                        textblockCustomIconInfoCustom.Visibility = Visibility.Collapsed;\n                    }\n                    break;\n            }\n        }\n\n        private void OnButtonCustomIconsSelectFolder(object sender, RoutedEventArgs e)\n        {\n            using (System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog())\n            {\n                dlg.Description = \"Select folder with custom icons\";\n                dlg.SelectedPath = settings.ButtonIndicatorCustomIconsFolder;\n                dlg.ShowNewFolderButton = true;\n                System.Windows.Forms.DialogResult result = dlg.ShowDialog();\n                if (result == System.Windows.Forms.DialogResult.OK)\n                {\n                    settings.ButtonIndicatorCustomIconsFolder = dlg.SelectedPath;\n                }\n            }\n        }\n\n        private void OnButtonExportBuiltinIcons(object sender, RoutedEventArgs e)\n        {\n            using (System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog())\n            {\n                dlg.Description = \"Select folder for export\";\n                // dlg.SelectedPath = settings.ButtonIndicatorCustomIconsFolder;\n                dlg.ShowNewFolderButton = true;\n                System.Windows.Forms.DialogResult result = dlg.ShowDialog();\n                if (result == System.Windows.Forms.DialogResult.OK)\n                {\n                    ImageResources.ExportBuiltinRessources(dlg.SelectedPath);\n                }\n            }\n            \n        }\n\n        private void OnClickCustomIconsHelp(object sender, RoutedEventArgs e)\n        {\n\n        }\n\n        private void OnClickCustomIconsRefresh(object sender, RoutedEventArgs e)\n        {\n            if (settings.ButtonIndicatorUseCustomIcons)\n            {\n                ImageResources.ReloadRessources(settings.ButtonIndicatorCustomIconsFolder);\n            }\n            else\n            {\n                ImageResources.ReloadRessources(null);\n            }\n        }\n\n\n\n\n        #endregion\n\n        #region Other Hyperlinks\n\n        private void Hyperlink_WelcomeWindow(object sender, RoutedEventArgs e)\n        {\n            ((App)Application.Current).showWelcomeWindow();\n        }\n\n        private void Hyperlink_DisableStandbyMode(object sender, RoutedEventArgs e)\n        {\n            settings.Standby = false;\n        }\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/SettingsStore.cs",
    "content": "﻿using Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawing;\nusing System.IO;\nusing System.IO.IsolatedStorage;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.Serialization;\nusing System.Runtime.Serialization.Json;\nusing System.Text;\nusing System.Threading.Tasks;\nusing WpfColorFontDialog;\n\nnamespace KeyNStroke\n{\n\n    #region Enums for some settings\n\n    public enum TextAlignment\n    {\n        Left,\n        Right,\n        Center\n    }\n\n    public enum TextDirection\n    {\n        Up,\n        Down\n    }\n\n    public enum Style\n    {\n        NoAnimation,\n        Slide\n    }\n\n    public enum ButtonIndicatorType\n    {\n        Disabled,\n        PicsAroundCursor\n    }\n\n    public enum KeystrokeMethodEnum\n    {\n        [Description(\"Text mode\")]\n        TextMode = 1,\n        [Description(\"Text mode (Backspace can delete text)\")]\n        TextModeBackspaceCanDeleteText = 2,\n        [Description(\"Shortcut mode (no letters and no shift)\")]\n        ShortcutModeNoText = 3,\n        [Description(\"Shortcut mode (with letters and shift)\")]\n        ShortcutModeWithText = 4\n    }\n\n    public static class Extensions\n    {\n        public static bool IsTextMode(this KeystrokeMethodEnum method)\n        {\n            return method == KeystrokeMethodEnum.TextMode || method == KeystrokeMethodEnum.TextModeBackspaceCanDeleteText;\n        }\n    }\n    \n    #endregion\n\n    #region Serializable classes for complex types\n\n    interface IGet<T>\n    {\n        T Get();\n    }\n\n    [DataContract]\n    public class SerializableFont : IGet<FontInfo>\n    {\n        [DataMember] public string family;\n        [DataMember] public SerializableColor color;\n        [DataMember] public double size;\n        [DataMember] public string style;\n        [DataMember] public int stretch;\n        [DataMember] public int weight;\n\n        public SerializableFont(FontInfo value)\n        {\n            color = new SerializableColor(value.Color.Brush.Color);\n            family = value.Family.Source;\n            size = value.Size;\n            stretch = value.Stretch.ToOpenTypeStretch();\n            if (value.Style == System.Windows.FontStyles.Italic) style = \"Italic\";\n            else if (value.Style == System.Windows.FontStyles.Oblique) style = \"Oblique\";\n            else style = \"Normal\";\n            weight = value.Weight.ToOpenTypeWeight();\n\n        }\n        public FontInfo Get()\n        {\n            System.Windows.FontStyle style = System.Windows.FontStyles.Normal;\n            if (this.style == \"Italic\") style = System.Windows.FontStyles.Italic;\n            if (this.style == \"Oblique\") style = System.Windows.FontStyles.Oblique;\n\n            return new FontInfo(new System.Windows.Media.FontFamily(family),\n                                size,\n                                style,\n                                System.Windows.FontStretch.FromOpenTypeStretch(stretch),\n                                System.Windows.FontWeight.FromOpenTypeWeight(weight),\n                                new System.Windows.Media.SolidColorBrush(color.Get()));\n        }\n        public override string ToString()\n        {\n            return $\"{family}, {size}, {style}, stretch={stretch}, weight={weight}, #{color.r:X2}{color.g:X2}{color.b:X2}\";\n        }\n    }\n\n    [DataContract]\n    public class SerializablePoint : IGet<System.Windows.Point>\n    {\n        [DataMember] public double x;\n        [DataMember] public double y;\n        public SerializablePoint(System.Windows.Point value)\n        {\n            x = value.X;\n            y = value.Y;\n        }\n        public System.Windows.Point Get()\n        {\n            return new System.Windows.Point(x, y);\n        }\n    }\n\n    [DataContract]\n    public class SerializableSize : IGet<Size>\n    {\n        [DataMember] public int width;\n        [DataMember] public int height;\n        public SerializableSize(Size value)\n        {\n            width = value.Width;\n            height = value.Height;\n        }\n        public Size Get()\n        {\n            return new Size(width, height);\n        }\n    }\n\n    [DataContract]\n    public class SerializableColor : IGet<System.Windows.Media.Color>\n    {\n        [DataMember] public byte a;\n        [DataMember] public byte r;\n        [DataMember] public byte g;\n        [DataMember] public byte b;\n\n        public SerializableColor(System.Windows.Media.Color value)\n        {\n            a = value.A;\n            r = value.R;\n            g = value.G;\n            b = value.B;\n        }\n        public System.Windows.Media.Color Get()\n        {\n            return System.Windows.Media.Color.FromArgb(a, r, g, b);\n        }\n    }\n\n    [DataContract]\n    public class SerializableColor2 : IGet<Color>\n    {\n        [DataMember] public int color;\n        public SerializableColor2(Color value)\n        {\n            color = value.ToArgb();\n        }\n        public Color Get()\n        {\n            return Color.FromArgb(color);\n        }\n    }\n\n    #endregion\n\n    #region Serializable Settings\n\n    [DataContract]\n    class Settings\n    {\n        [DataMember] public SerializableFont labelFont = null;\n        [DataMember] public SerializableColor2 labelColor = null;\n        [DataMember] public SerializableColor2 backgroundColor = null;\n        [DataMember] public Nullable<TextAlignment> labelTextAlignment = null;\n        [DataMember] public Nullable<TextDirection> labelTextDirection = null;\n        [DataMember] public Nullable<Style> labelAnimation = null;\n        [DataMember] public SerializablePoint windowLocation = null;\n        [DataMember] public SerializableSize windowSize = null;\n        [DataMember] public SerializablePoint panelLocation = null;\n        [DataMember] public SerializableSize panelSize = null;\n        [DataMember] public Nullable<double> lineDistance = null;\n        [DataMember] public Nullable<int> historyLength = null;\n        [DataMember] public Nullable<double> historyTimeout = null;\n        [DataMember] public Nullable<bool> enableHistoryTimeout = null;\n        [DataMember] public Nullable<bool> enableWindowFade = null;\n        [DataMember] public Nullable<bool> enableCursorIndicator = null;\n        [DataMember] public Nullable<double> cursorIndicatorOpacity = null;\n        [DataMember] public Nullable<double> cursorIndicatorSize = null;\n        [DataMember] public SerializableColor2 cursorIndicatorColor = null;\n        [DataMember] public Nullable<bool> cursorIndicatorFlashOnClick = null;\n        [DataMember] public SerializableColor2 cursorIndicatorClickColor = null;\n        [DataMember] public Nullable<bool> cursorIndicatorHideIfCustomCursor = null;\n        [DataMember] public Nullable<bool> cursorIndicatorDrawEdge = null;\n        [DataMember] public SerializableColor2 cursorIndicatorEdgeColor = null;\n        [DataMember] public Nullable<double> cursorIndicatorEdgeStrokeThickness = null;\n        [DataMember] public Nullable<ButtonIndicatorType> buttonIndicator = null;\n        [DataMember] public Nullable<double> buttonIndicatorScaling = null;\n        [DataMember] public Nullable<double> buttonIndicatorPositionAngle = null;\n        [DataMember] public Nullable<double> buttonIndicatorPositionDistance = null;\n        [DataMember] public Nullable<bool> buttonIndicatorShowModifiers = null;\n        [DataMember] public Nullable<bool> buttonIndicatorUseCustomIcons = null;\n        [DataMember] public string buttonIndicatorCustomIconsFolder = null;\n        [DataMember] public Nullable<bool> addButtonEventsToHistory = null;\n        [DataMember] public Nullable<bool> backspaceDeletesText = null; // replaced by keystrokeMethod\n        [DataMember] public Nullable<bool> periodicTopmost = null;\n        [DataMember] public Nullable<bool> enableKeystrokeHistory = null;\n        [DataMember] public String keystrokeHistorySettingsModeShortcut = null;\n        [DataMember] public Nullable<bool> enableSettingsMode = null;\n        [DataMember] public String keystrokeHistoryPasswordModeShortcut = null;\n        [DataMember] public Nullable<bool> enablePasswordMode = null;\n        [DataMember] public Nullable<KeystrokeMethodEnum> keystrokeMethod = null;\n        [DataMember] public Nullable<bool> enableAnnotateLine = null;\n        [DataMember] public SerializableColor2 annotateLineColor = null;\n        [DataMember] public String annotateLineShortcut = null;\n        [DataMember] public String standbyShortcut = null;\n        [DataMember] public Nullable<bool> startInStandby = null;\n        [DataMember] public Nullable<bool> welcomeOnStartup = null;\n\n\n    }\n\n    #endregion\n\n    public class SettingsStore : INotifyPropertyChanged\n    {\n\n        #region Constructor\n\n        public SettingsStore()\n        {\n            const string SETTINGS_PATH = \"Key-n-Stroke/settings.json\";\n            string appldatapath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\n            configpath = Path.Combine(appldatapath, SETTINGS_PATH);\n        }\n\n        string configpath;\n\n        #endregion\n\n        #region Helpers: Or(a,b)\n\n        private T Or<T>(IGet<T> a, T b)\n        {\n            if (a != null) return a.Get();\n            return b;\n        }\n\n        private T Or<T>(Nullable<T> a, T b) where T : struct\n        {\n            if (a.HasValue) return a.Value;\n            return b;\n        }\n\n        private T Or<T>(T a, T b) where T : class\n        {\n            if (a != null) return a;\n            return b;\n        }\n\n\n        #endregion\n\n        #region Settings Accessors\n\n        Settings i = new Settings();\n\n        public FontInfo LabelFontDefault = new FontInfo(new System.Windows.Media.FontFamily(\"Calibri\"),\n                                                        14.0,\n                                                        System.Windows.FontStyles.Normal,\n                                                        System.Windows.FontStretch.FromOpenTypeStretch(5),\n                                                        System.Windows.FontWeight.FromOpenTypeWeight(400),\n                                                        new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(255,255,255)));\n        // Note: The color from the FontInfo can't be used. If we use a non-pallete (a color without a Name), then\n        // The .Color item will read as null and the WpfColorFontDialog is unable to work with this FontInfo\n        // So always keep the color in a safe white.\n        public FontInfo LabelFont\n        {\n            get { return Or(i.labelFont, LabelFontDefault); }\n            set { i.labelFont = new SerializableFont(value); OnSettingChanged(\"LabelFont\"); }\n        }\n\n        public Color LabelColorDefault = Color.White;\n        public Color LabelColor\n        {\n            get { return Or(i.labelColor, LabelColorDefault); }\n            set { i.labelColor = new SerializableColor2(value); OnSettingChanged(\"LabelColor\"); }\n        }\n\n\n        public Color BackgroundColorDefault = Color.FromArgb((int)(0.78*255), 0, 0, 0);\n        public Color BackgroundColor\n        {\n            get { return Or(i.backgroundColor, BackgroundColorDefault); }\n            set { i.backgroundColor = new SerializableColor2(value); OnSettingChanged(\"BackgroundColor\"); }\n        }\n\n        public TextAlignment LabelTextAlignmentDefault = TextAlignment.Left;\n        public TextAlignment LabelTextAlignment\n        {\n            get { return Or(i.labelTextAlignment, LabelTextAlignmentDefault); }\n            set { i.labelTextAlignment = value; OnSettingChanged(\"LabelTextAlignment\"); }\n        }\n\n        public TextDirection LabelTextDirectionDefault = TextDirection.Up;\n        public TextDirection LabelTextDirection\n        {\n            get { return Or(i.labelTextDirection, LabelTextDirectionDefault); }\n            set { i.labelTextDirection = value; OnSettingChanged(\"LabelTextDirection\"); }\n        }\n\n        public Style LabelAnimationDefault = Style.Slide;\n        public Style LabelAnimation\n        {\n            get { return Or(i.labelAnimation, LabelAnimationDefault); }\n            set { i.labelAnimation = value; OnSettingChanged(\"LabelAnimation\"); }\n        }\n\n        public System.Windows.Point WindowLocationDefault = new System.Windows.Point(100, 100);\n        public System.Windows.Point WindowLocation\n        {\n            get { return Or(i.windowLocation, WindowLocationDefault); }\n            set { i.windowLocation = new SerializablePoint(value); OnSettingChanged(\"WindowLocation\"); }\n        }\n\n        public Size WindowSizeDefault = new Size(316, 193);\n        public Size WindowSize\n        {\n            get { return Or(i.windowSize, WindowSizeDefault); }\n            set { i.windowSize = new SerializableSize(value); OnSettingChanged(\"WindowSize\"); }\n        }\n\n        public void SetWindowSizeWithoutOnSettingChangedEvent(Size value)\n        {\n            i.windowSize = new SerializableSize(value);\n            dirty = true;\n        }\n\n        public System.Windows.Point PanelLocationDefault = new System.Windows.Point(50, 11);\n        public System.Windows.Point PanelLocation\n        {\n            get { return Or(i.panelLocation, PanelLocationDefault); }\n            set { i.panelLocation = new SerializablePoint(value); OnSettingChanged(\"PanelLocation\"); }\n        }\n\n        public Size PanelSizeDefault = new Size(226, 135);\n        public Size PanelSize\n        {\n            get { return Or(i.panelSize, PanelSizeDefault); }\n            set { i.panelSize = new SerializableSize(value); OnSettingChanged(\"PanelSize\"); }\n        }\n\n        public void SetPanelSizeWithoutOnSettingChangedEvent(Size value)\n        {\n            i.panelSize = new SerializableSize(value);\n            dirty = true;\n        }\n\n        public double LineDistanceDefault = 36;\n        public double LineDistance\n        {\n            get { return Or(i.lineDistance, LineDistanceDefault); }\n            set { i.lineDistance = value; OnSettingChanged(\"LineDistance\"); }\n        }\n\n        public int HistoryLengthDefault = 4;\n        public int HistoryLength\n        {\n            get { return Or(i.historyLength, HistoryLengthDefault); }\n            set { i.historyLength = value; OnSettingChanged(\"HistoryLength\"); }\n        }\n\n        public double HistoryTimeoutDefault = 10.0; // seconds\n        public double HistoryTimeout\n        {\n            get { return Or(i.historyTimeout, HistoryTimeoutDefault); }\n            set { i.historyTimeout = value; OnSettingChanged(\"HistoryTimeout\"); }\n        }\n\n        public bool EnableHistoryTimeoutDefault = true;\n        public bool EnableHistoryTimeout\n        {\n            get { return Or(i.enableHistoryTimeout, EnableHistoryTimeoutDefault); }\n            set { i.enableHistoryTimeout = value; OnSettingChanged(\"EnableHistoryTimeout\"); }\n        }\n\n        public bool EnableWindowFadeDefault = false;\n        public bool EnableWindowFade\n        {\n            get { return Or(i.enableWindowFade, EnableWindowFadeDefault); }\n            set { i.enableWindowFade = value; OnSettingChanged(\"EnableWindowFade\"); }\n        }\n\n        public bool EnableCursorIndicatorDefault = true;\n        public bool EnableCursorIndicator\n        {\n            get { return Or(i.enableCursorIndicator, EnableCursorIndicatorDefault); }\n            set { i.enableCursorIndicator = value; OnSettingChanged(\"EnableCursorIndicator\"); }\n        }\n\n        public double CursorIndicatorOpacityDefault = 0.3;\n        public double CursorIndicatorOpacity\n        {\n            get { return Or(i.cursorIndicatorOpacity, CursorIndicatorOpacityDefault); }\n            set { i.cursorIndicatorOpacity = value; OnSettingChanged(\"CursorIndicatorOpacity\"); }\n        }\n\n        public double CursorIndicatorSizeDefault = 55;\n        public double CursorIndicatorSize\n        {\n            get { return Or(i.cursorIndicatorSize, CursorIndicatorSizeDefault); }\n            set { i.cursorIndicatorSize = value; OnSettingChanged(\"CursorIndicatorSize\"); }\n        }\n\n        public Color CursorIndicatorColorDefault = Color.FromArgb(-32640);\n        public Color CursorIndicatorColor\n        {\n            get { return Or(i.cursorIndicatorColor, CursorIndicatorColorDefault); }\n            set { i.cursorIndicatorColor = new SerializableColor2(value); OnSettingChanged(\"CursorIndicatorColor\"); }\n        }\n\n        public bool CursorIndicatorFlashOnClickDefault = true;\n        public bool CursorIndicatorFlashOnClick\n        {\n            get { return Or(i.cursorIndicatorFlashOnClick, CursorIndicatorFlashOnClickDefault); }\n            set { i.cursorIndicatorFlashOnClick = value; OnSettingChanged(\"CursorIndicatorFlashOnClick\"); }\n        }\n\n        public Color CursorIndicatorClickColorDefault = Color.FromArgb(-32640);\n        public Color CursorIndicatorClickColor\n        {\n            get { return Or(i.cursorIndicatorClickColor, CursorIndicatorClickColorDefault); }\n            set { i.cursorIndicatorClickColor = new SerializableColor2(value); OnSettingChanged(\"CursorIndicatorClickColor\"); }\n        }\n\n        public bool CursorIndicatorDrawEdgeDefault = true;\n        public bool CursorIndicatorDrawEdge\n        {\n            get { return Or(i.cursorIndicatorDrawEdge, CursorIndicatorDrawEdgeDefault); }\n            set { i.cursorIndicatorDrawEdge = value; OnSettingChanged(\"CursorIndicatorDrawEdge\"); }\n        }\n\n        public Color CursorIndicatorEdgeColorDefault = Color.Black;\n        public Color CursorIndicatorEdgeColor\n        {\n            get { return Or(i.cursorIndicatorEdgeColor, CursorIndicatorEdgeColorDefault); }\n            set { i.cursorIndicatorEdgeColor = new SerializableColor2(value); OnSettingChanged(\"CursorIndicatorEdgeColor\"); }\n        }\n\n        public double CursorIndicatorEdgeStrokeThicknessDefault = 2;\n        public double CursorIndicatorEdgeStrokeThickness\n        {\n            get { return Or(i.cursorIndicatorEdgeStrokeThickness, CursorIndicatorEdgeStrokeThicknessDefault); }\n            set { i.cursorIndicatorEdgeStrokeThickness = value; OnSettingChanged(\"CursorIndicatorEdgeStrokeThickness\"); }\n        }\n\n        public bool CursorIndicatorHideIfCustomCursorDefault = false;\n        public bool CursorIndicatorHideIfCustomCursor\n        {\n            get { return Or(i.cursorIndicatorHideIfCustomCursor, CursorIndicatorHideIfCustomCursorDefault); }\n            set { i.cursorIndicatorHideIfCustomCursor = value; OnSettingChanged(\"CursorIndicatorHideIfCustomCursor\"); }\n        }\n\n        public ButtonIndicatorType ButtonIndicatorDefault = ButtonIndicatorType.PicsAroundCursor;\n        public ButtonIndicatorType ButtonIndicator\n        {\n            get { return Or(i.buttonIndicator, ButtonIndicatorDefault); }\n            set { i.buttonIndicator = value; OnSettingChanged(\"ButtonIndicator\"); }\n        }\n\n        public double ButtonIndicatorScalingDefault = 1.0f;\n        public double ButtonIndicatorScaling\n        {\n            get { return Or(i.buttonIndicatorScaling, ButtonIndicatorScalingDefault); }\n            set { i.buttonIndicatorScaling = value; OnSettingChanged(\"ButtonIndicatorScaling\"); }\n        }\n\n        public double ButtonIndicatorPositionAngleDefault = 0f;\n        public double ButtonIndicatorPositionAngle\n        {\n            get { return Or(i.buttonIndicatorPositionAngle, ButtonIndicatorPositionAngleDefault); }\n            set { i.buttonIndicatorPositionAngle = value; OnSettingChanged(\"ButtonIndicatorPositionAngle\"); }\n        }\n\n        public double ButtonIndicatorPositionDistanceDefault = 56;\n        public double ButtonIndicatorPositionDistance\n        {\n            get { return Or(i.buttonIndicatorPositionDistance, ButtonIndicatorPositionDistanceDefault); }\n            set { i.buttonIndicatorPositionDistance = value; OnSettingChanged(\"ButtonIndicatorPositionDistance\"); }\n        }\n\n        public bool ButtonIndicatorShowModifiersDefault = true;\n        public bool ButtonIndicatorShowModifiers\n        {\n            get { return Or(i.buttonIndicatorShowModifiers, ButtonIndicatorShowModifiersDefault); }\n            set { i.buttonIndicatorShowModifiers = value; OnSettingChanged(\"ButtonIndicatorShowModifiers\"); }\n        }\n\n        public bool ButtonIndicatorUseCustomIconsDefault = false;\n        public bool ButtonIndicatorUseCustomIcons\n        {\n            get { return Or(i.buttonIndicatorUseCustomIcons, ButtonIndicatorUseCustomIconsDefault); }\n            set { i.buttonIndicatorUseCustomIcons = value; OnSettingChanged(\"ButtonIndicatorUseCustomIcons\"); }\n        }\n\n        public string ButtonIndicatorCustomIconsFolderDefault = \"\";\n        public string ButtonIndicatorCustomIconsFolder\n        {\n            get { return Or(i.buttonIndicatorCustomIconsFolder, ButtonIndicatorCustomIconsFolderDefault); }\n            set { i.buttonIndicatorCustomIconsFolder = value; OnSettingChanged(\"ButtonIndicatorCustomIconsFolder\"); }\n        }\n\n        public bool AddButtonEventsToHistoryDefault = false;\n        public bool AddButtonEventsToHistory\n        {\n            get { return Or(i.addButtonEventsToHistory, AddButtonEventsToHistoryDefault); }\n            set { i.addButtonEventsToHistory = value; OnSettingChanged(\"AddButtonEventsToHistory\"); }\n        }\n\n        public bool BackspaceDeletesText\n        {\n            get { return KeystrokeMethod == KeystrokeMethodEnum.TextModeBackspaceCanDeleteText; }\n        }\n\n        public bool PeriodicTopmostDefault = true;\n        public bool PeriodicTopmost\n        {\n            get { return Or(i.periodicTopmost, PeriodicTopmostDefault); }\n            set { i.periodicTopmost = value; OnSettingChanged(\"PeriodicTopmost\"); }\n        }\n\n        public bool EnableKeystrokeHistoryDefault = true;\n        public bool EnableKeystrokeHistory\n        {\n            get { return Or(i.enableKeystrokeHistory, EnableKeystrokeHistoryDefault); }\n            set { i.enableKeystrokeHistory = value; OnSettingChanged(\"EnableKeystrokeHistory\"); }\n        }\n\n        public String KeystrokeHistorySettingsModeShortcutDefault = \"RightCtrl + F12\";\n        public String KeystrokeHistorySettingsModeShortcut\n        {\n            get { return Or(i.keystrokeHistorySettingsModeShortcut, KeystrokeHistorySettingsModeShortcutDefault); }\n            set { i.keystrokeHistorySettingsModeShortcut = value; OnSettingChanged(\"KeystrokeHistorySettingsModeShortcut\"); }\n        }\n\n        public bool EnableSettingsModeDefault = false;\n        public bool EnableSettingsMode\n        {\n            get { return Or(i.enableSettingsMode, EnableSettingsModeDefault); }\n            set { i.enableSettingsMode = value; OnSettingChanged(\"EnableSettingsMode\"); }\n        }\n\n        public String KeystrokeHistoryPasswordModeShortcutDefault = \"RightCtrl + F11\";\n        public String KeystrokeHistoryPasswordModeShortcut\n        {\n            get { return Or(i.keystrokeHistoryPasswordModeShortcut, KeystrokeHistoryPasswordModeShortcutDefault); }\n            set { i.keystrokeHistoryPasswordModeShortcut = value; OnSettingChanged(\"KeystrokeHistoryPasswordModeShortcut\"); }\n        }\n\n        public bool EnablePasswordModeDefault = false;\n        public bool EnablePasswordMode\n        {\n            get { return Or(i.enablePasswordMode, EnablePasswordModeDefault); }\n            set { i.enablePasswordMode = value; OnSettingChanged(\"EnablePasswordMode\"); }\n        }\n\n        public KeystrokeMethodEnum KeystrokeMethodDefault = KeystrokeMethodEnum.TextModeBackspaceCanDeleteText;\n        public KeystrokeMethodEnum KeystrokeMethod\n        {\n            get\n            {\n                // Update compatability: Take over setting from depreciated backspaceDeletesText\n                if (!i.keystrokeMethod.HasValue && i.backspaceDeletesText.HasValue)\n                {\n                    if (i.backspaceDeletesText.Value)\n                    {\n                        return KeystrokeMethodEnum.TextModeBackspaceCanDeleteText;\n                    }\n                    else\n                    {\n                        return KeystrokeMethodEnum.TextMode;\n                    }\n                }\n                return Or(i.keystrokeMethod, KeystrokeMethodDefault);\n            }\n            set { i.keystrokeMethod = value; OnSettingChanged(\"KeystrokeMethod\"); }\n        }\n\n        public bool EnableAnnotateLineDefault = false;\n        public bool EnableAnnotateLine\n        {\n            get { return Or(i.enableAnnotateLine, EnableAnnotateLineDefault); }\n            set { i.enableAnnotateLine = value; OnSettingChanged(\"EnableAnnotateLine\"); }\n        }\n\n        public Color AnnotateLineColorDefault = Color.FromArgb(0xFF, 0x7E, 0xEF, 0x84);\n        public Color AnnotateLineColor\n        {\n            get { return Or(i.annotateLineColor, AnnotateLineColorDefault); }\n            set { i.annotateLineColor = new SerializableColor2(value); OnSettingChanged(\"AnnotateLineColor\"); }\n        }\n\n        public String AnnotateLineShortcutDefault = \"RightCtrl + F10\";\n        public String AnnotateLineShortcut\n        {\n            get { return Or(i.annotateLineShortcut, AnnotateLineShortcutDefault); }\n            set { i.annotateLineShortcut = value; OnSettingChanged(\"AnnotateLineShortcut\"); }\n        }\n\n        public bool AnnotateLineShortcutTrigger\n        {\n            set { OnSettingChanged(\"AnnotateLineShortcutTrigger\"); }\n        }\n\n        public String StandbyShortcutDefault = \"RightCtrl + F9\";\n        public String StandbyShortcut\n        {\n            get { return Or(i.standbyShortcut, StandbyShortcutDefault); }\n            set { i.standbyShortcut = value; OnSettingChanged(\"StandbyShortcut\"); }\n        }\n\n        public bool StartInStandbyDefault = false;\n        public bool StartInStandby\n        {\n            get { return Or(i.startInStandby, StartInStandbyDefault); }\n            set { i.startInStandby = value; OnSettingChanged(\"StartInStandby\"); }\n        }\n\n        public bool standby = true;\n        public bool Standby\n        {\n            get { return standby; }\n            set { standby = value; OnSettingChanged(\"Standby\"); }\n        }\n\n        public bool WelcomeOnStartupDefault = true;\n        public bool WelcomeOnStartup\n        {\n            get { return Or(i.welcomeOnStartup, WelcomeOnStartupDefault); }\n            set { i.welcomeOnStartup = value; OnSettingChanged(\"WelcomeOnStartup\"); }\n        }\n\n        // Add new settings also to method CallPropertyChangedForAllProperties()\n\n        #endregion\n\n        #region SettingsChangedEvent\n\n        public event PropertyChangedEventHandler PropertyChanged;\n\n        private void OnSettingChanged(string property)\n        {\n            dirty = true;\n            var watch = System.Diagnostics.Stopwatch.StartNew();\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));\n            watch.Stop();\n            Log.e(\"SETTING\", $\"elapsedMs = {watch.ElapsedMilliseconds}\");\n        }\n\n        public void CallPropertyChangedForAllProperties()\n        {\n            if (PropertyChanged != null) {\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LabelFont\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LabelColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"BackgroundColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LabelTextAlignment\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LabelTextDirection\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LabelAnimation\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"WindowLocation\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"WindowSize\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"PanelLocation\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"PanelSize\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"LineDistance\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"HistoryLength\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"HistoryTimeout\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableHistoryTimeout\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableWindowFade\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableCursorIndicator\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorOpacity\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorSize\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorFlashOnClick\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorClickColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorEdgeColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorDrawEdge\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorEdgeStrokeThickness\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"CursorIndicatorHideIfCustomCursor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicator\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorScalingPercentage\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorPositionAngle\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorPositionDistance\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorShowModifiers\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorUseCustomIcons\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"ButtonIndicatorCustomIconsFolder\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"AddButtonEventsToHistory\"));\n                // PropertyChanged(this, new PropertyChangedEventArgs(\"BackspaceDeletesText\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"PeriodicTopmost\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableKeystrokeHistory\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"KeystrokeHistorySettingsModeShortcut\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableSettingsMode\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"KeystrokeHistoryPasswordModeShortcut\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnablePasswordMode\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"KeystrokeMethod\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"EnableAnnotateLine\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"AnnotateLineColor\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"AnnotateLineShortcut\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"StandbyShortcut\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"StartInStandby\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"Standby\"));\n                PropertyChanged(this, new PropertyChangedEventArgs(\"WelcomeOnStartup\"));\n\n            }\n        }\n\n        #endregion\n\n        #region SaveAll and LoadAll\n\n\n        bool dirty = false;\n\n        public void SaveAll()\n        {\n            if (dirty)\n            {\n                try\n                {\n                    if (!Directory.Exists(Path.GetDirectoryName(configpath)))\n                    {\n                        Directory.CreateDirectory(Path.GetDirectoryName(configpath));\n                    }\n                    using (var stream = new FileStream(configpath, FileMode.Create, FileAccess.Write))\n                    {\n                        DataContractJsonSerializer ser =\n                            new DataContractJsonSerializer(typeof(Settings));\n\n                        ser.WriteObject(stream, i);\n                    }\n                }\n                catch (System.Security.SecurityException sx)\n                {\n                    System.Windows.MessageBox.Show(sx.Message);\n                    throw;\n                }\n            }\n            dirty = false;\n        }\n\n        public void LoadAll()\n        {\n            try\n            {\n                if (File.Exists(configpath))\n                {\n                    using (var stream = new FileStream(configpath, FileMode.Open, FileAccess.Read))\n                    {\n                        DataContractJsonSerializer ser =\n                            new DataContractJsonSerializer(typeof(Settings));\n\n                        try\n                        {\n                            i = (Settings)ser.ReadObject(stream);\n                        } catch\n                        {\n                            Log.e(\"SETTINGS\", \"Could not load settings, use default.\");\n                            i = new Settings();\n                        }\n                    }\n\n                }\n            }\n            catch (System.Security.SecurityException sx)\n            {\n                System.Windows.MessageBox.Show(sx.Message);\n                throw;\n            }\n            i.enableSettingsMode = false;\n            standby = StartInStandby;\n            dirty = false;\n        }\n\n        public void ResetAll()\n        {\n            i = new Settings();\n            dirty = true;\n            SaveAll();\n            standby = false;\n            CallPropertyChangedForAllProperties();\n        }\n\n        #endregion\n\n        public override string ToString()\n        {\n\n            return $@\"LabelFont:                       {LabelFont}\nBackgroundColor:                 {BackgroundColor}\nLabelTextAlignment:              {LabelTextAlignment}\nLabelTextDirection:              {LabelTextDirection}\nLabelAnimation:                  {LabelAnimation}\nLabelColor:                      {LabelColor}\nWindowLocation:                  {WindowLocation}\nWindowSize:                      {WindowSize}\nPanelLocation:                   {PanelLocation}\nPanelSize:                       {PanelSize}\nLineDistance:                    {LineDistance}\nHistoryLength:                   {HistoryLength}\nHistoryTimeout:                  {HistoryTimeout}\nEnableHistoryTimeout:            {EnableHistoryTimeout}\nEnableWindowFade:                {EnableWindowFade}\nEnableCursorIndicator:           {EnableCursorIndicator}\nCursorIndicatorOpacity:          {CursorIndicatorOpacity}\nCursorIndicatorSize:             {CursorIndicatorSize}\nCursorIndicatorColor:            {CursorIndicatorColor}\nCursorIndicatorFlashOnClick:     {CursorIndicatorFlashOnClick}\nCursorIndicatorClickColor:       {CursorIndicatorClickColor}\nCursorIndicatorDrawEdge:         {CursorIndicatorDrawEdge}\nCursorIndicatorEdgeColor:        {CursorIndicatorEdgeColor}\nCursorIndicatorEdgeStrokeThickness: {CursorIndicatorEdgeStrokeThickness}\nCursorIndicatorHideIfCustomCursor: {CursorIndicatorHideIfCustomCursor}\nButtonIndicator:                 {ButtonIndicator}\nbuttonIndicatorScalingPercentage:{ButtonIndicatorScaling}\nButtonIndicatorPositionAngle:    {ButtonIndicatorPositionAngle}\nButtonIndicatorPositionDistance: {ButtonIndicatorPositionDistance}\nButtonIndicatorShowModifiers:    {ButtonIndicatorShowModifiers}\nButtonIndicatorUseCustomIcons:   {ButtonIndicatorUseCustomIcons}\nButtonIndicatorCustomIconsFolder:{ButtonIndicatorCustomIconsFolder}\nAddButtonEventsToHistory:        {AddButtonEventsToHistory}\nBackspaceDeletesText:            {BackspaceDeletesText}\nPeriodicTopmost:                 {PeriodicTopmost}\nEnableKeystrokeHistory:          {EnableKeystrokeHistory}\nKeystrokeHistorySettingsModeShortcut: {KeystrokeHistorySettingsModeShortcut}\nEnableSettingsMode:             {EnableSettingsMode}\nKeystrokeHistoryPasswordModeShortcut: {KeystrokeHistoryPasswordModeShortcut}\nEnablePasswordMode:             {EnablePasswordMode}\nKeystrokeMethod:                {KeystrokeMethod}\nEnableAnnotateLine:             {EnableAnnotateLine}\nAnnotateLineColor:              {AnnotateLineColor}\nAnnotateLineShortcut:           {AnnotateLineShortcut}\nStandbyShortcut:                {StandbyShortcut}\nStartInStandby:                 {StartInStandby}\nStandby:                        {Standby}\nWelcomeOnStartup:               {WelcomeOnStartup}\n\";\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/SpecialkeysParser.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nnamespace KeyNStroke\n{\n    class SpecialkeysParser\n    {\n        public static string ToString(Key k)\n        {\n            switch(k){\n                case Key.LeftShift:\n                case Key.RightShift:\n                    return \"⇧\";\n                case Key.LeftCtrl:\n                case Key.RightCtrl:\n                    return \"Ctrl\";\n                case Key.LWin:\n                case Key.RWin:\n                    return \"Win\";\n                case Key.LeftAlt:\n                case Key.RightAlt:\n                    return \"Alt\";\n                case Key.CapsLock:\n                    return \"⇪\";\n                case Key.LineFeed:\n                case Key.Return:\n                    return \" ⏎\";\n                case Key.Back:\n                    return \" ⌫ \";\n                case Key.Left:\n                    return \" ← \";\n                case Key.Right:\n                    return \" → \";\n                case Key.Down:\n                    return \" ↓ \";\n                case Key.Up:\n                    return \" ↑ \";\n                case Key.Escape:\n                    return \" [Esc] \";\n                case Key.PrintScreen:\n                    return \" [Print] \";\n                case Key.Pause:\n                    return \" [Pause] \";\n                case Key.Insert:\n                    return \" [Insert] \";\n                case Key.Delete:\n                    return \" [Delete] \";\n\n\n                case Key.Tab:\n                    return \"↹\";\n                case Key.Space:\n                    return \"␣\";\n                case Key.PageUp: \n                    return \" ↖ \";\n                case Key.PageDown:\n                     return \" ↘ \";\n                case Key.End:\n                    return \" ⇲ \";\n                case Key.Home:\n                    return \" ⇱ \";\n                case Key.Print:\n                    return \" ⎙ \";\n\n                case Key.Clear:\n                case Key.ImeProcessed:\n                case Key.Attn:\n                case Key.CrSel:\n                case Key.ExSel:\n                case Key.EraseEof:\n                case Key.Cancel:\n                case Key.Select:\n                case Key.Execute:\n                case Key.Help:\n                case Key.Apps:\n                case Key.Pa1:\n                case Key.Sleep:\n                    return \" [\" + k.ToString() + \"] \";\n\n            \n           \n                //case Key.OemSemicolon: //  Key.Oem1 // Let ToUnicode handle this character\n                //    return \";\";\n                //case Key.OemComma: // Let ToUnicode handle this character\n                //    return \",\";\n                //case Key.OemQuestion: // Key.Oem2 // Let ToUnicode handle this character\n                //    return \"?\";\n                //case Key.OemTilde: // Key.Oem3   // Let ToUnicode handle this character\n                //    return \"~\";\n                //case Key.AbntC1:\n                //case Key.AbntC2:\n                //case Key.OemOpenBrackets:\n                //case Key.OemCloseBrackets:\n                //case Key.OemQuotes: // Key.Oem7 // Let ToUnicode handle this character\n                //    return \"\\\"\";\n\n                //case Key.Oem102:\n                //case Key.OemPipe: // Key.Oem5  // Let ToUnicode handle this character\n                //    return \"|\";\n                // case Key.OemBackslash: // Let ToUnicode handle this character\n                //    return \"\\\\\";\n\n\n                //case Key.Multiply:  // Let ToUnicode handle this character\n                //    return \"*\";\n                //case Key.Add:  // Let ToUnicode handle this character\n                //    return \"+\";\n                case Key.Separator:\n                    return \" [Seperator] \";\n                //case Key.Subtract:\n                //case Key.OemMinus: // Let ToUnicode handle this character\n                //    return \"-\";\n                //case Key.OemPeriod:\n                //case Key.Decimal: // Let ToUnicode handle this character\n                //    return \".\";\n                //case Key.Divide: // Let ToUnicode handle this character\n                //    return \"/\";\n                case Key.NumLock:\n                    return \" [NumLock] \";\n                case Key.Scroll:\n                    return \" [ScrollLock] \";\n\n                case Key.BrowserBack:\n                    return \" [🌐⇦] \";\n                case Key.BrowserForward:\n                    return \" [🌐⇨] \";\n                case Key.BrowserRefresh:\n                    return \" [🌐↻] \";\n                case Key.BrowserStop:\n                    return \" [🌐✋] \";\n                case Key.BrowserSearch:\n                    return \" [🌐🔎] \";\n                case Key.BrowserFavorites:\n                    return \" [🌐★] \";\n                case Key.BrowserHome:\n                    return \" [🌐⌂] \";\n\n          \n                case Key.VolumeMute:\n                    return \" 🔇 \";\n                case Key.VolumeDown:\n                    return \" 🔉⏬ \";\n                case Key.VolumeUp:\n                    return \" 🔊⏫ \";\n                case Key.MediaNextTrack:\n                    return \" ⏭ \";\n                case Key.MediaPreviousTrack:\n                    return \" ⏮ \";\n                case Key.MediaStop:\n                    return \" ◼ \";\n                case Key.MediaPlayPause:\n                    return \" ⏯ \";\n                case Key.LaunchMail:\n                    return \" 📧 \";\n                case Key.SelectMedia:\n                    return \" ♪ \";\n                case Key.LaunchApplication1:\n                    return \" ① \";\n                case Key.LaunchApplication2:\n                    return \" ② \";\n\n                case Key.Play:\n                    return \" ▶ \";\n                case Key.Zoom:\n                    return \" [🔎±] \";\n                \n\n            }\n            if(Key.F1 <= k && k <= Key.F24)\n                return \" \" + k.ToString() + \" \";\n\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Themes/Generic.xaml",
    "content": "<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"clr-namespace:KeyNStroke\">\n\n\n    <Style TargetType=\"{x:Type local:ResizeButton}\">\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type local:ResizeButton}\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\">\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n</ResourceDictionary>\n"
  },
  {
    "path": "KeyNStroke/TweenLabel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Forms;\nusing KeyNStroke;\n\nnamespace KeyNStroke\n{\n    class TweenLabel : Control\n    {\n        #region Recycling\n\n        static Stack<TweenLabel> unusedLabels = new Stack<TweenLabel>(15);\n        \n        static int debug_nextId = 1;\n        int debug_id;\n\n        void PrintDebug(String msg)\n        {\n            Log.e(\"TL\", debug_id.ToString() + \" y:\" + this.Location.Y + \" '\" + this.Text + \"' \" + msg);\n        }\n\n        public static TweenLabel getNewLabel(Form form, SettingsStore s)\n        {\n            if (unusedLabels.Count > 0)\n            {\n                return unusedLabels.Pop().Init(form, s);\n            }\n            TweenLabel t = new TweenLabel().Init(form, s);\n            t.debug_id = debug_nextId;\n            debug_nextId += 1;\n            return t;\n        }\n\n        public void Recycle()\n        {\n            PrintDebug(\"recyle\");\n            this.Parent.Controls.Remove(this);\n            this.Parent = null;\n            this.Visible = false;\n            attachedToTimer = false;\n            if (TweenLabel.moveTimer != null)\n                TweenLabel.moveTimer.Tick -= T_Move;\n            this.historyTimeout = null;\n            TweenLabel.unusedLabels.Push(this);\n        }\n\n        #endregion\n\n        SettingsStore settings;\n\n        private TweenLabel Init(Form form, SettingsStore s)\n        {\n            PrintDebug(\"revive\");\n            if (settings == null)\n            {\n                settings = s;\n                settings.PropertyChanged += settingChanged;\n                this.TextChanged += TweenLabel_TextChanged;\n                //this.Font = new Font(settings.LabelFont.;\n                //this.ForeColor = settings.TextColor;\n                this.DoubleBuffered = true;\n            }\n\n            return this;\n        }\n\n        void TweenLabel_TextChanged(object sender, EventArgs e)\n        {\n            ResetHistoryTimeoutTimer();\n        }\n\n\n        private void settingChanged(object sender, PropertyChangedEventArgs e)\n        {\n            switch (e.PropertyName)\n            {\n                case \"LabelFont\":\n                    //this.Font = settings.LabelFont;\n                    break;\n                case \"TextColor\":\n                    //this.ForeColor = settings.TextColor;\n                    break;\n                case \"HistoryTimeout\":\n                    ResetHistoryTimeoutTimer();\n                    break;\n            }\n        }\n\n        public void tweenFadeIn()\n        {\n            PrintDebug(\"fade in start\");\n            this.Opacity = 0;\n            this.Visible = true;\n            Timer T = new Timer();\n            T.Tick += T_FadeIn;\n            T.Interval = 40; // bit more than 30 Hz\n            T.Start();\n        }\n\n        void T_FadeIn(object sender, EventArgs e)\n        {\n            this.Opacity += 0.12f;\n            if (Opacity >= 1)\n            {\n                Timer T = (Timer)sender;\n                T.Stop();\n                Opacity = 1;\n                PrintDebug(\"fade in finished\");\n                ResetHistoryTimeoutTimer();\n            }\n            this.Refresh();\n        }\n\n        Timer fadeOutTimer;\n        Func<TweenLabel, bool> fadeOutOnFinish;\n\n        public void FadeOutAndRecycle(Func<TweenLabel, bool> onFinish)\n        {\n            CancelTimeoutTimer();\n            if (fadeOutTimer == null)\n            {\n                fadeOutTimer = new Timer();\n                fadeOutTimer.Tick += T_FadeOut;\n            }\n            if (!fadeOutTimer.Enabled)\n            {\n                this.Opacity = 1;\n                fadeOutOnFinish = onFinish;\n                fadeOutTimer.Interval = 40; // bit more than 30 Hz\n                PrintDebug(\"fade out start\");\n                fadeOutTimer.Start();\n                CancelTimeoutTimer();\n            }\n        }\n\n        void T_FadeOut(object sender, EventArgs e)\n        {\n            this.Opacity -= 0.04f;\n            if (Opacity <= 0)\n            {\n                fadeOutTimer.Stop();\n                Opacity = 0;\n                if (fadeOutOnFinish != null)\n                {\n                    fadeOutOnFinish(this);\n                    fadeOutOnFinish = null;\n                }\n                PrintDebug(\"fade out finish\");\n                this.Recycle();\n                return;\n            }\n            this.Refresh();\n        }\n\n        bool IsFadingOut()\n        {\n            return fadeOutTimer != null && fadeOutTimer.Enabled;\n        }\n\n        Point moveStartLocation;\n        Point moveDir;\n        float movePercent;\n        bool attachedToTimer = false;\n        static Timer moveTimer; // one timer for all for synchronous\n        static int moveTimerUserCount = 0;\n\n        public void TweenMove(Point newMoveDir)\n        {\n            if (moveTimer == null)\n            {\n                TweenLabel.moveTimer = new Timer();\n                TweenLabel.moveTimer.Interval = 40; // bit more than 30 Hz\n                TweenLabel.moveTimer.Start();\n            }\n            if (!attachedToTimer)\n            {\n                TweenLabel.moveTimer.Tick += T_Move;\n                TweenLabel.moveTimerUserCount++;\n                Log.e(\"TLMove\", \"Handler attached\");\n\n                movePercent = 0;\n                moveStartLocation = this.Location;\n                moveDir = newMoveDir;\n\n                attachedToTimer = true;\n            }\n            else\n            {\n                movePercent = (float)moveDir.Y * movePercent / ((float)newMoveDir.Y + moveDir.Y);\n                moveDir = Point.Add(moveDir, new Size(newMoveDir));\n            }\n            \n        }\n\n        void T_Move(object sender, EventArgs e)\n        {\n            movePercent += 0.2f;\n            if (movePercent >= 1)\n            {\n                Timer T = (Timer)sender;\n                T.Tick -= T_Move;\n                Log.e(\"TLMove\", \"Handler removed\");\n\n\n                attachedToTimer = false;\n                moveTimerUserCount -= 1;\n                this.Location = Point.Add(moveStartLocation, new Size(moveDir));\n                return;\n            }\n            this.Location = Point.Add(moveStartLocation, new Size((int)(moveDir.X * movePercent), (int)(moveDir.Y * movePercent)));\n        }\n\n        float opacity = 1f;\n\n        public float Opacity\n        {\n            get { return opacity; }\n            set { opacity = value; }\n        }\n\n        public void DrawString(System.Drawing.Graphics G)\n        {\n            Color C = Color.FromArgb(Math.Min((int)(opacity * 255f), 255), this.ForeColor);\n            SolidBrush drawBrush = new SolidBrush(C);\n            StringFormat drawFormat = new StringFormat();\n            SizeF StringSize = G.MeasureString(this.Text, this.Font);\n            int x = 0;\n            int y = 0;\n\n            switch (settings.LabelTextAlignment)\n            {\n                case TextAlignment.Left:\n                    x = 0;\n                    break;\n                case TextAlignment.Right:\n                    x = this.Width - (int)StringSize.Width;\n                    break;\n                case TextAlignment.Center:\n                    x = (int)(((float)this.Width - StringSize.Width) / 2.0f);\n                    break;\n            }\n\n            // vertical center\n            y = (int)(((float)this.Height - StringSize.Height) / 2.0f);\n\n            using (Bitmap buffer = new Bitmap(this.Width, this.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))\n            {\n                using (Graphics graphics = Graphics.FromImage(buffer))\n                {\n                    TextRenderer.DrawText(graphics, this.Text, this.Font, new Point(x, y),\n                                    C, Color.Transparent, TextFormatFlags.NoPadding);\n\n                }\n\n                ColorMatrix matrix = new ColorMatrix();\n                matrix.Matrix33 = opacity;\n                ImageAttributes attributes = new ImageAttributes();\n                attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);\n\n                //now draw the image  \n                G.DrawImage(buffer, new Rectangle(0, 0, buffer.Width, buffer.Height), 0, 0, buffer.Width, buffer.Height, GraphicsUnit.Pixel, attributes);\n                //G.DrawImageUnscaled(buffer, this.ClientRectangle);\n\n            }\n\n            //TextRenderer.DrawText(G, this.Text, this.Font, new Point(x, y),\n            //    C, Color.Transparent, TextFormatFlags.NoPadding);\n            \n            //G.DrawString(this.Text, this.Font, drawBrush, x, y, drawFormat);\n            drawBrush.Dispose();\n\n            // Declare and instantiate a new pen.\n            //System.Drawing.Pen myPen = new System.Drawing.Pen(Color.Aqua);\n\n            // Draw an aqua rectangle in the rectangle represented by the control.\n            //G.DrawRectangle(myPen, new Rectangle(new Point(0, 0),\n            //   Size.Subtract(this.Size, new Size(1, 1))));\n        }\n\n        public bool AddingWouldFit(string additionalChars)\n        {\n            //SizeF StringSize = this.CreateGraphics().MeasureString(\n            //                        this.Text + additionalChars, this.Font);\n            Size StringSize2 = TextRenderer.MeasureText(this.CreateGraphics(),\n                                    this.Text, this.Font, this.Size, TextFormatFlags.NoPadding);\n\n            //return StringSize.Width < (this.Width - 2);\n            return StringSize2.Width < (this.Width - 25);\n        }\n\n        protected override void OnPaint(PaintEventArgs e)\n        {\n            //base.OnPaint(e);\n            DrawString(e.Graphics);\n        }\n\n        Timer timeoutTimer;\n        public delegate void HistoryTimeOutEvent(TweenLabel l);\n        public event HistoryTimeOutEvent historyTimeout;\n\n        private void ResetHistoryTimeoutTimer()\n        {\n            PrintDebug(\"reset history timeout\");\n            CancelTimeoutTimer();\n            if (!settings.EnableHistoryTimeout || IsFadingOut())\n                return;\n            if (timeoutTimer == null)\n            {\n                timeoutTimer = new Timer();\n                timeoutTimer.Tick += timeoutTimer_Tick;\n            }\n\n            timeoutTimer.Interval = (int) (settings.HistoryTimeout * 1000);\n            timeoutTimer.Start();\n        }\n\n        private void CancelTimeoutTimer()\n        {\n            if (timeoutTimer != null)\n            {\n                timeoutTimer.Stop();\n            }\n        }\n\n        void timeoutTimer_Tick(object sender, EventArgs e)\n        {\n            PrintDebug(\"fire history timeout\");\n            CancelTimeoutTimer();\n            if (historyTimeout != null)\n            {\n                historyTimeout.Invoke(this);\n            }\n            FadeOutAndRecycle(null);\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/UIHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Media;\nusing MColor = System.Windows.Media.Color;\nusing DColor = System.Drawing.Color;\n\n\nnamespace KeyNStroke\n{\n    class UIHelper\n    {\n        #region Helper to convert Drawing.Color to Windows.Media.Color\n        public static MColor ToMediaColor(DColor color)\n        {\n            return MColor.FromArgb(color.A, color.R, color.G, color.B);\n        }\n\n        public static DColor ToDrawingColor(MColor color)\n        {\n            return DColor.FromArgb(color.A, color.R, color.G, color.B);\n        }\n        #endregion\n\n        /// <summary>\n        /// Finds a Child of a given item in the visual tree. \n        /// </summary>\n        /// <param name=\"parent\">A direct parent of the queried item.</param>\n        /// <typeparam name=\"T\">The type of the queried item.</typeparam>\n        /// <param name=\"childName\">x:Name or Name of child. </param>\n        /// <returns>The first parent item that matches the submitted type parameter. \n        /// If not matching item can be found, \n        /// a null parent is being returned.</returns>\n        public static T FindChild<T>(DependencyObject parent, string childName)\n           where T : DependencyObject\n        {\n            // Confirm parent and childName are valid. \n            if (parent == null) return null;\n\n            if (parent is FrameworkElement)\n                ((FrameworkElement)parent).ApplyTemplate();\n\n            T foundChild = null;\n\n            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);\n            for (int i = 0; i < childrenCount; i++)\n            {\n                var child = VisualTreeHelper.GetChild(parent, i);\n                // If the child is not of the request child type child\n                T childType = child as T;\n                if (childType == null)\n                {\n                    // recursively drill down the tree\n                    foundChild = FindChild<T>(child, childName);\n\n                    // If the child is found, break so we do not overwrite the found child. \n                    if (foundChild != null) break;\n                }\n                else if (!string.IsNullOrEmpty(childName))\n                {\n                    var frameworkElement = child as FrameworkElement;\n                    // If the child's name is set for search\n                    if (frameworkElement != null && frameworkElement.Name == childName)\n                    {\n                        // if the child's name is of the request name\n                        foundChild = (T)child;\n                        break;\n                    }\n                }\n                else\n                {\n                    // child element found.\n                    foundChild = (T)child;\n                    break;\n                }\n            }\n\n            return foundChild;\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Updater/Admininstration.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Reflection;\nusing System.Security.Cryptography;\nusing System.Xml;\n\nnamespace KeyNStroke.Updater\n{\n    class Admininstration\n    {\n        // Files for key creation and testing (all in current working directory)\n        const string PATH_PUB_KEY = \"updateKey.pub.xml\";\n        const string PATH_PRIV_KEY = \"updateKey.priv.xml\";\n        const string PATH_UPDATE_MANIFEST = \"updateManifest.xml\";\n\n        /// <summary>\n        /// Checks the command line and executes administrative functions if the parameter is given.\n        /// Returns true if the program should exit.\n        /// </summary>\n        /// <param name=\"args\"></param>\n        static public bool HandleArgs(string[] args)\n        {\n            if (args.Length > 0)\n            {\n                if (args[0] == \"--create-signing-keypair\")\n                {\n                    CreateSigningKeyPair();\n                    return true;\n                }\n                if (args[0] == \"--create-update-manifest\")\n                {\n                    CreateUpdateManifest();\n                    return true;\n                }\n                if (args[0] == \"--sign-update-manifest\")\n                {\n                    SignUpdateManifest();\n                    return true;\n                }\n                if (args[0] == \"--test-update\")\n                {\n                    DownloadAndVerifyManifestAndUpdate();\n                    return true;\n                }\n                if (args[0] == \"--test-signing\")\n                {\n                    TestSigning();\n                    return true;\n                }\n            }\n            return false;\n        }\n\n\n        /// <summary>\n        /// Creates a new RSA key and saves it next to the executable.\n        /// Put the public key into the Resources folder and bake it into the executable.\n        /// Store the private key somewhere and never give it away.\n        /// </summary>\n        static void CreateSigningKeyPair()\n        {\n\n            RSA rsaKey = RSA.Create();\n            string pub = rsaKey.ToXmlString(false);\n            string priv = rsaKey.ToXmlString(true);\n\n            using (StreamWriter outputFile = new StreamWriter(PATH_PUB_KEY))\n            {\n                outputFile.Write(pub);\n            }\n\n            using (StreamWriter outputFile = new StreamWriter(PATH_PRIV_KEY))\n            {\n                outputFile.Write(priv);\n            }\n        }\n\n        /// <summary>\n        /// Load the private key from the current directory.\n        /// </summary>\n        /// <returns>The private key.</returns>\n        static RSA GetPrivateKey()\n        {\n            RSA priv = RSA.Create();\n            using (var sr = new StreamReader(PATH_PRIV_KEY))\n            {\n                priv.FromXmlString(sr.ReadToEnd());\n            }\n            return priv;\n        }\n\n        /// <summary>\n        /// Verifies that the embedded public key can be used to verify documents \n        /// signed with the private key found in the working directory.\n        /// The test result is written to the Colsole.\n        /// </summary>\n        static void TestSigning()\n        {\n            String dirOfExecutable = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);\n            String tmpDocumentPath = Path.Combine(dirOfExecutable, \"test.xml\");\n\n            // Sign a test document with the private key and serialize the document to the disk\n            {\n                RSA priv = GetPrivateKey();\n                string xmlmessage = @\"\n                    <root>  \n                        <creditcard>\n                            <number>19834209</number>\n                            <expiry>02/02/2002</expiry>\n                        </creditcard>\n                    </root> \";\n                XmlDocument xmlDoc = new XmlDocument\n                {\n                    PreserveWhitespace = true\n                };\n                xmlDoc.LoadXml(xmlmessage);\n                Utils.SignXml(xmlDoc, priv);\n                // xmlDoc.GetElementsByTagName(\"number\")[0].InnerText = \"123123\";\n                // xmlDoc.GetElementsByTagName(\"root\")[0].AppendChild(xmlDoc.CreateNode(\"element\", \"test\", \"\"));\n                xmlDoc.Save(tmpDocumentPath);\n            }\n\n            // Load the test document from the disk and verify the signature with the embedded public key\n            {\n                XmlDocument xmlDoc = new XmlDocument\n                {\n                    PreserveWhitespace = true\n                };\n                xmlDoc.Load(tmpDocumentPath);\n                try\n                {\n                    Utils.VerifyXml(xmlDoc, Utils.GetEmbeddedPubKey());\n                    Console.WriteLine(\"The XML signature is valid.\");\n                    Console.WriteLine(xmlDoc.OuterXml);\n                }\n                catch (Exception e)\n                {\n                    Console.WriteLine(\"The XML signature is not valid.\");\n                    Console.WriteLine(e.Message);\n                    Console.WriteLine(xmlDoc.OuterXml);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Creates a manifest that can be used to update to the current executed assembly.\n        /// The manifest is saved to the current working directory.\n        /// The manifest is not signed so the user can set the info etc.\n        /// </summary>\n        static void CreateUpdateManifest()\n        {\n            string executable = System.Reflection.Assembly.GetEntryAssembly().Location;\n            string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3);\n            string hash = Utils.SHA256OfFile(executable);\n\n            string manifest = $@\"<manifest>\n    <update>\n        <version>{version}</version>\n        <date>{DateTime.UtcNow:s}</date>\n        <info>New release with bug fixes and new features</info>\n        <download>https://github.com/Phaiax/Key-n-Stroke/raw/master/Releases/{version}/{Path.GetFileName(executable)}</download>\n        <sha256>{hash}</sha256>\n    </update>\n</manifest>\";\n\n            XmlDocument xmlDoc = new XmlDocument\n            {\n                PreserveWhitespace = true\n            };\n            xmlDoc.LoadXml(manifest);\n            xmlDoc.Save(PATH_UPDATE_MANIFEST);\n        }\n\n        /// <summary>\n        /// Signs the manifest in the current working directory.\n        /// </summary>\n        static void SignUpdateManifest()\n        {\n            XmlDocument xmlDoc = new XmlDocument\n            {\n                PreserveWhitespace = true\n            };\n            xmlDoc.Load(PATH_UPDATE_MANIFEST);\n            Utils.SignXml(xmlDoc, GetPrivateKey());\n            xmlDoc.Save(PATH_UPDATE_MANIFEST);\n            Console.WriteLine($\"scp {PATH_UPDATE_MANIFEST} $SSHSERVER:html/key-n-stroke/\");\n\n            string executable = System.Reflection.Assembly.GetEntryAssembly().Location;\n            Console.WriteLine($\"scp /{executable.Replace(\"\\\\\", \"/\").Replace(\":\", \"\")} $SSHSERVER:html/key-n-stroke/\");\n        }\n\n        /// <summary>\n        /// Download and verify the manifest and the executable.\n        /// </summary>\n        static void DownloadAndVerifyManifestAndUpdate()\n        {\n            try\n            {\n                Console.WriteLine(\"Downloading manifest ...\");\n\n                XmlDocument manifest = Utils.DownloadAndVerifyManifest();\n                Console.WriteLine(\"Downloading update ...\");\n                byte[] data = Utils.DownloadExecutableAndVerifyHash(manifest, (e) =>\n                {\n                    Console.Write($\"\\r {e}%\");\n                }, new TimeSpan(0, 0, 10));\n                Console.WriteLine();\n                Console.WriteLine($\"Manifest and update ({data.Length} bytes) are ok.\");\n            }\n            catch (Exception e)\n            {\n                Console.WriteLine(\"ERROR:\");\n                Console.WriteLine(e.ToString());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Updater/Statemachine.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Windows;\nusing System.Xml;\n\nnamespace KeyNStroke.Updater\n{\n\n    /// <summary>\n    /// This implements a singleton statemachine that handles the update process.\n    /// \n    /// There is an API to connect this statemachine to one or more UIs.\n    /// The UIs are synchronized, so any UI is allowed to trigger actions,\n    /// and results will be displayed in all connected UIs at once.\n    /// \n    /// The UI needs to provide the following widgets:\n    ///      - A button with variable text that can be disabled.\n    ///      - A oneline TextBlock for error/progress information.\n    /// The UI needs to implement the following functionality:\n    ///      - Call Updater.Instance.UiButton_Click() on Button click.\n    ///      - Subscribe to the event Updater.Instance.onUiUpdate.\n    ///      - On every event, update the UI elements according to the parameter <code>UiUpdateEventArgs</code> of this event.\n    ///      - Call Updater.Instance.UiRefresh() once after setting up the event handler.\n    ///      - Handle the fact that the callback calls from a different thread.\n    ///      - Subscribe to the event Updater.Instance.onShutdownRequest and shutdown the application if this event occurs.\n    /// </summary>\n    public class Statemachine\n    {\n\n        #region Public API\n\n        /// <summary>\n        /// The UI state.\n        /// One button and one oneline TextBlock must be available on each GUI that wants to provide a GUI for update functionality.\n        /// (We abstract the UI state into this struct so we can embed the update functionality into multiple windows.)\n        /// </summary>\n        public class UiUpdateEventArgs\n        {\n            public string buttonText;\n            public bool buttonEnabled;\n            public string info;\n            /// <summary>\n            /// If this has more than 0 items the info shall be displayed as a hyperlink. On clicking the hyperlink, the details\n            /// shall be displayed in a message window.\n            /// </summary>\n            public List<string> details;\n        }\n\n        public delegate void UiUpdateEventHandler(object sender, UiUpdateEventArgs e);\n        public delegate void ShutdownRequestEventHandler(object sender);\n\n        /// <summary>\n        /// Event that is triggered whenever the UI should change or if UiRefresh is called.\n        /// </summary>\n        public event UiUpdateEventHandler OnUiUpdate;\n\n\n        /// <summary>\n        /// Event that is triggered when an update is impending and the application must exit immediately.\n        /// </summary>\n        public event ShutdownRequestEventHandler OnShutdownRequest;\n\n        /// <summary>\n        /// A function that should be called if the UI button is clicked.\n        /// </summary>\n        /// <param name=\"sender\">Can be null.</param>\n        /// <param name=\"e\">Can be null.</param>\n        public void UiButton_Click(object sender, RoutedEventArgs e)\n        {\n            switch (updateState)\n            {\n                case UpdateState.ManifestDownloadTriggerable:\n                case UpdateState.NoUpdateAvailable:\n                case UpdateState.ManifestDownloadFailed:\n                case UpdateState.ManifestVerificationFailed:\n                case UpdateState.UpdateVerificationFailed:\n                case UpdateState.UpdateDownloadFailed:\n                    updateCommands.Add(UpdateCommands.DownloadManifest);\n                    break;\n                case UpdateState.UpdateAvailable:\n                    updateCommands.Add(UpdateCommands.DownloadUpdate);\n                    break;\n                case UpdateState.ManifestDownloading:\n                case UpdateState.UpdateDownloading:\n                case UpdateState.UpdateApplicationImpending:\n                    break;\n            }\n        }\n\n        const String UI_CHECK_FOR_UPDATES = \"Check for updates\";\n        const String UI_NO_UPDATE_AVAILABLE = \"No update available\";\n        const String UI_CHECKING_FOR_UPDATES = \"Checking for updates...\";\n        const String UI_MANIFEST_DOWNLOAD_FAILED = \"Manifest download failed.\";\n        const String UI_MANIFEST_VERIFICATION_FAILED = \"Manifest verification failed.\";\n        const String UI_DO_UPDATE_TO_VERSION = \"Update to version\";\n        const String UI_WHATS_NEW_IN_VERSION = \"What's new in version\";\n        const String UI_UPDATE_DOWNLOAD_IN_PROGRESS = \"Dowloading...\";\n        const String UI_UPDATE_DOWNLOAD_FAILED = \"Update download failed.\";\n        const String UI_UPDATE_VERIFICATION_FAILED = \"Update verification failed.\";\n        const String UI_UPDATE_IMPENDING = \"Installing... The application will restart in a few moments.\";\n\n        /// <summary>\n        /// Trigger the UiUpdateEventHandler event. This should be called after a new window connected its UI to the updater.\n        /// </summary>\n        public void UiRefresh()\n        {\n            UiUpdateEventArgs eargs = new UiUpdateEventArgs\n            {\n                info = \"\",\n                details = new List<string>(0)\n        };\n            switch (updateState)\n            {\n                case UpdateState.ManifestDownloadTriggerable:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    break;\n                case UpdateState.ManifestDownloading:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = false;\n                    eargs.info = UI_CHECKING_FOR_UPDATES;\n                    break;\n                case UpdateState.ManifestDownloadFailed:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_MANIFEST_DOWNLOAD_FAILED;\n                    eargs.details.Add(lastErrorMessage);\n                    break;\n                case UpdateState.ManifestVerificationFailed:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_MANIFEST_VERIFICATION_FAILED;\n                    eargs.details.Add(lastErrorMessage);\n                    break;\n                case UpdateState.NoUpdateAvailable:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_NO_UPDATE_AVAILABLE;\n                    break;\n                case UpdateState.UpdateAvailable:\n                    eargs.buttonText = UI_DO_UPDATE_TO_VERSION + \" \" + nextVersion;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_WHATS_NEW_IN_VERSION + \" \" + nextVersion;\n                    eargs.details.Add(nextVersionInfo);\n                    break;\n                case UpdateState.UpdateDownloading:\n                    eargs.buttonText = UI_DO_UPDATE_TO_VERSION + \" \" + nextVersion;\n                    eargs.buttonEnabled = false;\n                    eargs.info = UI_UPDATE_DOWNLOAD_IN_PROGRESS + \" (\" + downloadPercentage.ToString() + \"%)\";\n                    break;\n                case UpdateState.UpdateVerificationFailed:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_UPDATE_VERIFICATION_FAILED;\n                    eargs.details.Add(lastErrorMessage);\n                    break;\n                case UpdateState.UpdateDownloadFailed:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = true;\n                    eargs.info = UI_UPDATE_DOWNLOAD_FAILED;\n                    eargs.details.Add(lastErrorMessage);\n                    break;\n                case UpdateState.UpdateApplicationImpending:\n                    eargs.buttonText = UI_CHECK_FOR_UPDATES;\n                    eargs.buttonEnabled = false;\n                    eargs.info = UI_UPDATE_IMPENDING;\n                    break;\n                default:\n                    break;\n            }\n\n            OnUiUpdate?.Invoke(this, eargs);\n        }\n\n        #endregion\n\n        #region Singleton\n\n        private static readonly Statemachine instance = new Statemachine();\n\n        /// <summary>\n        /// Singleton\n        /// </summary>\n        public static Statemachine Instance\n        {\n            get { return instance; }\n        }\n\n        #endregion\n\n        #region Constructor, Thread start\n\n        private readonly Thread updateThread;\n\n        private Statemachine()\n        {\n            updateThread = new Thread(new ThreadStart(UpdateThread));\n            updateThread.IsBackground = true;\n            updateThread.Start();\n        }\n\n        #endregion\n\n        #region Variables \n\n        /// <summary>\n        /// A queue for communication from UI to the Update Thread\n        /// </summary>\n        readonly BlockingCollection<UpdateCommands> updateCommands = new BlockingCollection<UpdateCommands>(new ConcurrentQueue<UpdateCommands>());\n\n        #region Infos for User/UI\n\n        /// <summary>\n        /// The latest error message.\n        /// </summary>\n        string lastErrorMessage = \"\";\n\n        /// <summary>\n        /// The download progress during state UpdateDownloading\n        /// </summary>\n        int downloadPercentage = 0;\n\n        /// <summary>\n        /// The version number of the available update.\n        /// </summary>\n        string nextVersion = \"\";\n\n        /// <summary>\n        /// The changelog for the available update.\n        /// </summary>\n        string nextVersionInfo = \"\";\n\n        #endregion\n\n        #endregion\n\n\n\n\n        private UpdateState updateState;\n\n        public enum UpdateState\n        {\n            ManifestDownloadTriggerable,\n            ManifestDownloading,\n            ManifestDownloadFailed,\n            ManifestVerificationFailed, // like ManifestDownloadTriggerable, but with error message\n            NoUpdateAvailable,\n            UpdateAvailable,\n            UpdateDownloading,\n            UpdateVerificationFailed, // like ManifestDownloadTriggerable, but with error message\n            UpdateDownloadFailed,\n            UpdateApplicationImpending,\n        }\n\n        private enum UpdateCommands\n        {\n            DownloadManifest,\n            DownloadUpdate\n        }\n\n        void UpdateThread()\n        {\n            XmlDocument verifiedManifest = null;\n            byte[] update_exe = null;\n\n            while (true)\n            {\n                try\n                {\n                    // Blocks until at least one new Item is available\n                    UpdateCommands command = updateCommands.Take();\n\n                    // Only execute the last command in the queue, discard all others\n                    while (updateCommands.Count > 0)\n                    {\n                        command = updateCommands.Take();\n                    }\n\n                    switch (updateState)\n                    {\n                        case UpdateState.ManifestDownloadTriggerable:\n                        case UpdateState.ManifestDownloadFailed:\n                        case UpdateState.ManifestVerificationFailed:\n                        case UpdateState.UpdateVerificationFailed:\n                        case UpdateState.UpdateDownloadFailed:\n                        case UpdateState.NoUpdateAvailable:\n                            if (command == UpdateCommands.DownloadManifest)\n                            {\n                                updateState = UpdateState.ManifestDownloading;\n                                UiRefresh();\n                                try\n                                {\n                                    XmlDocument m = Utils.DownloadAndVerifyManifest();\n                                    if (Utils.CanUpdate(m))\n                                    {\n                                        updateState = UpdateState.UpdateAvailable;\n                                        nextVersion = m.SelectSingleNode(\"manifest/update/version\").InnerText;\n                                        nextVersionInfo = m.SelectSingleNode(\"manifest/update/info\").InnerText;\n                                        verifiedManifest = m;\n                                    }\n                                    else\n                                    {\n                                        updateState = UpdateState.NoUpdateAvailable;\n                                    }\n                                }\n                                catch (Utils.VerificationException e)\n                                {\n                                    updateState = UpdateState.ManifestVerificationFailed;\n                                    lastErrorMessage = \"Verification failed: \\n\" + e.Message;\n                                }\n                                catch (Utils.DownloadFailedException e)\n                                {\n                                    updateState = UpdateState.ManifestDownloadFailed;\n                                    lastErrorMessage = \"Download failed: \\n\" + e.Message;\n                                }\n                                catch (Exception e)\n                                {\n                                    updateState = UpdateState.ManifestDownloadFailed;\n                                    lastErrorMessage = \"Unexpected error: \\n\" + e.Message;\n                                }\n                            }\n                            UiRefresh();\n                            break;\n                        case UpdateState.ManifestDownloading:\n                        case UpdateState.UpdateDownloading:\n                        case UpdateState.UpdateApplicationImpending:\n                            // Will never happen here\n                            break;\n                        case UpdateState.UpdateAvailable:\n                            if (command == UpdateCommands.DownloadUpdate)\n                            {\n                                updateState = UpdateState.UpdateDownloading;\n                                UiRefresh();\n                                try\n                                {\n                                    update_exe = Utils.DownloadExecutableAndVerifyHash(verifiedManifest, (progress) =>\n                                    {\n                                        downloadPercentage = progress;\n                                        UiRefresh();\n                                    }, new TimeSpan(0, 5, 0));\n                                    updateState = UpdateState.UpdateApplicationImpending;\n                                    UiRefresh();\n                                    Updater.TriggerUpdateStep2(update_exe);\n                                    OnShutdownRequest?.Invoke(this);\n                                }\n                                catch (Utils.VerificationException e)\n                                {\n                                    updateState = UpdateState.UpdateVerificationFailed;\n                                    lastErrorMessage = \"Verification failed: \\n\" + e.Message;\n                                }\n                                catch (Utils.DownloadFailedException e)\n                                {\n                                    updateState = UpdateState.UpdateDownloadFailed;\n                                    lastErrorMessage = \"Download failed: \\n\" + e.Message;\n                                }\n                                catch (Exception e)\n                                {\n                                    updateState = UpdateState.UpdateDownloadFailed;\n                                    lastErrorMessage = \"Unexpected error: \\n\" + e.Message;\n                                }\n                            }\n                            UiRefresh();\n                            break;\n                    }\n\n                }\n                catch (InvalidOperationException)\n                {\n                    return;\n                }\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Updater/Updater.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Diagnostics;\n\nnamespace KeyNStroke.Updater\n{\n    class Updater\n    {\n        public static Statemachine Instance\n        {\n            get { return Statemachine.Instance; }\n        }\n\n\n        private Updater()\n        {\n        }\n\n        /// <summary>\n        /// Returns true if the program should exit\n        /// </summary>\n        /// <param name=\"args\"></param>\n        static public bool HandleArgs(string[] args)\n        {\n            if (args.Length > 0)\n            {\n                if (args[0] == \"--update-step2\" && args.Length == 3)\n                {\n                    UpdateStep2(args[1], int.Parse(args[2]));\n                    return true;\n                }\n                if (args[0] == \"--update-step3\" && args.Length == 3)\n                {\n                    UpdateStep3(args[1], int.Parse(args[2]));\n                    return false; // continue running\n                }\n                if (Admininstration.HandleArgs(args))\n                {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n\n\n        static public void TriggerUpdateStep2(byte[] update)\n        {\n            string oldExePath = System.Reflection.Assembly.GetEntryAssembly().Location;\n            string tmpExeName = $\"Key-n-Stroke_Updater.exe\";\n            string oldExeParentFolder = Path.GetDirectoryName(oldExePath); // may not be writable\n            string systemTmpFolder = Path.GetTempPath(); // Should be writable in any case\n            string tmpExePath = Path.Combine(systemTmpFolder, tmpExeName);\n\n            using (var fs = new FileStream(tmpExePath, FileMode.Create, FileAccess.Write))\n            {\n                fs.Write(update, 0, update.Length);\n            }\n            int ownPid = Process.GetCurrentProcess().Id;\n\n            ProcessStartInfo psi = new ProcessStartInfo\n            {\n                FileName = tmpExePath,\n                WorkingDirectory = Path.GetDirectoryName(tmpExePath),\n                Arguments = $\"--update-step2 \\\"{oldExePath}\\\" {ownPid}\"\n            };\n\n            if (!Utils.IsDirectoryWritable(oldExeParentFolder))\n            {\n                psi.UseShellExecute = true;\n                psi.Verb = \"runas\";\n            }\n\n            Process.Start(psi);\n            // Shutdown own process immediately!\n        }\n\n        /// <summary>\n        /// Handles the swap of the executables.\n        /// The we are the updated executable and the previous executable (the one that started us) must be overwritten by us.\n        /// \n        /// </summary>\n        static public void UpdateStep2(string oldExePath, int startingProcessPid)\n        {\n            try\n            {\n                Process orig = Process.GetProcessById(startingProcessPid);\n                if (!orig.WaitForExit(60000))\n                {\n                    Console.WriteLine(\"ERROR: Calling process did not exit in time\");\n                    return;\n                }\n                orig.Close();\n            } catch (ArgumentException)\n            {\n                // The process specified by the processId parameter is not running. The identifier might be expired.\n            }\n\n            string tmpExePath = System.Reflection.Assembly.GetEntryAssembly().Location;\n            Log.e(\"UPDATE\", $\"Copy from {tmpExePath} to {oldExePath}\");\n            if (File.Exists(oldExePath))\n            {\n                File.Delete(oldExePath);\n            }\n            File.Copy(tmpExePath, oldExePath);\n\n            int ownPid = Process.GetCurrentProcess().Id;\n\n            ProcessStartInfo psi = new ProcessStartInfo\n            {\n                FileName = oldExePath,\n                WorkingDirectory = Path.GetDirectoryName(oldExePath),\n                Arguments = $\"--update-step3 \\\"{tmpExePath}\\\" {ownPid}\"\n            };\n\n            Process.Start(psi); // Even if the updater has been started as Admin, this will start as the original user again.\n            // Shutdown own process immediately\n        }\n\n        /// <summary>\n        /// Delete the temporary executable\n        /// </summary>\n        /// <param name=\"tmpExePath\"></param>\n        /// <param name=\"startingProcessPid\"></param>\n        static public void UpdateStep3(string tmpExePath, int startingProcessPid)\n        {\n            try\n            {\n                Process orig = Process.GetProcessById(startingProcessPid);\n                if (!orig.WaitForExit(60000))\n                {\n                    Console.WriteLine(\"ERROR: Calling process did not exit in time\");\n                    return;\n                }\n                orig.Close();\n            }\n            catch (ArgumentException)\n            {\n                // The process specified by the processId parameter is not running. The identifier might be expired.\n            }\n\n            if (File.Exists(tmpExePath))\n            {\n                Log.e(\"UPDATE\", $\"Deleted {tmpExePath}\");\n                File.Delete(tmpExePath);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Updater/Utils.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing System.Reflection;\nusing System.Security.Cryptography;\nusing System.Security.Cryptography.Xml;\nusing System.Security.Principal;\nusing System.Text;\nusing System.Threading;\nusing System.Windows;\nusing System.Xml;\n\nnamespace KeyNStroke.Updater\n{\n    class Utils\n    {\n        #region Exceptions\n\n        /// <summary>\n        /// Exception thrown if the verification of the manifest or update.exe failed.\n        /// </summary>\n        public class VerificationException : Exception\n        {\n            public VerificationException()\n            {\n            }\n\n            public VerificationException(string message)\n                : base(message)\n            {\n            }\n\n            public VerificationException(string message, Exception inner)\n                : base(message, inner)\n            {\n            }\n        }\n\n        /// <summary>\n        /// Exception thrown if the download of the manifest or update.exe failed.\n        /// </summary>\n        public class DownloadFailedException : Exception\n        {\n            public DownloadFailedException()\n            {\n            }\n\n            public DownloadFailedException(string message)\n                : base(message)\n            {\n            }\n\n            public DownloadFailedException(string message, Exception inner)\n                : base(message, inner)\n            {\n            }\n        }\n\n        #endregion\n\n        #region Hash\n\n        /// <summary>\n        /// Calculates the SHA256 hash of the given file.\n        /// </summary>\n        /// <param name=\"path\"></param>\n        /// <returns>The hash as string.</returns>\n        public static string SHA256OfFile(string path)\n        {\n            SHA256 Sha256 = SHA256.Create();\n\n            using (FileStream stream = File.OpenRead(path))\n            {\n                byte[] bytes = Sha256.ComputeHash(stream);\n                string result = \"\";\n                foreach (byte b in bytes) result += b.ToString(\"x2\");\n                return result;\n            }\n        }\n\n        /// <summary>\n        /// Calculates the SHA256 hash of the given bytes.\n        /// </summary>\n        /// <param name=\"data\"></param>\n        /// <returns>The hash as string.</returns>\n        public static string SHA256OfBytes(byte[] data)\n        {\n            SHA256 Sha256 = SHA256.Create();\n\n            byte[] bytes = Sha256.ComputeHash(data);\n            string result = \"\";\n            foreach (byte b in bytes) result += b.ToString(\"x2\");\n            return result;\n        }\n\n        #endregion\n\n        #region Permissions\n\n        public static bool IsDirectoryWritable(string path)\n        {\n            try\n            {\n                string random = Path.Combine(path, Path.GetRandomFileName());\n                using (FileStream fs = File.Create(random, 1, FileOptions.DeleteOnClose))\n                { }\n                return true;\n            }\n            catch\n            {\n                return false;\n            }\n        }\n\n        public static bool HasAdminPrivileges()\n        {\n            using (WindowsIdentity identity = WindowsIdentity.GetCurrent())\n            {\n                WindowsPrincipal principal = new WindowsPrincipal(identity);\n                return principal.IsInRole(WindowsBuiltInRole.Administrator);\n            }\n        }\n\n        #endregion\n\n        #region XML signature\n\n        // https://docs.microsoft.com/de-de/dotnet/standard/security/how-to-sign-xml-documents-with-digital-signatures\n        /// <summary>\n        /// Sign an XML file with an RSA key.\n        /// The signature can be verified with the other key of the key pair by using the function <code>VerifyXml()</code>.\n        /// The singature is appended as an additional node to `xmlDoc`.\n        /// </summary>\n        /// <param name=\"xmlDoc\">The document to be signed</param>\n        /// <param name=\"rsaKey\">The public or private key for signing.</param>\n        public static void SignXml(XmlDocument xmlDoc, RSA rsaKey)\n        {\n            // Check arguments.\n            if (xmlDoc == null)\n                throw new ArgumentNullException(nameof(xmlDoc));\n\n            // Create a SignedXml object.\n            SignedXml signedXml = new SignedXml(xmlDoc)\n            {\n                // Add the key to the SignedXml document.\n                SigningKey = rsaKey ?? throw new ArgumentNullException(nameof(rsaKey))\n            };\n\n            // Create a reference to be signed.\n            Reference reference = new Reference\n            {\n                Uri = \"\"\n            };\n\n            // Add an enveloped transformation to the reference.\n            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();\n            reference.AddTransform(env);\n\n            // Add the reference to the SignedXml object.\n            signedXml.AddReference(reference);\n\n            // Compute the signature.\n            signedXml.ComputeSignature();\n\n            // Get the XML representation of the signature and save\n            // it to an XmlElement object.\n            XmlElement xmlDigitalSignature = signedXml.GetXml();\n\n            // Append the element to the XML document.\n            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));\n        }\n\n        // https://docs.microsoft.com/de-de/dotnet/standard/security/how-to-sign-xml-documents-with-digital-signatures\n        /// <summary>\n        /// Verify the signature of an XML document signed by the function <code>SignXml()</code>.\n        /// Throws an exception if the signature is not valid.\n        /// </summary>\n        /// <param name=\"xmlDoc\">The signed document.</param>\n        /// <param name=\"rsaKey\">The public or private key (other one) that has been used for signing.</param>\n        /// <returns></returns>\n        /// <exception cref=\"ArgumentNullException\">Wrong arguments</exception>\n        /// <exception cref=\"CryptographicException\">Verification failed</exception>\n        public static void VerifyXml(XmlDocument xmlDoc, RSA rsaKey)\n        {\n            // Check arguments.\n            if (xmlDoc == null)\n                throw new ArgumentNullException(\"xmlDoc\");\n            if (rsaKey == null)\n                throw new ArgumentNullException(\"pubKey\");\n\n            // Create a new SignedXml object and pass it\n            // the XML document class.\n            SignedXml signedXml = new SignedXml(xmlDoc);\n\n            // Find the \"Signature\" node and create a new\n            // XmlNodeList object.\n            XmlNodeList nodeList = xmlDoc.GetElementsByTagName(\"Signature\");\n\n            // Throw an exception if no signature was found.\n            if (nodeList.Count <= 0)\n            {\n                throw new CryptographicException(\"No Signature was found in the document.\");\n            }\n\n            // This example only supports one signature for\n            // the entire XML document.  Throw an exception\n            // if more than one signature was found.\n            if (nodeList.Count > 1)\n            {\n                throw new CryptographicException(\"More that one signature was found in the document.\");\n            }\n\n            // Load the first <signature> node.\n            signedXml.LoadXml((XmlElement)nodeList[0]);\n\n            // Check the signature and return the result.\n            bool valid = signedXml.CheckSignature(rsaKey);\n\n            if (!valid)\n            {\n                throw new CryptographicException(\"Signature invalid.\");\n            }\n        }\n\n        #endregion\n\n        #region Download Helper\n\n        public delegate void ProgressCallBack(int progress);\n\n        /// <summary>Downloads a ressource from the web. Provides progress information</summary>\n        /// <returns>The downloaded bytes (never null)</returns>\n        /// <exception cref=\"DownloadFailedException\">Thrown if the download fails.</exception>\n        private static byte[] DownloadWithTimeoutAndProgress(string url, ProgressCallBack progressCallBack, TimeSpan? timeout)\n        {\n            Exception exception = null; // probably a WebException\n            byte[] data = null;\n            bool canceled = false;\n\n            using (WebClient wc = new WebClient())\n            {\n                wc.DownloadProgressChanged += (o, e) =>\n                {\n                    bool _canceled;\n                    lock (e.UserState)\n                    {\n                        _canceled = canceled;\n                    }\n                    if (!canceled)\n                    {\n                        progressCallBack?.Invoke(e.ProgressPercentage);\n                    }\n                };\n                wc.DownloadDataCompleted += (o, e) =>\n                {\n                    if (e.Error != null)\n                    {\n                        exception = e.Error;\n                    }\n                    else\n                    {\n                        data = e.Result;\n                    }\n                    lock (e.UserState)\n                    {\n                        //releases blocked thread\n                        Monitor.Pulse(e.UserState);\n                    }\n                };\n\n                var syncObject = new Object();\n\n                lock (syncObject)\n                {\n                    SynchronizationContext orig = SynchronizationContext.Current;\n                    // Use thread pool and not the SyncContext for WPF\n                    //SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());\n\n                    wc.DownloadDataAsync(new Uri(url), syncObject);\n\n                    SynchronizationContext.SetSynchronizationContext(orig);\n\n                    if (timeout is TimeSpan to)\n                    {\n                        if (!Monitor.Wait(syncObject, to))\n                        {\n                            wc.CancelAsync();\n                            throw new DownloadFailedException(exception.Message, exception);\n                        }\n                    }\n                    else\n                    {\n                        Monitor.Wait(syncObject);\n                    }\n                }\n            }\n\n            if (exception != null)\n            {\n                throw new DownloadFailedException(exception.Message, exception);\n            }\n            if (data == null)\n            {\n                throw new DownloadFailedException(\"No data received.\");\n            }\n            return data;\n        }\n\n        #endregion\n\n        #region Download Update.exe\n\n        /// <summary>\n        /// Downloads the updated executable from the manifest and verifies its hash against the manifest.\n        /// Throws an exception on download failure or verification failure.\n        /// </summary>\n        /// <param name=\"manifest\">The verified manifest that specifies the hash and download url.</param>\n        /// <param name=\"progressCallBack\">Called on download progress change. This call originates from a different thread but only as long as this function blocks.</param>\n        /// <returns>The downloaded data.</returns>\n        /// <exception cref=\"DownloadFailedException\">Thrown if the download fails.</exception>\n        /// <exception cref=\"VerificationException\">Thrown if the verification fails.</exception>\n        public static byte[] DownloadExecutableAndVerifyHash(XmlDocument manifest, ProgressCallBack progressCallBack, TimeSpan? timeout)\n        {\n            var executableUrl = manifest.SelectSingleNode(\"manifest/update/download\").InnerText;\n            byte[] data = DownloadWithTimeoutAndProgress(executableUrl, progressCallBack, timeout);\n\n            var expectedHash = manifest.SelectSingleNode(\"manifest/update/sha256\").InnerText;\n            var hash = SHA256OfBytes(data);\n\n            if (hash != expectedHash)\n            {\n                throw new VerificationException(\"Update hash mismatch.\");\n            }\n            else\n            {\n                return data;\n            }\n        }\n\n        #endregion\n\n        #region Download Manifest\n\n        public static string ManifestUrl\n        {\n            get { return @\"https://invisibletower.de/key-n-stroke/updateManifest.xml\"; }\n        }\n\n        /// <summary>\n        /// For debugging: Remember the latest downloaded manifest even if verification failed\n        /// </summary>\n        private static string _rawManifest;\n\n        /// <summary>\n        /// Download and verify the update manifest.\n        /// Throws VerificationException if the manifest can't be validated.\n        /// </summary>\n        /// <returns>Returns the verified manifest.</returns>\n        /// <exception cref=\"DownloadFailedException\">Thrown if the download failed.</exception>\n        /// <exception cref=\"VerificationException\">Thrown if the signature could not be verified.</exception>\n        public static XmlDocument DownloadAndVerifyManifest()\n        {\n            XmlDocument xmlDoc = new XmlDocument\n            {\n                PreserveWhitespace = true\n            };\n            TimeSpan oneMinute = new TimeSpan(0, 0, 3);\n            \n            byte[] xml = DownloadWithTimeoutAndProgress(ManifestUrl, null, oneMinute);\n            \n            try\n            {\n                _rawManifest = Encoding.UTF8.GetString(xml); // store for debug\n            }\n            catch (Exception e)\n            {\n                throw new VerificationException(\"Manifest invalid (no UTF-8)\", e);\n            }\n\n            try\n            {\n                xmlDoc.LoadXml(_rawManifest);\n            }\n            catch (XmlException e)\n            {\n                throw new VerificationException(\"Manifest invalid (no xml)\", e);\n            }\n\n            try\n            {\n                VerifyXml(xmlDoc, GetEmbeddedPubKey());\n            }\n            catch (CryptographicException e)\n            {\n                throw new VerificationException(e.Message);\n            }\n\n            return xmlDoc;\n        }\n\n        #endregion\n\n        #region GetEmbeddedPubKey\n\n        /// <summary>\n        /// Return the embedded PubKey\n        /// </summary>\n        /// <returns></returns>\n        public static RSA GetEmbeddedPubKey()\n        {\n            Stream pubKeyStream = Application.GetResourceStream(new Uri(\"pack://application:,,,/Key-n-Stroke;component/Resources/updateKey.pub.xml\")).Stream;\n            RSA pub = RSA.Create();\n            using (StreamReader sr = new StreamReader(pubKeyStream))\n            {\n                pub.FromXmlString(sr.ReadToEnd());\n            }\n            return pub;\n        }\n\n        #endregion\n\n        #region Version compare (CanUpdate())\n\n        /// <summary>\n        /// Checks if the updatable version is greater than the installed version.\n        /// </summary>\n        /// <returns></returns>\n        public static bool CanUpdate(XmlDocument manifest)\n        {\n            try\n            {\n                string update_version = manifest.SelectSingleNode(\"manifest/update/version\").InnerText;\n                Version current = Assembly.GetExecutingAssembly().GetName().Version;\n                Version update = Version.Parse(update_version);\n                return (update.CompareTo(current) > 0);\n            }\n            catch (Exception)\n            {\n                return false;\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/UrlOpener.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace KeyNStroke\n{\n    public class UrlOpener\n    {\n        public static void OpenGithub()\n        {\n            string url = \"https://github.com/Phaiax/Key-n-Stroke/\";\n            ProcessStartInfo si = new ProcessStartInfo(url);\n            Process.Start(si);\n        }\n\n        public static void OpenGithubREADME()\n        {\n            string url = \"https://github.com/Phaiax/Key-n-Stroke/blob/master/README.md\";\n            ProcessStartInfo si = new ProcessStartInfo(url);\n            Process.Start(si);\n        }\n\n        public static void OpenGithubIssues()\n        {\n            string url = \"https://github.com/Phaiax/Key-n-Stroke/issues\";\n            ProcessStartInfo si = new ProcessStartInfo(url);\n            Process.Start(si);\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/Welcome.xaml",
    "content": "﻿<Window x:Class=\"KeyNStroke.Welcome\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:KeyNStroke\"\n        mc:Ignorable=\"d\"\n        Title=\"Welcome\"\n        Height=\"440.617\"\n        Width=\"741.54\"\n        Icon=\"Resources/app.ico\"\n        WindowStartupLocation=\"CenterScreen\">\n    <Window.Resources>\n        <BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\n    </Window.Resources>\n    <Grid HorizontalAlignment=\"Stretch\"\n          Margin=\"10,10,10,10\"\n          VerticalAlignment=\"Stretch\"\n          x:Name=\"layout_root\">\n        <StackPanel Margin=\"10,10,-0.333,163.333\">\n            <TextBlock TextWrapping=\"Wrap\">\n                <Span FontSize=\"24\">\n                    <Run Text=\"Welcome to Key'n'Strokes \" /><Run x:Name=\"VersionInfo\" />\n                </Span>\n                <LineBreak />\n                <Span FontSize=\"24\"><Run /></Span>\n                <LineBreak /><LineBreak />\n                <Run Text=\"Click \" />\n                <InlineUIContainer>\n                    <Image Width=\"20\"\n                           Source=\"Resources/app.ico\" />\n                </InlineUIContainer>\n                <Run Text=\" in the notification area to access the\" />\n                <Hyperlink RequestNavigate=\"Hyperlink_RequestNavigate_settings\"\n                           NavigateUri=\"none://dummy\">settings</Hyperlink>.\n                <LineBreak />\n                <LineBreak />\n                <Run Text=\"Press \" /><Span FontWeight=\"Bold\"><Run Text=\"{Binding StandbyShortcut, Mode=OneWay}\" /></Span><Run Text=\" to toggle standby mode.\" />\n            </TextBlock>\n            <TextBlock Visibility=\"{Binding EnableKeystrokeHistory, Converter={StaticResource BooleanToVisibilityConverter}}\">\n                <Run Text=\"Press \" /><Span FontWeight=\"Bold\"><Run Text=\"{Binding KeystrokeHistorySettingsModeShortcut, Mode=OneWay}\" /></Span><Run Text=\" to move the keystroke history window.\" />\n                <LineBreak />\n                <Run Text=\"Press \" /><Span FontWeight=\"Bold\"><Run Text=\"{Binding KeystrokeHistoryPasswordModeShortcut, Mode=OneWay}\" /></Span><Run Text=\" to toggle password mode.\" />\n            </TextBlock>\n            <TextBlock Visibility=\"{Binding EnableAnnotateLine, Converter={StaticResource BooleanToVisibilityConverter}}\">\n                <Run Text=\"Press \" /><Span FontWeight=\"Bold\"><Run Text=\"{Binding AnnotateLineShortcut, Mode=OneWay}\" /></Span><Run Text=\" to draw a line.\" />\n            </TextBlock>\n        </StackPanel>\n        <Button x:Name=\"ButtonCheckForUpdates\"\n                Content=\"Check for Updates\"\n                HorizontalAlignment=\"Left\"\n                Margin=\"10,0,0,10.333\"\n                VerticalAlignment=\"Bottom\"\n                Width=\"129\"\n                Height=\"24\" />\n        <TextBlock Margin=\"10,299,-0.333,52.333\"\n                   TextWrapping=\"Wrap\">\n            <Hyperlink RequestNavigate=\"Hyperlink_RequestNavigate_README\"\n                       NavigateUri=\"none://dummy\">Documentation</Hyperlink>\n            <LineBreak />\n            <Hyperlink RequestNavigate=\"Hyperlink_RequestNavigate_Issues\"\n                       NavigateUri=\"none://dummy\">Report Bugs in GitHub</Hyperlink></TextBlock>\n        <Button x:Name=\"ButtonExitApplication\"\n                Content=\"Exit Application\"\n                Margin=\"0,0,165.667,10.333\"\n                Click=\"ButtonExitApplication_Click\"\n                HorizontalAlignment=\"Right\"\n                Width=\"129\"\n                Height=\"24\"\n                VerticalAlignment=\"Bottom\" />\n        <Button x:Name=\"ButtonHideThisWindow\"\n                Content=\"Let's go\"\n                Margin=\"0,0,9.667,10.333\"\n                Click=\"ButtonHideThisWindow_Click\"\n                Height=\"24\"\n                VerticalAlignment=\"Bottom\"\n                HorizontalAlignment=\"Right\"\n                Width=\"129\" />\n        <TextBlock x:Name=\"TextUpdateStatus\"\n                   HorizontalAlignment=\"Left\"\n                   Margin=\"152,0,0,13.333\"\n                   VerticalAlignment=\"Bottom\"\n                   Width=\"227\" />\n    </Grid>\n</Window>\n"
  },
  {
    "path": "KeyNStroke/Welcome.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Threading;\nusing System.Windows;\nusing System.Windows.Documents;\n\nnamespace KeyNStroke\n{\n    /// <summary>\n    /// Interaktionslogik für Welcome.xaml\n    /// </summary>\n    public partial class Welcome : Window\n    {\n        readonly SettingsStore settings;\n\n        public Welcome(SettingsStore s)\n        {\n            InitializeComponent();\n            settings = s;\n            layout_root.DataContext = settings;\n\n            Updater.Updater.Instance.OnUiUpdate += Updater_onUIUpdate;\n            Updater.Updater.Instance.OnShutdownRequest += Updater_onShutdownRequest;\n            ButtonCheckForUpdates.Click += Updater.Updater.Instance.UiButton_Click;\n\n            Version current = Assembly.GetExecutingAssembly().GetName().Version;\n            VersionInfo.Text = current.ToString(3);\n\n        }\n\n        protected override void OnClosed(EventArgs e)\n        {\n            base.OnClosed(e);\n            ((App)Application.Current).onWelcomeWindowClosed();\n        }\n\n        private void Hyperlink_RequestNavigate_README(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\n        {\n            UrlOpener.OpenGithubREADME();\n        }\n\n\n        private void Hyperlink_RequestNavigate_Issues(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\n        {\n            UrlOpener.OpenGithubIssues();\n        }\n\n\n        private void ButtonSettings_Click(object sender, RoutedEventArgs e)\n        {\n            ((App)Application.Current).showSettingsWindow();\n        }\n\n        #region Updater\n\n        List<string> updateDetails;\n\n        private void Updater_onUIUpdate(object sender, Updater.Statemachine.UiUpdateEventArgs e)\n        {\n            this.Dispatcher.InvokeAsync(() =>\n            {\n                ButtonCheckForUpdates.Content = e.buttonText;\n                ButtonCheckForUpdates.IsEnabled = e.buttonEnabled;\n                TextUpdateStatus.Inlines.Clear();\n                if (e.details != null && e.details.Count > 0)\n                {\n                    Hyperlink hl = new Hyperlink(new Run(e.info))\n                    {\n                        NavigateUri = new Uri(\"none://dummy\")\n                    };\n                    hl.RequestNavigate += Hyperlink_RequestNavigate_UpdateDetails;\n                    TextUpdateStatus.Inlines.Add(hl);\n                    updateDetails = e.details;\n                } else\n                {\n                    TextUpdateStatus.Inlines.Add(new Run(e.info));\n                    updateDetails = null;\n                }\n            });\n        }\n\n        private void Hyperlink_RequestNavigate_UpdateDetails(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\n        {\n            if (updateDetails != null)\n            {\n                MessageBox.Show(String.Join(\"\\n\", updateDetails), \"Updater Info\", MessageBoxButton.OK);\n            }\n        }\n\n\n\n        private void Updater_onShutdownRequest(object sender)\n        {\n            this.Dispatcher.InvokeAsync(() =>\n            {\n                Application.Current.Shutdown();\n            });\n        }\n\n        #endregion\n\n        private void ButtonHideThisWindow_Click(object sender, RoutedEventArgs e)\n        {\n            Close();\n        }\n\n        private void ButtonExitApplication_Click(object sender, RoutedEventArgs e)\n        {\n            System.Windows.Application.Current.Shutdown();\n        }\n\n        private void Hyperlink_RequestNavigate_settings(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)\n        {\n            ((App)Application.Current).showSettingsWindow();\n        }\n    }\n}\n"
  },
  {
    "path": "KeyNStroke/app.manifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"MyApplication.app\"/>\n  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n    <security>\n      <requestedPrivileges xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <!-- UAC-Manifestoptionen\n             Wenn Sie die Ebene der Benutzerkontensteuerung für Windows ändern möchten, ersetzen Sie den \n             Knoten \"requestedExecutionLevel\" wie folgt.\n\n        <requestedExecutionLevel  level=\"asInvoker\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"requireAdministrator\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"highestAvailable\" uiAccess=\"false\" />\n\n            Durch Angabe des Elements \"requestedExecutionLevel\" wird die Datei- und Registrierungsvirtualisierung deaktiviert. \n            Entfernen Sie dieses Element, wenn diese Virtualisierung aus Gründen der Abwärtskompatibilität\n            für die Anwendung erforderlich ist.\n        -->\n        <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" />\n      </requestedPrivileges>\n    </security>\n  </trustInfo>\n\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Eine Liste der Windows-Versionen, unter denen diese Anwendung getestet\n           und für die sie entwickelt wurde. Wenn Sie die Auskommentierung der entsprechenden Elemente aufheben,\n           wird von Windows automatisch die kompatibelste Umgebung ausgewählt. -->\n\n      <!-- Windows Vista -->\n      <!--<supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" />-->\n\n      <!-- Windows 7 -->\n      <!--<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" />-->\n\n      <!-- Windows 8 -->\n      <!--<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />-->\n\n      <!-- Windows 8.1 -->\n      <!--<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" />-->\n\n      <!-- Windows 10 -->\n      <!--<supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" />-->\n\n    </application>\n  </compatibility>\n\n  <!-- Gibt an, dass die Anwendung mit DPI-Werten kompatibel ist und von Windows nicht automatisch auf höhere\n       DPI-Werte skaliert wird. WPF-Anwendungen (Windows Presentation Foundation) sind automatisch mit DPI-Werten kompatibel und müssen sich nicht  \n       anmelden. Für Windows Forms-Anwendungen für .NET Framework 4.6, die sich für diese Einstellung anmelden, muss \n       auch die Einstellung \"'EnableWindowsFormsHighDpiAutoResizing\" in der \"app.config\" auf \"true\" festgelegt werden. -->\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitor</dpiAwareness>\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true</dpiAware>\n    </windowsSettings>\n  </application>\n\n\n  <!-- Designs für allgemeine Windows-Steuerelemente und -Dialogfelder (Windows XP und höher) aktivieren -->\n  <!--\n  <dependency>\n    <dependentAssembly>\n      <assemblyIdentity\n          type=\"win32\"\n          name=\"Microsoft.Windows.Common-Controls\"\n          version=\"6.0.0.0\"\n          processorArchitecture=\"*\"\n          publicKeyToken=\"6595b64144ccf1df\"\n          language=\"*\"\n        />\n    </dependentAssembly>\n  </dependency>\n  -->\n\n</assembly>\n"
  },
  {
    "path": "KeyNStroke/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Costura.Fody\" version=\"5.0.2\" targetFramework=\"net48\" developmentDependency=\"true\" />\n  <package id=\"Extended.Wpf.Toolkit\" version=\"3.8.1\" targetFramework=\"net48\" />\n  <package id=\"Fody\" version=\"6.3.0\" targetFramework=\"net48\" developmentDependency=\"true\" />\n  <package id=\"Microsoft.NETCore.Platforms\" version=\"1.1.0\" targetFramework=\"net48\" />\n  <package id=\"Microsoft.Win32.Primitives\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"NETStandard.Library\" version=\"1.6.1\" targetFramework=\"net48\" />\n  <package id=\"System.AppContext\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Collections\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Collections.Concurrent\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Console\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Diagnostics.Debug\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Diagnostics.DiagnosticSource\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Diagnostics.Tools\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Diagnostics.Tracing\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Globalization\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Globalization.Calendars\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.IO\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.IO.Compression\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.IO.Compression.ZipFile\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.IO.FileSystem\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.IO.FileSystem.Primitives\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Linq\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Linq.Expressions\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Net.Http\" version=\"4.3.4\" targetFramework=\"net48\" />\n  <package id=\"System.Net.Primitives\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Net.Sockets\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.ObjectModel\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Reflection\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Reflection.Extensions\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Reflection.Primitives\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Resources.ResourceManager\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime.Extensions\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime.Handles\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime.InteropServices\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime.InteropServices.RuntimeInformation\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Runtime.Numerics\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Security.Cryptography.Algorithms\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Security.Cryptography.Encoding\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Security.Cryptography.Primitives\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Security.Cryptography.X509Certificates\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Text.Encoding\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Text.Encoding.Extensions\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Text.RegularExpressions\" version=\"4.3.1\" targetFramework=\"net48\" />\n  <package id=\"System.Threading\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Threading.Tasks\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Threading.Timer\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Xml.ReaderWriter\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"System.Xml.XDocument\" version=\"4.3.0\" targetFramework=\"net48\" />\n  <package id=\"WpfColorFontDialog\" version=\"1.0.7\" targetFramework=\"net48\" />\n</packages>"
  },
  {
    "path": "KeyNStroke.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30011.22\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"KeyNStroke\", \"KeyNStroke\\KeyNStroke.csproj\", \"{BA117557-FEBC-45C5-8895-4017D05C3DCD}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{BA117557-FEBC-45C5-8895-4017D05C3DCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{BA117557-FEBC-45C5-8895-4017D05C3DCD}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{BA117557-FEBC-45C5-8895-4017D05C3DCD}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BA117557-FEBC-45C5-8895-4017D05C3DCD}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {72B0FF35-BC69-4A53-ACA7-FFFD74A21114}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Key'n'Stroke\n\nDisplays Keystrokes in an overlay window and visualize mouse clicks.\n\n\n# Features\n\n - Displays special keys (e.g. volume up: 🔊) and shortcuts\n - Visually indicate mouse cursor position\n - Visually indicate pressed buttons\n - Click Through\n - Opacity\n - Customizable: Icons, Colors, Font, Size and Position, Text Position and Orientation\n - Displays history of pressed keys\n - Icon in notification area\n - One small file, no installation required\n - Works with high-dpi screens\n - Works with multi-dpi screen setups\n\nRequirements: Windows 10\n\n\n# Download\n\n<a href=\"https://github.com/Phaiax/Key-n-Stroke/raw/master/Releases/v1.1.0/Key-n-Stroke.exe\">Download Key-n-Stroke.exe 1.1.0</a> (most recent, 2022-02-08)\n\n\nThere is no installation step. Just keep that exe file to start the application.\n\nYou will once be greeted with a [smart screen warning](https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/Smartscreen1.png). Click \"More Info\" and \"Execute Anyway\". It's fine if the issuer name is 'Open Source Developer, Daniel Seemer'.\n\n# Screenshots\n\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/mouse.png\">\n\t<img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/mouse.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/ctrl_scroll.png\">\n    <img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/ctrl_scroll.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/example1.png\">\n    <img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/example1.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/comic_sans_ms.png\">\n    <img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/comic_sans_ms.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/resizemode.png.png\">\n    <img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/resizemode.png.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/bottom_right.png\">\n\t<img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/bottom_right.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/bottom_center.png\">\n\t<img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/bottom_center.png\">\n</a>\n<a href=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/settings.png\">\n\t<img src=\"https://raw.githubusercontent.com/Phaiax/Key-n-Stroke/master/Screenshots/settings.png\">\n</a>\n\n\n## Releases\n\nYou can find all the exe files in the <code>Releases</code> folder\n\n- v0.0.1 (Text positioning not implemented yet)\n- v0.1.1 (Looks fine)\n- v0.1.2 (More Whitespace between special chars)\n- v0.1.3 (fix bug with Win+? shortcuts)\n- v0.2.0 (indicator for cursor position, history timeout, bugfixes, documentation)\n- v0.3.0 (indicator for pressed buttons, deadkey fixes, backspace functionality)\n- v0.3.1 (new icons, little fixes and internal improvements)\n- v1.0.0 \n    - Rebrand as Key'n'Stroke for better discoverability, previously PxKeystrokesForScreencasts\n    - Switch to .NET 4.8 and WPF\n    - Nicer icon rendering\n    - High-Dpi compatability\n    - Custom Icons\n- v1.0.1 \n    - No changes, just testing the update mechanism\n- v1.1.0\n    - New: Draw lines on the screen\n    - New: Shortcut mode for keystroke window\n    - New: Standby mode\n    - New: Hide cursor indicator if cursor is hidden\n    - New: Draw edge of cursor indicator\n    - New: Indicate clicks via color change in cursor indicator\n    - New: Setting to hide welcome window on startup\n    - Fix: Capture shortcuts\n    - Fix: Keystroke window startup position overlaps taskbar\n    - Fix: Crash on modifier+click with button indicators enabled\n    - Fix: Show shortcut shift+backspace\n- v1.2.0\n    - Fix: Fix display of special characters (e.g / was showing as ?)\n\n\n# Feature Requests\n\nThis app is Open Source. If you miss a feature, write an issue or go for it and send a Pull-Request afterwards!\n\n\n# Donations and Financial\n\nYou can donate via <a href=\"https://www.paypal.me/phaiax\">paypal.me/phaiax</a>, send Bitcoins (`1JWER55pheUeJzaUcqaYwP8ZaGe5C16Rp9`) or Stellar Lumens (`phaiax*keybase.io`).  You can verify the wallet addresses cryptographically on <a href=\"https://keybase.io/phaiax\">keybase.io/phaiax</a>. Add the message \"pkfs\" or \"keynstroke\" to the payment so I can associate it with this project. After funds for the next six years of developer certificates are raised (which I assume will never happen), I will use any additional funds to buy and eat icecream for myself and anyone I like.\n\nThis app shows a smart screen warning. To fix this, I bought a validated security certificate from <a href=\"http://www.certum.eu/certum/cert,offer_en_open_source_cs.xml\">Certum</a>. Unfortunately that did not fix it. (And the internet says so). At least it now shows my name as the Author.\n\nI said that if someone funds at least three years of certification (€ 75.00), I will use that money to buy a new certificate each year and implement a secure autoupdater. It's not that much, but I started anyway :smile: Here is the list of donations for full transparency: (three as of Feb 2022)\n\n| Date       | Amount | From             |\n|------------|--------|------------------|\n| 2023-11-20 | € 20   | S. M.            |\n| 2023-06-11 | € 9.10 | R. G. R.         |\n| 2023-12-02 | € 22.22| A. V. N.         |\n| 2023-07-25 | € 5    | D. N.            |\n| 2023-05-30 | € 10.26| I. J.            |\n| 2023-03-14 | € 5    | J. L.            |\n| 2022-02-02 | € 50   | Mario Pastoor    |\n| 2021-11-28 | € 16.95| Joshua Taylor    |\n| 2021-03-05 | € 11.14| Heitor K. M. M.  |\n| 2020-05-17 | € 5    | Marek S.         |\n| 2020-08-06 | € 10   | AGlass0fMilk     |\n\nExpenses: Certum Open Source Certificate and cryptographic card reader + Shipping: €119.87\n\nTotal: €164.67 - €119.87 = €44.8\n\nThx a lot, its time for a new certificate since the first one is expired :D\n\n\n\n# License\n\nApache Version 2, refer to the file LICENSE for details\n\n\n# Developed and signed by\n\n- Daniel Seemer (mail-oscert@invisibletower.de)\n"
  },
  {
    "path": "version.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json\",\n  \"version\": \"1.1.0\",\n  \"publicReleaseRefSpec\": [\n    \"^refs/heads/master$\",\n    \"^refs/heads/develop$\",\n    \"^refs/heads/rel/v\\\\d+\\\\.\\\\d+\"\n  ]\n}\n"
  }
]