├── tools ├── AzCopy.exe ├── xunit │ ├── xunit.dll │ ├── xunit.gui.exe │ ├── xunit.console.exe │ ├── xunit.gui.x86.exe │ ├── xunit.gui.clr4.exe │ ├── xunit.installer.exe │ ├── xunit.console.clr4.exe │ ├── xunit.console.x86.exe │ ├── xunit.extensions.dll │ ├── xunit.gui.clr4.x86.exe │ ├── xunit.runner.tdnet.dll │ ├── xunit.runner.msbuild.dll │ ├── xunit.runner.utility.dll │ ├── xunit.console.clr4.x86.exe │ ├── xunit.dll.tdnet │ ├── xunit.console.exe.config │ ├── xunit.console.x86.exe.config │ ├── xunit.console.clr4.exe.config │ ├── xunit.console.clr4.x86.exe.config │ ├── EULA.txt │ ├── NUnitXml.xslt │ └── HTML.xslt ├── Microsoft.WindowsAzure.Storage.dll └── Microsoft.WindowsAzure.Storage.DataMovement.dll ├── src ├── Carnac │ ├── icon.ico │ ├── IShell.cs │ ├── carnac.png │ ├── carnac_2.png │ ├── App.xaml.cs │ ├── icon.embedded.ico │ ├── Resources │ │ ├── Entypo.ttf │ │ └── Entypo-license.txt │ ├── FodyWeavers.xml │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── Utilities │ │ ├── ITimerFactory.cs │ │ ├── TimerFactory.cs │ │ ├── TimerState.cs │ │ └── ProcessUtilities.cs │ ├── CarnacWindowManager.cs │ ├── App.xaml │ ├── ViewModels │ │ └── KeyShowViewModel.cs │ ├── KeyMonitor │ │ ├── HotKeyWinApi.cs │ │ └── HotKey.cs │ ├── Carnac.ncrunchproject │ ├── Views │ │ ├── PlacementMarginConverter.cs │ │ ├── ShellView.xaml.cs │ │ └── KeyShowView.xaml.cs │ ├── packages.config │ ├── Carnac.v2.ncrunchproject │ ├── AppBootstrapper.cs │ └── Fody.targets ├── .nuget │ ├── NuGet.exe │ ├── NuGet.Config │ └── NuGet.targets ├── Carnac.Logic │ ├── FodyWeavers.xml │ ├── KeyMonitor │ │ ├── KeyDirection.cs │ │ ├── InterceptKeyEventArgs.cs │ │ └── InterceptKeys.cs │ ├── Keymaps │ │ ├── konami.yml │ │ ├── ncrunch.yml │ │ └── resharper.yml │ ├── IMessageProvider.cs │ ├── Enums │ │ └── NotificationPlacement.cs │ ├── IKeyProvider.cs │ ├── Native │ │ ├── POINTL.cs │ │ ├── DetailedScreen.cs │ │ ├── DISPLAY_DEVICE.cs │ │ ├── DisplayDeviceStateFlags.cs │ │ └── DEVMODE.cs │ ├── IShortcutProvider.cs │ ├── IScreenManager.cs │ ├── IPasswordModeService.cs │ ├── ShortcutCollection.cs │ ├── NotifyPropertyChanged.cs │ ├── packages.config │ ├── NotifyProperty.cs │ ├── Carnac.Logic.ncrunchproject │ ├── Internal │ │ └── FixedQueue.cs │ ├── Models │ │ ├── KeyPress.cs │ │ ├── PopupSettings.cs │ │ └── Message.cs │ ├── KeyShortcut.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Carnac.Logic.v2.ncrunchproject │ ├── Win32Methods.cs │ ├── KeyPressDefinition.cs │ ├── ScreenManager.cs │ ├── PasswordModeService.cs │ ├── Fody.targets │ ├── ShortcutProvider.cs │ ├── MessageProvider.cs │ ├── KeyProvider.cs │ └── Carnac.Logic.csproj ├── Carnac.Tests │ ├── System.Reactive.dll │ ├── SpecificationFor.cs │ ├── Carnac.Tests.ncrunchproject │ ├── Carnac.Tests.v2.ncrunchproject │ ├── packages.config │ ├── KeyPlayer.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── MessageFacts.cs │ ├── KeyProviderTests.cs │ ├── KeyStreams.cs │ ├── MessageProviderFacts.cs │ ├── ViewModels │ │ └── ShellViewModelFacts.cs │ └── Carnac.Tests.csproj ├── KeyStreamCapture │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── packages.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── KeyStreamCapture.ncrunchproject │ ├── KeyStreamCapture.v2.ncrunchproject │ ├── MainWindow.xaml │ ├── app.config │ └── MainWindow.xaml.cs ├── Carnac.ncrunchsolution └── Carnac.sln ├── .gitignore ├── README.md └── LICENSE.md /tools/AzCopy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/AzCopy.exe -------------------------------------------------------------------------------- /src/Carnac/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac/icon.ico -------------------------------------------------------------------------------- /src/.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/.nuget/NuGet.exe -------------------------------------------------------------------------------- /src/Carnac/IShell.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac { 2 | public interface IShell { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /src/Carnac/carnac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac/carnac.png -------------------------------------------------------------------------------- /tools/xunit/xunit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.dll -------------------------------------------------------------------------------- /src/Carnac/carnac_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac/carnac_2.png -------------------------------------------------------------------------------- /tools/xunit/xunit.gui.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.gui.exe -------------------------------------------------------------------------------- /src/Carnac/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac 2 | { 3 | public partial class App 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Carnac/icon.embedded.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac/icon.embedded.ico -------------------------------------------------------------------------------- /tools/xunit/xunit.console.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.console.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.gui.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.gui.x86.exe -------------------------------------------------------------------------------- /src/Carnac/Resources/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac/Resources/Entypo.ttf -------------------------------------------------------------------------------- /tools/xunit/xunit.gui.clr4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.gui.clr4.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.installer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.installer.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.console.clr4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.console.clr4.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.console.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.console.x86.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.extensions.dll -------------------------------------------------------------------------------- /tools/xunit/xunit.gui.clr4.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.gui.clr4.x86.exe -------------------------------------------------------------------------------- /tools/xunit/xunit.runner.tdnet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.runner.tdnet.dll -------------------------------------------------------------------------------- /src/Carnac.Logic/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Carnac.Tests/System.Reactive.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/src/Carnac.Tests/System.Reactive.dll -------------------------------------------------------------------------------- /tools/xunit/xunit.runner.msbuild.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.runner.msbuild.dll -------------------------------------------------------------------------------- /tools/xunit/xunit.runner.utility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.runner.utility.dll -------------------------------------------------------------------------------- /tools/xunit/xunit.console.clr4.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/xunit/xunit.console.clr4.x86.exe -------------------------------------------------------------------------------- /src/Carnac/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tools/Microsoft.WindowsAzure.Storage.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/Microsoft.WindowsAzure.Storage.dll -------------------------------------------------------------------------------- /tools/Microsoft.WindowsAzure.Storage.DataMovement.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanselman/carnac/master/tools/Microsoft.WindowsAzure.Storage.DataMovement.dll -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyMonitor/KeyDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac.Logic.KeyMonitor 2 | { 3 | public enum KeyDirection 4 | { 5 | Down, 6 | Up, 7 | Unknown 8 | } 9 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Keymaps/konami.yml: -------------------------------------------------------------------------------- 1 | group: Konami 2 | process: 3 | 4 | shortcuts: 5 | - name: Konami!!! 6 | keys: 7 | - Up,Up,Down,Down,Left,Right,Left,Right,B,A -------------------------------------------------------------------------------- /src/.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Carnac.Logic/IMessageProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Carnac.Logic.Models; 3 | 4 | namespace Carnac.Logic 5 | { 6 | public interface IMessageProvider : IObservable 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Carnac/Resources/Entypo-license.txt: -------------------------------------------------------------------------------- 1 | Entypo (http://www.entypo.com/) is created by Daniel Bruce and released under the Creative Commons, Share Alike/Attribution license. 2 | 3 | http://creativecommons.org/licenses/by-sa/3.0/ -------------------------------------------------------------------------------- /tools/xunit/xunit.dll.tdnet: -------------------------------------------------------------------------------- 1 | 2 | xUnit.net {0}.{1}.{2} build {3} 3 | xunit.runner.tdnet.dll 4 | Xunit.Runner.TdNet.TdNetRunner 5 | -------------------------------------------------------------------------------- /src/Carnac/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Enums/NotificationPlacement.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac.Logic.Enums 2 | { 3 | public enum NotificationPlacement 4 | { 5 | TopLeft = 1, 6 | BottomLeft = 2, 7 | TopRight = 3, 8 | BottomRight = 4 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Carnac.Logic/IKeyProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Carnac.Logic.Models; 4 | 5 | namespace Carnac.Logic 6 | { 7 | public interface IKeyProvider : IObservable 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Native/POINTL.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Carnac.Logic.Native 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct POINTL 7 | { 8 | public int x; 9 | public int y; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/IShortcutProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Carnac.Logic.Models; 3 | 4 | namespace Carnac.Logic 5 | { 6 | public interface IShortcutProvider 7 | { 8 | IEnumerable GetShortcutsMatching(IEnumerable keys); 9 | } 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | *.user 4 | /TestResults 5 | *.vspscc 6 | *.vssscc 7 | deploy 8 | deploy/* 9 | *.suo 10 | *.cache 11 | packages/ 12 | msbuild.log 13 | artifacts/log 14 | artifacts/ 15 | post-build* 16 | _ReSharper.* 17 | deploy-to-ec2* 18 | *.ncrunchsolution 19 | *.orig 20 | Thumbs.db -------------------------------------------------------------------------------- /src/Carnac/Utilities/ITimerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | 4 | namespace Carnac.Utilities 5 | { 6 | [InheritedExport] 7 | public interface ITimerFactory 8 | { 9 | IDisposable Start(int period, Action callback); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Carnac.Logic/IScreenManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.Composition; 3 | using Carnac.Logic.Native; 4 | 5 | namespace Carnac.Logic 6 | { 7 | [InheritedExport] 8 | public interface IScreenManager 9 | { 10 | IEnumerable GetScreens(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Carnac/CarnacWindowManager.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using Caliburn.Micro; 3 | 4 | namespace Carnac 5 | { 6 | public class CarnacWindowManager : WindowManager 7 | { 8 | public Window CreateWindow(object rootModel) 9 | { 10 | return CreateWindow(rootModel, false, null, null); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/KeyStreamCapture/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Carnac.Logic/IPasswordModeService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Carnac.Logic.KeyMonitor; 3 | 4 | namespace Carnac.Logic 5 | { 6 | public interface IPasswordModeService 7 | { 8 | bool CheckPasswordMode(InterceptKeyEventArgs key); 9 | IEnumerable PasswordKeyCombination { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/KeyStreamCapture/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace KeyStreamCapture 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Carnac.Tests/SpecificationFor.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac.Tests 2 | { 3 | public abstract class SpecificationFor 4 | { 5 | public T Subject; 6 | 7 | public abstract T Given(); 8 | public abstract void When(); 9 | 10 | protected SpecificationFor() 11 | { 12 | Subject = Given(); 13 | When(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Carnac/Utilities/TimerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Carnac.Utilities 4 | { 5 | public class TimerFactory : ITimerFactory 6 | { 7 | public IDisposable Start(int period, Action callback) 8 | { 9 | var timerState = new TimerState(period, callback); 10 | timerState.Start(); 11 | return timerState; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/ShortcutCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using Carnac.Logic.Models; 5 | 6 | namespace Carnac.Logic 7 | { 8 | public class ShortcutCollection : Collection 9 | { 10 | public string Group { get; set; } 11 | public string Process { get; set; } 12 | 13 | public IEnumerable GetShortcutsMatching(IEnumerable keys) 14 | { 15 | var matches = this.Where(s => s.StartsWith(keys)); 16 | return matches; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /tools/xunit/xunit.console.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tools/xunit/xunit.console.x86.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Carnac.Logic/NotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Carnac.Logic 4 | { 5 | public class NotifyPropertyChanged : INotifyPropertyChanged 6 | { 7 | public event PropertyChangedEventHandler PropertyChanged; 8 | 9 | protected void OnPropertyChanged(PropertyChangedEventArgs e) 10 | { 11 | var handler = PropertyChanged; 12 | if (handler != null) handler(this, e); 13 | } 14 | 15 | protected void OnPropertyChanged(string propertyName) 16 | { 17 | OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tools/xunit/xunit.console.clr4.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tools/xunit/xunit.console.clr4.x86.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Carnac/App.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Carnac/ViewModels/KeyShowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Caliburn.Micro; 3 | using Carnac.Logic.Models; 4 | using Message = Carnac.Logic.Models.Message; 5 | 6 | namespace Carnac.ViewModels 7 | { 8 | public class KeyShowViewModel: Screen 9 | { 10 | public KeyShowViewModel(ObservableCollection keys, PopupSettings popupSettings) 11 | { 12 | Keys = keys; 13 | Settings = popupSettings; 14 | } 15 | 16 | public ObservableCollection Keys { get; private set; } 17 | 18 | public PopupSettings Settings { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Carnac/KeyMonitor/HotKeyWinApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Forms; 4 | using System.Windows.Input; 5 | 6 | namespace Carnac.KeyMonitor 7 | { 8 | /// 9 | /// First off all we need to import WinAPI methods 10 | /// 11 | public class HotKeyWinApi 12 | { 13 | public const int WmHotKey = 0x0312; 14 | 15 | [DllImport("user32.dll", SetLastError = true)] 16 | public static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifiers, Keys vk); 17 | 18 | [DllImport("user32.dll", SetLastError = true)] 19 | public static extern bool UnregisterHotKey(IntPtr hWnd, int id); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Carnac.Tests/Carnac.Tests.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | true 7 | true 8 | true 9 | true 10 | 60000 11 | 12 | 13 | AutoDetect 14 | -------------------------------------------------------------------------------- /src/Carnac.Tests/Carnac.Tests.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | true 7 | true 8 | true 9 | true 10 | 60000 11 | 12 | 13 | AutoDetect 14 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/KeyStreamCapture.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | true 7 | true 8 | true 9 | true 10 | 60000 11 | 12 | 13 | AutoDetect 14 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/KeyStreamCapture.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | true 7 | true 8 | true 9 | true 10 | 60000 11 | 12 | 13 | AutoDetect 14 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Native/DetailedScreen.cs: -------------------------------------------------------------------------------- 1 | namespace Carnac.Logic.Native 2 | { 3 | public class DetailedScreen 4 | { 5 | public int Index { get; set; } 6 | public string FriendlyName { get; set; } 7 | public double Width { get; set; } 8 | public double Height { get; set; } 9 | 10 | public double RelativeHeight { get; set; } 11 | public double RelativeWidth { get; set; } 12 | 13 | public double Top { get; set; } 14 | public double Left { get; set; } 15 | 16 | public bool NotificationPlacementTopLeft { get; set; } 17 | public bool NotificationPlacementBottomLeft { get; set; } 18 | public bool NotificationPlacementTopRight { get; set; } 19 | public bool NotificationPlacementBottomRight { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Native/DISPLAY_DEVICE.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Carnac.Logic.Native 4 | { 5 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 6 | public struct DISPLAY_DEVICE 7 | { 8 | [MarshalAs(UnmanagedType.U4)] 9 | public int cb; 10 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 11 | public string DeviceName; 12 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 13 | public string DeviceString; 14 | [MarshalAs(UnmanagedType.U4)] 15 | public DisplayDeviceStateFlags StateFlags; 16 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 17 | public string DeviceID; 18 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 19 | public string DeviceKey; 20 | } 21 | } -------------------------------------------------------------------------------- /src/Carnac.ncrunchsolution: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | True 4 | false 5 | UseDynamicAnalysis 6 | UseStaticAnalysis 7 | UseStaticAnalysis 8 | UseStaticAnalysis 9 | Run all tests automatically:BFRydWU=;Run all tests manually:BUZhbHNl;Run impacted tests automatically, others manually (experimental!):CklzSW1wYWN0ZWQ=;Run pinned tests automatically, others manually:CElzUGlubmVk 10 | -------------------------------------------------------------------------------- /src/Carnac.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Carnac/Carnac.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | true 7 | true 8 | true 9 | true 10 | 60000 11 | 12 | 13 | AutoDetect 14 | ..\Tools\NotifyPropertyWeaverMsBuildTask.dll 15 | -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyMonitor/InterceptKeyEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace Carnac.Logic.KeyMonitor 5 | { 6 | public class InterceptKeyEventArgs : EventArgs 7 | { 8 | public InterceptKeyEventArgs(Keys key, KeyDirection keyDirection, bool altPressed, bool controlPressed, bool shiftPressed) 9 | { 10 | AltPressed = altPressed; 11 | ControlPressed = controlPressed; 12 | Key = key; 13 | KeyDirection = keyDirection; 14 | ShiftPressed = shiftPressed; 15 | } 16 | 17 | public bool Handled { get; set; } 18 | public bool AltPressed { get; private set; } 19 | public bool ControlPressed { get; private set; } 20 | public bool ShiftPressed { get; private set; } 21 | public Keys Key { get; private set; } 22 | public KeyDirection KeyDirection { get; private set; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Carnac.Logic/NotifyProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Carnac.Logic 4 | { 5 | 6 | /// 7 | /// Enables property notification. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] 10 | public class NotifyPropertyAttribute : Attribute 11 | { 12 | /// 13 | /// true to perform an equality check before firing property notification; otherwise false. 14 | /// 15 | /// Defaults to false. 16 | public bool PerformEqualityCheck { get; set; } 17 | 18 | /// 19 | /// The names of other properties to perform notification for. 20 | /// 21 | public string[] AlsoNotifyFor { get; set; } 22 | 23 | /// 24 | /// Gets the object's changed status. 25 | /// 26 | public bool SetIsChanged { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Native/DisplayDeviceStateFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Carnac.Logic.Native 4 | { 5 | [Flags()] 6 | public enum DisplayDeviceStateFlags : int 7 | { 8 | /// The device is part of the desktop. 9 | AttachedToDesktop = 0x1, 10 | MultiDriver = 0x2, 11 | /// The device is part of the desktop. 12 | PrimaryDevice = 0x4, 13 | /// Represents a pseudo device used to mirror application drawing for remoting or other purposes. 14 | MirroringDriver = 0x8, 15 | /// The device is VGA compatible. 16 | VGACompatible = 0x16, 17 | /// The device is removable; it cannot be the primary display. 18 | Removable = 0x20, 19 | /// The device has more display modes than its output devices support. 20 | ModesPruned = 0x8000000, 21 | Remote = 0x4000000, 22 | Disconnect = 0x2000000 23 | } 24 | } -------------------------------------------------------------------------------- /src/Carnac.Tests/KeyPlayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Subjects; 4 | using Carnac.Logic.KeyMonitor; 5 | 6 | namespace Carnac.Tests 7 | { 8 | public class KeyPlayer : List, IObservable 9 | { 10 | readonly Subject subject = new Subject(); 11 | 12 | public IDisposable Subscribe(IObserver observer) 13 | { 14 | return subject.Subscribe(observer); 15 | } 16 | 17 | public void Play() 18 | { 19 | foreach (var key in this) 20 | { 21 | subject.OnNext(key); 22 | } 23 | subject.OnCompleted(); 24 | } 25 | 26 | public void Play(Subject interceptKeysSource) 27 | { 28 | foreach (var key in this) 29 | { 30 | interceptKeysSource.OnNext(key); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Carnac.Logic.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | false 6 | false 7 | true 8 | true 9 | false 10 | true 11 | true 12 | 60000 13 | 14 | 15 | AutoDetect 16 | ..\tools\NotifyPropertyWeaverMsBuildTask.dll 17 | -------------------------------------------------------------------------------- /src/Carnac/Utilities/TimerState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Timers; 3 | using System.Windows; 4 | using System.Windows.Threading; 5 | 6 | namespace Carnac.Utilities 7 | { 8 | public class TimerState : IDisposable 9 | { 10 | readonly Timer timer; 11 | bool isDisposing; 12 | 13 | public TimerState(int period, Action callback) 14 | { 15 | timer = new Timer(period); 16 | timer.Elapsed += 17 | (s, e) => 18 | { 19 | if (Application.Current == null || Application.Current.Dispatcher == null) return; 20 | Application.Current.Dispatcher.BeginInvoke(callback, DispatcherPriority.Background, null); 21 | }; 22 | } 23 | 24 | public void Dispose() 25 | { 26 | if (isDisposing) 27 | return; 28 | 29 | isDisposing = true; 30 | timer.Stop(); 31 | } 32 | 33 | public void Start() 34 | { 35 | timer.Start(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Carnac the Magnificent Keyboard Utility 2 | 3 | A keyboard logging and presentation utility for presentations, screencasts, and to help you become a better keyboard user. 4 | 5 | ### Installation 6 | You can install via ClickOnce from [http://ginnivan.blob.core.windows.net/carnac/Carnac.application](http://ginnivan.blob.core.windows.net/carnac/Carnac.application) 7 | 8 | ### Getting started 9 | 10 | **Getting started with Git and GitHub** 11 | 12 | * [Setting up Git for Windows and connecting to GitHub](http://help.github.com/win-set-up-git/) 13 | * [Forking a GitHub repository](http://help.github.com/fork-a-repo/) 14 | * [The simple guide to GIT guide](http://rogerdudler.github.com/git-guide/) 15 | 16 | Once you're familiar with Git and GitHub, clone the repository and run the ```.\build.cmd``` script to compile the code and run all the unit tests. You can use this script to test your changes quickly. 17 | 18 | ### Discussing ideas 19 | 20 | * [Trello Board](https://trello.com/board/carnac/4f38fe6ec2fe26391c4e7d34) - add ideas, or claim an idea and start working on it! 21 | * [JabbR Chatroom](http://jabbr.net/#/rooms/code52) - discuss things in real-time with people all over the world! 22 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Internal/FixedQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Carnac.Logic.Internal 4 | { 5 | internal class FixedQueue : IEnumerable 6 | { 7 | private readonly int fixedSize; 8 | private readonly Queue queue; 9 | 10 | public FixedQueue(int fixedSize) 11 | { 12 | this.fixedSize = fixedSize; 13 | queue = new Queue(); 14 | } 15 | 16 | public void Enqueue(T item) 17 | { 18 | queue.Enqueue(item); 19 | if (queue.Count > fixedSize) 20 | { 21 | queue.Dequeue(); 22 | } 23 | } 24 | 25 | public void Clear() 26 | { 27 | queue.Clear(); 28 | } 29 | 30 | public IEnumerator GetEnumerator() 31 | { 32 | return queue.GetEnumerator(); 33 | } 34 | 35 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 36 | { 37 | return GetEnumerator(); 38 | } 39 | 40 | public int FixedSize 41 | { 42 | get { return fixedSize; } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Carnac/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.32559 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Carnac.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.530 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace KeyStreamCapture.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Carnac/Views/PlacementMarginConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using Carnac.Logic.Native; 6 | 7 | namespace Carnac.Views 8 | { 9 | public class PlacementMarginConverter : IMultiValueConverter 10 | { 11 | public object Convert(object[] values, Type targetType, object parameter, 12 | CultureInfo culture) 13 | { 14 | if ((bool)values[2] == false || values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue) 15 | return new Thickness(0); 16 | 17 | var or = (Thickness)values[0]; 18 | var sc = (DetailedScreen)values[1]; 19 | 20 | var th = new Thickness 21 | { 22 | Top = or.Top*(sc.RelativeHeight/sc.Height), 23 | Bottom = or.Bottom*(sc.RelativeHeight/sc.Height), 24 | Left = or.Left*(sc.RelativeWidth/sc.Width), 25 | Right = or.Right*(sc.RelativeWidth/sc.Width) 26 | }; 27 | 28 | return th; 29 | 30 | } 31 | 32 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, 33 | CultureInfo culture) 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Carnac/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Models/KeyPress.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Windows.Forms; 4 | using Carnac.Logic.KeyMonitor; 5 | 6 | namespace Carnac.Logic.Models 7 | { 8 | public class KeyPress : KeyPressDefinition 9 | { 10 | public KeyPress(Process process, InterceptKeyEventArgs interceptKeyEventArgs, bool winkeyPressed, IEnumerable input): 11 | base(interceptKeyEventArgs.Key, winkeyPressed, interceptKeyEventArgs.ShiftPressed, interceptKeyEventArgs.AltPressed, interceptKeyEventArgs.ControlPressed) 12 | { 13 | Process = process; 14 | InterceptKeyEventArgs = interceptKeyEventArgs; 15 | Input = input; 16 | } 17 | 18 | public Process Process { get; private set; } 19 | public InterceptKeyEventArgs InterceptKeyEventArgs { get; private set; } 20 | public IEnumerable Input { get; private set; } 21 | 22 | public bool IsShortcut 23 | { 24 | get 25 | { 26 | return InterceptKeyEventArgs.AltPressed || InterceptKeyEventArgs.ControlPressed || WinkeyPressed; 27 | } 28 | } 29 | 30 | public bool IsLetter 31 | { 32 | get 33 | { 34 | return InterceptKeyEventArgs.Key >= Keys.A && InterceptKeyEventArgs.Key <= Keys.Z; 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyShortcut.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Carnac.Logic.Models; 4 | 5 | namespace Carnac.Logic 6 | { 7 | public class KeyShortcut 8 | { 9 | private readonly KeyPressDefinition[] keyCombinations; 10 | 11 | public KeyShortcut(string name, params KeyPressDefinition[] keyCombinations) 12 | { 13 | Name = name; 14 | this.keyCombinations = keyCombinations; 15 | } 16 | 17 | public string Name { get; private set; } 18 | 19 | public bool StartsWith(IEnumerable keyPresses) 20 | { 21 | var index = 0; 22 | return keyPresses.All(keyPress => keyCombinations.Length > index && keyCombinations[index++].Equals(keyPress)); 23 | } 24 | 25 | public bool EndsWith(IEnumerable keyPresses) 26 | { 27 | var index = keyCombinations.Length - keyPresses.Count(); 28 | bool result = keyPresses.All(keyPress => keyCombinations.Length > index && keyCombinations[index++].Equals(keyPress)); 29 | return result; 30 | } 31 | 32 | public bool IsMatch(IEnumerable keyPresses) 33 | { 34 | var index = 0; 35 | return keyPresses.All(keyPress => keyCombinations.Length > index && keyCombinations[index++].Equals(keyPress)) && index == keyCombinations.Length; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Carnac.Logic")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Carnac.Logic")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("1efdbb8a-fe0c-4f1b-bf2a-7db06587fac6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Carnac.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Carnac.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Carnac.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d994d6b8-fb84-457b-bd09-ba93e892d9d7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Keymaps/ncrunch.yml: -------------------------------------------------------------------------------- 1 | group: NCrunch 2 | process: devenv 3 | 4 | shortcuts: 5 | - name: NCrunch.BreakIntoFirstCoveringTest 6 | keys: 7 | - Ctrl+M, B 8 | 9 | - name: NCrunch.ClearProcessingQueueofPendingTests 10 | keys: 11 | - Ctrl+R, Ctrl+S 12 | 13 | - name: NCrunch.DebugCoveringTests 14 | keys: 15 | - Ctrl+M, T 16 | 17 | - name: NCrunch.DebugCoveringTestsInNewProcess 18 | keys: 19 | - Ctrl+M, N 20 | 21 | - name: NCrunch.DisableNCrunch 22 | keys: 23 | - Ctrl+M, D 24 | 25 | - name: NCrunch.EnableNCrunch 26 | keys: 27 | - Ctrl+M, E 28 | 29 | - name: NCrunch.IgnoreCoverIngTest 30 | keys: 31 | - Ctrl+M, I 32 | 33 | - name: NCrunch.PinCoveringTestsToTestsWindow 34 | keys: 35 | - Ctrl+M, P 36 | 37 | - name: NCrunch.ProcessingQueue 38 | keys: 39 | - Ctrl+Shift+Q 40 | 41 | - name: NCrunch.RunAllTestsRightNow 42 | keys: 43 | - Ctrl+R, Ctrl+J 44 | 45 | - name: NCrunch.RunCoveringTestsInNewProcess 46 | keys: 47 | - Ctrl+M, U 48 | 49 | - name: NCrunch.RunprioritiseCoveringTests 50 | keys: 51 | - Ctrl+M, R 52 | 53 | - name: NCrunch.RunPrioritiseTestsPinnedToTestsWindow 54 | keys: 55 | - Ctrl+R, Ctrl+P 56 | 57 | - name: NCrunch.RunPrioritiseTestsVisibleinTestsWindow 58 | keys: 59 | - Ctrl+R, Ctrl+K 60 | 61 | - name: NCrunch.ShowCoveringTests 62 | keys: 63 | - Ctrl+M, S 64 | 65 | - name: NCrunch.ShowExceptionDetails 66 | keys: 67 | - Ctrl+M, X 68 | 69 | - name: NCrunch.Tests 70 | keys: 71 | - Ctrl+Shift+M 72 | -------------------------------------------------------------------------------- /src/Carnac/Carnac.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | true 6 | false 7 | false 8 | false 9 | false 10 | false 11 | true 12 | true 13 | false 14 | true 15 | true 16 | true 17 | 60000 18 | 19 | 20 | 21 | AutoDetect 22 | STA 23 | x86 24 | ..\packages\Fody.1.19.1.0\FodyCommon.dll;..\packages\Fody.1.19.1.0\FodyCommon.pdb;..\packages\Fody.1.19.1.0\FodyIsolated.dll;..\packages\Fody.1.19.1.0\FodyIsolated.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Mdb.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Mdb.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.Pdb.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Pdb.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.Rocks.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Rocks.pdb 25 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Carnac.Logic.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | false 5 | true 6 | false 7 | false 8 | false 9 | false 10 | false 11 | true 12 | true 13 | false 14 | true 15 | true 16 | true 17 | 60000 18 | 19 | 20 | 21 | AutoDetect 22 | STA 23 | x86 24 | ..\packages\Fody.1.19.1.0\FodyCommon.dll;..\packages\Fody.1.19.1.0\FodyCommon.pdb;..\packages\Fody.1.19.1.0\FodyIsolated.dll;..\packages\Fody.1.19.1.0\FodyIsolated.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Mdb.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Mdb.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.Pdb.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Pdb.pdb;..\packages\Fody.1.19.1.0\Mono.Cecil.Rocks.dll;..\packages\Fody.1.19.1.0\Mono.Cecil.Rocks.pdb 25 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Win32Methods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Carnac.Logic 5 | { 6 | public static class Win32Methods 7 | { 8 | 9 | public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); 10 | 11 | // ReSharper disable InconsistentNaming 12 | // 13 | 14 | public const int WH_KEYBOARD_LL = 13; 15 | public const int WM_KEYDOWN = 256; 16 | public const int WM_KEYUP = 257; 17 | public const int WM_SYSKEYUP = 261; 18 | public const int WM_SYSKEYDOWN = 260; 19 | public const int WS_EX_TRANSPARENT = 0x00000020; 20 | public const int GWL_EXSTYLE = (-20); 21 | 22 | // 23 | // ReSharper restore InconsistentNaming 24 | 25 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 26 | public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); 27 | 28 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 29 | [return: MarshalAs(UnmanagedType.Bool)] 30 | public static extern bool UnhookWindowsHookEx(IntPtr hhk); 31 | 32 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 33 | public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 34 | 35 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 36 | public static extern IntPtr GetModuleHandle(string lpModuleName); 37 | 38 | [DllImport("user32.dll")] 39 | static extern int GetWindowLong(IntPtr hwnd, int index); 40 | 41 | [DllImport("user32.dll")] 42 | static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle); 43 | 44 | public static void SetWindowExTransparent(IntPtr hwnd) 45 | { 46 | var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); 47 | SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Carnac.Tests/MessageFacts.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | using Carnac.Logic.KeyMonitor; 3 | using Carnac.Logic.Models; 4 | using Xunit; 5 | using Message = Carnac.Logic.Models.Message; 6 | 7 | namespace Carnac.Tests 8 | { 9 | public class MessageFacts 10 | { 11 | [Fact] 12 | public void message_groups_multiple_backspace_key_presses_together() 13 | { 14 | // arrange 15 | var message = new Message(); 16 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.Back, KeyDirection.Down, false, false, false), false, new[]{"Back"})); 17 | 18 | // act 19 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.Back, KeyDirection.Down, false, false, false), false, new[] { "Back" })); 20 | 21 | // assert 22 | Assert.Equal("Back x 2 ", string.Join(string.Empty, message.Text)); 23 | } 24 | 25 | [Fact] 26 | public void message_groups_multiple_arrow_key_presses_together() 27 | { 28 | // arrange 29 | var message = new Message(); 30 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.Down, KeyDirection.Down, false, false, false), false, new[] { "Down" })); 31 | 32 | // act 33 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.Down, KeyDirection.Down, false, false, false), false, new[] { "Down" })); 34 | 35 | // assert 36 | Assert.Equal("↓ x 2 ", string.Join(string.Empty, message.Text)); 37 | } 38 | 39 | [Fact] 40 | public void multiple_shortcuts_have_comma_inserted_between_input() 41 | { 42 | // arrange 43 | var message = new Message(); 44 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.R, KeyDirection.Down, false, true, false), false, new[] { "Control", "R" })); 45 | 46 | // act 47 | message.AddKey(new KeyPress(null, new InterceptKeyEventArgs(Keys.T, KeyDirection.Down, false, true, false), false, new[] { "Control", "T" })); 48 | 49 | // assert 50 | Assert.Equal("Control + R, Control + T", string.Join(string.Empty, message.Text)); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyPressDefinition.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace Carnac.Logic 4 | { 5 | public class KeyPressDefinition 6 | { 7 | public KeyPressDefinition( 8 | Keys key, 9 | bool winkeyPressed = false, 10 | bool shiftPressed = false, 11 | bool altPressed = false, 12 | bool controlPressed = false) 13 | { 14 | Key = key; 15 | ControlPressed = controlPressed; 16 | AltPressed = altPressed; 17 | ShiftPressed = shiftPressed; 18 | WinkeyPressed = winkeyPressed; 19 | } 20 | 21 | public Keys Key { get; private set; } 22 | public bool ControlPressed { get; private set; } 23 | public bool AltPressed { get; private set; } 24 | public bool ShiftPressed { get; private set; } 25 | public bool WinkeyPressed { get; private set; } 26 | 27 | public bool Equals(KeyPressDefinition other) 28 | { 29 | if (ReferenceEquals(null, other)) return false; 30 | if (ReferenceEquals(this, other)) return true; 31 | return Equals(other.Key, Key) && 32 | other.ControlPressed.Equals(ControlPressed) && 33 | other.AltPressed.Equals(AltPressed) && 34 | other.ShiftPressed.Equals(ShiftPressed) && 35 | other.WinkeyPressed.Equals(WinkeyPressed); 36 | } 37 | 38 | public override bool Equals(object obj) 39 | { 40 | if (ReferenceEquals(null, obj)) return false; 41 | if (ReferenceEquals(this, obj)) return true; 42 | if (obj.GetType() != typeof(KeyPressDefinition)) return false; 43 | return Equals((KeyPressDefinition)obj); 44 | } 45 | 46 | public override int GetHashCode() 47 | { 48 | unchecked 49 | { 50 | int result = Key.GetHashCode(); 51 | result = (result * 397) ^ ControlPressed.GetHashCode(); 52 | result = (result * 397) ^ AltPressed.GetHashCode(); 53 | result = (result * 397) ^ ShiftPressed.GetHashCode(); 54 | result = (result * 397) ^ WinkeyPressed.GetHashCode(); 55 | return result; 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Carnac/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Carnac")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Carnac")] 15 | [assembly: AssemblyCopyright("Copyright © Code52 2012")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("KeyStreamCapture")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("KeyStreamCapture")] 15 | [assembly: AssemblyCopyright("Copyright © 2012")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /tools/xunit/EULA.txt: -------------------------------------------------------------------------------- 1 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 2 | 3 | 1. Definitions 4 | 5 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. 6 | 7 | A "contribution" is the original software, or any additions or changes to the software. 8 | 9 | A "contributor" is any person that distributes its contribution under this license. 10 | 11 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 12 | 13 | 2. Grant of Rights 14 | 15 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 16 | 17 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 18 | 19 | 3. Conditions and Limitations 20 | 21 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 22 | 23 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 24 | 25 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 26 | 27 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 28 | 29 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ###Microsoft Public License (MS-PL) 2 | 3 | This license governs use of the accompanying software. If you use the software, you 4 | accept this license. If you do not accept the license, do not use the software. 5 | 6 | 1. Definitions 7 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 8 | same meaning here as under U.S. copyright law. 9 | A "contribution" is the original software, or any additions or changes to the software. 10 | A "contributor" is any person that distributes its contribution under this license. 11 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 12 | 13 | 2. Grant of Rights 14 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 15 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 16 | 17 | 3. Conditions and Limitations 18 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 19 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 20 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 21 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 22 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -------------------------------------------------------------------------------- /src/.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 6 | $(NuGetToolsPath)\nuget.exe 7 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 8 | $([System.IO.Path]::Combine($(SolutionDir), "packages")) 9 | $(TargetDir.Trim('\\')) 10 | 11 | 12 | "" 13 | 14 | 15 | false 16 | 17 | 18 | false 19 | 20 | 21 | "$(NuGetExePath)" install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)" 22 | "$(NuGetExePath)" pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols 23 | 24 | 25 | 26 | RestorePackages; 27 | $(BuildDependsOn); 28 | 29 | 30 | 31 | 32 | $(BuildDependsOn); 33 | BuildPackage; 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 51 | 52 | -------------------------------------------------------------------------------- /src/Carnac/Utilities/ProcessUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using System.Windows; 6 | 7 | namespace Carnac.Utilities 8 | { 9 | public static class ProcessUtilities 10 | { 11 | private static Mutex mutex = null; 12 | 13 | [DllImport("user32.dll", SetLastError = true)] 14 | static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 15 | 16 | [DllImport("user32.dll")] 17 | [return: MarshalAs(UnmanagedType.Bool)] 18 | static extern bool SetForegroundWindow(IntPtr hWnd); 19 | 20 | [DllImport("user32.dll")] 21 | static extern bool IsIconic(IntPtr hWnd); 22 | 23 | [DllImport("user32.dll")] 24 | static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 25 | 26 | const int SW_RESTORE = 9; 27 | 28 | [DllImport("user32.dll")] 29 | static extern IntPtr GetLastActivePopup(IntPtr hWnd); 30 | 31 | [DllImport("user32.dll")] 32 | static extern bool IsWindowEnabled(IntPtr hWnd); 33 | 34 | /// Determine if the current process is already running 35 | public static bool ThisProcessIsAlreadyRunning() 36 | { 37 | bool createdNew; 38 | mutex = new Mutex(false, Application.Current.MainWindow.GetType().Assembly.FullName, out createdNew); 39 | return !createdNew; 40 | } 41 | 42 | /// Set focus to the previous instance of the specified program. 43 | public static void SetFocusToPreviousInstance(string windowCaptionPart) 44 | { 45 | var hWnd = IntPtr.Zero; 46 | foreach (var process in Process.GetProcesses()) 47 | { 48 | if (process.MainWindowTitle.Contains(windowCaptionPart)) 49 | { 50 | hWnd = process.MainWindowHandle; 51 | } 52 | } 53 | // Look for previous instance of this program. 54 | //IntPtr = FindWindow(null, windowCaption); 55 | // If a previous instance of this program was found... 56 | if (hWnd != null) 57 | { 58 | // Is it displaying a popup window? 59 | var hPopupWnd = GetLastActivePopup(hWnd); 60 | // If so, set focus to the popup window. Otherwise set focus 61 | // to the program's main window. 62 | if (hPopupWnd != null && IsWindowEnabled(hPopupWnd)) 63 | { 64 | hWnd = hPopupWnd; 65 | } 66 | 67 | SetForegroundWindow(hWnd); 68 | // If program is minimized, restore it. 69 | if (IsIconic(hWnd)) 70 | { 71 | ShowWindow(hWnd, SW_RESTORE); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Models/PopupSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows; 4 | using Carnac.Logic.Enums; 5 | 6 | namespace Carnac.Logic.Models 7 | { 8 | public class PopupSettings : NotifyPropertyChanged 9 | { 10 | [DefaultValue(350)] 11 | public int ItemMaxWidth { get; set; } 12 | 13 | [DefaultValue(0.5)] 14 | public double ItemOpacity { get; set; } 15 | 16 | [DefaultValue("Black")] 17 | public string ItemBackgroundColor { get; set; } 18 | 19 | [DefaultValue("White")] 20 | public string FontColor { get; set; } 21 | 22 | [DefaultValue(40)] 23 | public int FontSize { get; set; } 24 | 25 | public int Screen { get; set; } 26 | 27 | [NotifyProperty(AlsoNotifyFor = new[] { "ScaleTransform", "Alignment" })] 28 | public NotificationPlacement Placement { get; set; } 29 | 30 | //Used to determine which from it's leftmost co-ord 31 | private double left; 32 | public double Left 33 | { 34 | get { return left; } 35 | set 36 | { 37 | left = value; 38 | OnLeftChanged(EventArgs.Empty); 39 | } 40 | } 41 | 42 | public event EventHandler LeftChanged; 43 | 44 | protected void OnLeftChanged(EventArgs e) 45 | { 46 | var handler = LeftChanged; 47 | if (handler != null) handler(this, e); 48 | } 49 | 50 | [NotifyProperty(AlsoNotifyFor = new[] { "Margins" })] 51 | public int TopOffset { get; set; } 52 | [NotifyProperty(AlsoNotifyFor = new[] { "Margins" })] 53 | public int BottomOffset { get; set; } 54 | 55 | [NotifyProperty(AlsoNotifyFor = new[] { "Margins" })] 56 | public int LeftOffset { get; set; } 57 | 58 | [NotifyProperty(AlsoNotifyFor = new[] { "Margins" })] 59 | public int RightOffset { get; set; } 60 | 61 | public double ScaleTransform 62 | { 63 | get { return Placement == NotificationPlacement.TopLeft || Placement == NotificationPlacement.TopRight ? 1 : -1; } 64 | } 65 | 66 | public string Alignment 67 | { 68 | get { return Placement == NotificationPlacement.TopLeft || Placement == NotificationPlacement.BottomLeft ? "Left" : "Right"; } 69 | } 70 | 71 | public Thickness Margins 72 | { 73 | get { return new Thickness(LeftOffset, TopOffset, RightOffset, BottomOffset); } 74 | } 75 | 76 | public string SortDescription 77 | { 78 | get { return Placement == NotificationPlacement.TopLeft || Placement == NotificationPlacement.TopRight ? "Ascending" : "Descending"; } 79 | } 80 | 81 | public bool DetectShortcutsOnly { get; set; } 82 | public bool SettingsConfigured { get; set; } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Carnac.Logic/ScreenManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using Carnac.Logic.Native; 6 | 7 | namespace Carnac.Logic 8 | { 9 | public class ScreenManager : IScreenManager 10 | { 11 | [DllImport("user32.dll")] 12 | private static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags); 13 | 14 | [DllImport("User32.dll")] 15 | private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode); 16 | 17 | public IEnumerable GetScreens() 18 | { 19 | var screens = new List(); 20 | 21 | int index = 1; 22 | var d = new DISPLAY_DEVICE(); 23 | d.cb = Marshal.SizeOf(d); 24 | try 25 | { 26 | for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++) 27 | { 28 | d.cb = Marshal.SizeOf(d); 29 | 30 | var x = new DISPLAY_DEVICE(); 31 | x.cb = Marshal.SizeOf(x); 32 | 33 | //Get the actual monitor 34 | EnumDisplayDevices(d.DeviceName, 0, ref x, 0); 35 | 36 | if (string.IsNullOrEmpty(x.DeviceName) || string.IsNullOrEmpty(x.DeviceString)) 37 | continue; 38 | 39 | var screen = new DetailedScreen { FriendlyName = x.DeviceString, Index = index++ }; 40 | 41 | var mode = new DEVMODE(); 42 | mode.dmSize = (ushort)Marshal.SizeOf(mode); 43 | if (EnumDisplaySettings(d.DeviceName, -1, ref mode)) 44 | { 45 | screen.Width = (int)mode.dmPelsWidth; 46 | screen.Height = (int)mode.dmPelsHeight; 47 | screen.Top = mode.dmPosition.y; 48 | screen.Left = mode.dmPosition.x; 49 | } 50 | 51 | screens.Add(screen); 52 | } 53 | } 54 | catch (Exception) 55 | { 56 | //log this 57 | } 58 | 59 | var biggestScreen = screens.OrderByDescending(s => s.Width).FirstOrDefault(); 60 | if (biggestScreen != null) 61 | { 62 | var maxWidth = biggestScreen.Width; 63 | foreach (var s in screens) 64 | { 65 | s.RelativeWidth = 200 * (s.Width / maxWidth); 66 | s.RelativeHeight = s.RelativeWidth * (s.Height / s.Width); 67 | s.Top *= (s.RelativeHeight / s.Height); 68 | } 69 | } 70 | 71 | screens = screens.OrderBy(s => s.Left).ToList(); 72 | 73 | return screens; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/KeyStreamCapture/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.530 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace KeyStreamCapture.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KeyStreamCapture.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Carnac.Logic/PasswordModeService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Windows.Forms; 4 | using Carnac.Logic.Internal; 5 | using Carnac.Logic.KeyMonitor; 6 | 7 | namespace Carnac.Logic 8 | { 9 | public class PasswordModeService : IPasswordModeService 10 | { 11 | private readonly FixedQueue log; 12 | private InterceptKeyEventArgs[] passwordKeyCombination; 13 | private readonly InterceptKeyEventArgsEqualityComparer comparer = new InterceptKeyEventArgsEqualityComparer(); 14 | private bool currentMode; 15 | 16 | public PasswordModeService() 17 | { 18 | log = new FixedQueue(this.PasswordKeyCombination.Count()); 19 | } 20 | 21 | public bool CheckPasswordMode(InterceptKeyEventArgs key) 22 | { 23 | log.Enqueue(key); 24 | var sortedLog = log.ToList(); 25 | sortedLog.Sort(); 26 | var isMatch = sortedLog.SequenceEqual(PasswordKeyCombination, comparer); 27 | if (isMatch) 28 | { 29 | currentMode = !currentMode; 30 | this.log.Clear(); 31 | return true; //this way when the sequence is entered again to EXIT password mode, the key password keycombo doesn't show on screen 32 | } 33 | 34 | return currentMode; 35 | } 36 | 37 | public IEnumerable PasswordKeyCombination 38 | { 39 | get 40 | { 41 | if (passwordKeyCombination == null) 42 | { 43 | passwordKeyCombination = new[] 44 | { 45 | new InterceptKeyEventArgs(Keys.P, KeyDirection.Down,true,true,false), 46 | }; 47 | } 48 | 49 | return passwordKeyCombination; 50 | } 51 | } 52 | 53 | private class InterceptKeyEventArgsEqualityComparer : IEqualityComparer 54 | { 55 | public bool Equals(InterceptKeyEventArgs x, InterceptKeyEventArgs y) 56 | { 57 | if (x == null && y == null) 58 | { 59 | return true; 60 | } 61 | 62 | if (x == null || y == null) 63 | { 64 | return false; 65 | } 66 | 67 | return x.Key == y.Key 68 | && x.ShiftPressed == y.ShiftPressed 69 | && x.AltPressed == y.AltPressed 70 | && x.ControlPressed == y.ControlPressed; 71 | } 72 | 73 | public int GetHashCode(InterceptKeyEventArgs obj) 74 | { 75 | return obj.Key.GetHashCode() << obj.AltPressed.GetHashCode() 76 | << obj.ShiftPressed.GetHashCode() << obj.ControlPressed.GetHashCode(); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/Carnac/Views/ShellView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.Reflection; 5 | using System.Windows; 6 | using System.Windows.Forms; 7 | using Carnac.Logic.Native; 8 | using Carnac.Utilities; 9 | using Carnac.ViewModels; 10 | using Application = System.Windows.Application; 11 | 12 | namespace Carnac.Views 13 | { 14 | public partial class ShellView 15 | { 16 | public ShellView() 17 | { 18 | InitializeComponent(); 19 | 20 | // Check if there was instance before this. If there was-close the current one. 21 | if (ProcessUtilities.ThisProcessIsAlreadyRunning()) 22 | { 23 | ProcessUtilities.SetFocusToPreviousInstance("Carnac"); 24 | Application.Current.Shutdown(); 25 | } 26 | 27 | var exitMenuItem = new MenuItem 28 | { 29 | Text = Properties.Resources.ShellView_Exit 30 | }; 31 | 32 | var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Carnac.icon.embedded.ico"); 33 | 34 | var ni = new NotifyIcon 35 | { 36 | Icon = new Icon(iconStream), 37 | ContextMenu = new ContextMenu(new[] { exitMenuItem }) 38 | }; 39 | 40 | exitMenuItem.Click += (sender, args) => 41 | { 42 | ni.Visible = false; 43 | Application.Current.Shutdown(); 44 | }; 45 | ni.MouseClick += NotifyIconClick; 46 | ni.Visible = true; 47 | } 48 | 49 | void NotifyIconClick(object sender, MouseEventArgs mouseEventArgs) 50 | { 51 | if (mouseEventArgs.Button == MouseButtons.Right) return; 52 | 53 | Show(); 54 | WindowState = WindowState.Normal; 55 | Topmost = true; // When it comes back, make sure it's on top... 56 | Topmost = false; // and then it doesn't need to be anymore. 57 | } 58 | 59 | protected override void OnStateChanged(EventArgs e) 60 | { 61 | base.OnStateChanged(e); 62 | 63 | if (WindowState == WindowState.Minimized) 64 | { 65 | Hide(); 66 | } 67 | } 68 | 69 | protected override void OnClosing(CancelEventArgs e) 70 | { 71 | e.Cancel = true; 72 | Hide(); 73 | base.OnClosing(e); 74 | } 75 | 76 | private void RadioChecked(object sender, RoutedEventArgs e) 77 | { 78 | var dc = DataContext as ShellViewModel; 79 | if (dc == null) 80 | return; 81 | 82 | var rb = sender as System.Windows.Controls.RadioButton; 83 | if (rb == null) 84 | return; 85 | 86 | var tag = rb.Tag as DetailedScreen; 87 | if (tag == null) 88 | return; 89 | 90 | dc.SelectedScreen = tag; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/Carnac.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.20623.1 VSUPREVIEW 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Carnac", "Carnac\Carnac.csproj", "{3D54543F-9FF2-4298-A621-0C66A36412CD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Carnac.Tests", "Carnac.Tests\Carnac.Tests.csproj", "{820B7770-FCD1-4987-A88C-E5DAE74F4AD6}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Carnac.Logic", "Carnac.Logic\Carnac.Logic.csproj", "{614470A0-432C-46EB-B0EB-DE0A9062FC22}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{8A90581C-CDD7-4F29-9325-1AACC5D11EE9}" 13 | ProjectSection(SolutionItems) = preProject 14 | .nuget\NuGet.exe = .nuget\NuGet.exe 15 | .nuget\NuGet.targets = .nuget\NuGet.targets 16 | EndProjectSection 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyStreamCapture", "KeyStreamCapture\KeyStreamCapture.csproj", "{DD585B10-CB02-49B6-9E37-42E5C605C192}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D23D1BAB-8B80-4B73-893C-F669039FC44D}" 21 | ProjectSection(SolutionItems) = preProject 22 | ..\build.cmd = ..\build.cmd 23 | ..\build.proj = ..\build.proj 24 | ..\README.md = ..\README.md 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {3D54543F-9FF2-4298-A621-0C66A36412CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {3D54543F-9FF2-4298-A621-0C66A36412CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {3D54543F-9FF2-4298-A621-0C66A36412CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {3D54543F-9FF2-4298-A621-0C66A36412CD}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {820B7770-FCD1-4987-A88C-E5DAE74F4AD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {820B7770-FCD1-4987-A88C-E5DAE74F4AD6}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {820B7770-FCD1-4987-A88C-E5DAE74F4AD6}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {820B7770-FCD1-4987-A88C-E5DAE74F4AD6}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {614470A0-432C-46EB-B0EB-DE0A9062FC22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {614470A0-432C-46EB-B0EB-DE0A9062FC22}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {614470A0-432C-46EB-B0EB-DE0A9062FC22}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {614470A0-432C-46EB-B0EB-DE0A9062FC22}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {DD585B10-CB02-49B6-9E37-42E5C605C192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {DD585B10-CB02-49B6-9E37-42E5C605C192}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {DD585B10-CB02-49B6-9E37-42E5C605C192}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {DD585B10-CB02-49B6-9E37-42E5C605C192}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | EndGlobal 54 | -------------------------------------------------------------------------------- /src/Carnac/Views/KeyShowView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | using System.Windows.Interop; 5 | using Carnac.Logic; 6 | using Carnac.ViewModels; 7 | using Timer = System.Timers.Timer; 8 | 9 | namespace Carnac.Views 10 | { 11 | public partial class KeyShowView 12 | { 13 | public KeyShowView() 14 | { 15 | InitializeComponent(); 16 | } 17 | 18 | protected override void OnSourceInitialized(EventArgs e) 19 | { 20 | base.OnSourceInitialized(e); 21 | 22 | var hwnd = new WindowInteropHelper(this).Handle; 23 | Win32Methods.SetWindowExTransparent(hwnd); 24 | 25 | var timer = new Timer(100); 26 | timer.Elapsed += 27 | (s, x) => 28 | { 29 | SetWindowPos(hwnd, 30 | HWND.TOPMOST, 31 | 0, 0, 0, 0, 32 | (uint)(SWP.NOMOVE | SWP.NOSIZE | SWP.SHOWWINDOW)); 33 | }; 34 | 35 | timer.Start(); 36 | 37 | var vm = ((KeyShowViewModel)DataContext); 38 | Left = vm.Settings.Left; 39 | vm.Settings.LeftChanged += SettingsLeftChanged; 40 | WindowState = WindowState.Maximized; 41 | } 42 | 43 | [DllImport("user32.dll", SetLastError = true)] 44 | public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int W, int H, uint uFlags); 45 | 46 | 47 | /// 48 | /// HWND values for hWndInsertAfter 49 | /// 50 | public static class HWND 51 | { 52 | public static readonly IntPtr 53 | NOTOPMOST = new IntPtr(-2), 54 | BROADCAST = new IntPtr(0xffff), 55 | TOPMOST = new IntPtr(-1), 56 | TOP = new IntPtr(0), 57 | BOTTOM = new IntPtr(1); 58 | } 59 | 60 | 61 | /// 62 | /// SetWindowPos Flags 63 | /// 64 | public static class SWP 65 | { 66 | public static readonly int 67 | NOSIZE = 0x0001, 68 | NOMOVE = 0x0002, 69 | NOZORDER = 0x0004, 70 | NOREDRAW = 0x0008, 71 | NOACTIVATE = 0x0010, 72 | DRAWFRAME = 0x0020, 73 | FRAMECHANGED = 0x0020, 74 | SHOWWINDOW = 0x0040, 75 | HIDEWINDOW = 0x0080, 76 | NOCOPYBITS = 0x0100, 77 | NOOWNERZORDER = 0x0200, 78 | NOREPOSITION = 0x0200, 79 | NOSENDCHANGING = 0x0400, 80 | DEFERERASE = 0x2000, 81 | ASYNCWINDOWPOS = 0x4000; 82 | } 83 | 84 | private void WindowLoaded(object sender, RoutedEventArgs e) 85 | { 86 | 87 | } 88 | 89 | void SettingsLeftChanged(object sender, EventArgs e) 90 | { 91 | WindowState = WindowState.Normal; 92 | var vm = ((KeyShowViewModel)DataContext); 93 | Left = vm.Settings.Left; 94 | WindowState = WindowState.Maximized; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Carnac/AppBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.ComponentModel.Composition.Hosting; 5 | using System.Linq; 6 | using System.Windows; 7 | using Caliburn.Micro; 8 | using Carnac.Logic; 9 | using Carnac.Logic.KeyMonitor; 10 | using Carnac.Logic.Models; 11 | using MahApps.Metro.Controls; 12 | using SettingsProviderNet; 13 | 14 | namespace Carnac 15 | { 16 | public class AppBootstrapper : Bootstrapper 17 | { 18 | public static EventAggregator Aggregator { get; set; } 19 | CompositionContainer container; 20 | CarnacWindowManager windowManager; 21 | SettingsProvider settingsProvider; 22 | 23 | /// 24 | /// By default, we are configured to use MEF 25 | /// 26 | protected override void Configure() 27 | { 28 | Aggregator = new EventAggregator(); 29 | 30 | var catalog = new AggregateCatalog( 31 | new AssemblyCatalog(typeof(AppBootstrapper).Assembly), 32 | new AssemblyCatalog(typeof(IScreenManager).Assembly)); 33 | 34 | container = new CompositionContainer(catalog); 35 | 36 | var batch = new CompositionBatch(); 37 | 38 | batch.AddExportedValue(new KeyProvider(InterceptKeys.Current, new PasswordModeService())); 39 | windowManager = new CarnacWindowManager(); 40 | batch.AddExportedValue(windowManager); 41 | batch.AddExportedValue(new EventAggregator()); 42 | settingsProvider = new SettingsProvider(new RoamingAppDataStorage("Carnac")); 43 | batch.AddExportedValue(settingsProvider); 44 | batch.AddExportedValue(container); 45 | batch.AddExportedValue(catalog); 46 | 47 | container.Compose(batch); 48 | } 49 | 50 | protected override object GetInstance(Type serviceType, string key) 51 | { 52 | string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key; 53 | var exports = container.GetExportedValues(contract).ToArray(); 54 | 55 | if (exports.Any()) 56 | return exports.First(); 57 | 58 | throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract)); 59 | } 60 | 61 | protected override IEnumerable GetAllInstances(Type serviceType) 62 | { 63 | return container.GetExportedValues(AttributedModelServices.GetContractName(serviceType)); 64 | } 65 | 66 | protected override void BuildUp(object instance) 67 | { 68 | container.SatisfyImportsOnce(instance); 69 | } 70 | 71 | protected override void OnStartup(object sender, StartupEventArgs e) 72 | { 73 | Shell = (IShell) IoC.GetInstance(typeof (IShell), null); 74 | var window = windowManager.CreateWindow(Shell); 75 | if (!settingsProvider.GetSettings().SettingsConfigured) 76 | window.Show(); 77 | } 78 | 79 | public IShell Shell { get; set; } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Carnac/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34003 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Carnac.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Carnac.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Exit. 65 | /// 66 | internal static string ShellView_Exit { 67 | get { 68 | return ResourceManager.GetString("ShellView_Exit", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Carnac/KeyMonitor/HotKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.Windows; 4 | using System.Windows.Forms; 5 | using System.Windows.Input; 6 | using System.Windows.Interop; 7 | 8 | namespace Carnac.KeyMonitor 9 | { 10 | namespace Common.Essentials 11 | { 12 | /// 13 | /// http://outcoldman.ru/en/blog/show/240 14 | /// 15 | public sealed class HotKey : IDisposable 16 | { 17 | public event Action HotKeyPressed; 18 | 19 | private readonly int id; 20 | private bool isKeyRegistered; 21 | readonly IntPtr handle; 22 | 23 | public HotKey(ModifierKeys modifierKeys, Keys key, Window window) 24 | : this(modifierKeys, key, new WindowInteropHelper(window)) 25 | { 26 | Contract.Requires(window != null); 27 | } 28 | 29 | public HotKey(ModifierKeys modifierKeys, Keys key, WindowInteropHelper window) 30 | : this(modifierKeys, key, window.Handle) 31 | { 32 | Contract.Requires(window != null); 33 | } 34 | 35 | public HotKey(ModifierKeys modifierKeys, Keys key, IntPtr windowHandle) 36 | { 37 | Contract.Requires(modifierKeys != ModifierKeys.None || key != Keys.None); 38 | Contract.Requires(windowHandle != IntPtr.Zero); 39 | 40 | Key = key; 41 | KeyModifier = modifierKeys; 42 | id = GetHashCode(); 43 | handle = windowHandle; 44 | RegisterHotKey(); 45 | ComponentDispatcher.ThreadPreprocessMessage += ThreadPreprocessMessageMethod; 46 | } 47 | 48 | ~HotKey() 49 | { 50 | Dispose(); 51 | } 52 | 53 | public Keys Key { get; private set; } 54 | 55 | public ModifierKeys KeyModifier { get; private set; } 56 | 57 | public void RegisterHotKey() 58 | { 59 | if (Key == Keys.None) 60 | return; 61 | if (isKeyRegistered) 62 | UnregisterHotKey(); 63 | isKeyRegistered = HotKeyWinApi.RegisterHotKey(handle, id, KeyModifier, Key); 64 | if (!isKeyRegistered) 65 | throw new ApplicationException("Hotkey already in use"); 66 | } 67 | 68 | public void UnregisterHotKey() 69 | { 70 | isKeyRegistered = !HotKeyWinApi.UnregisterHotKey(handle, id); 71 | } 72 | 73 | public void Dispose() 74 | { 75 | ComponentDispatcher.ThreadPreprocessMessage -= ThreadPreprocessMessageMethod; 76 | UnregisterHotKey(); 77 | } 78 | 79 | private void ThreadPreprocessMessageMethod(ref MSG msg, ref bool handled) 80 | { 81 | if (!handled) 82 | { 83 | if (msg.message == HotKeyWinApi.WmHotKey 84 | && (int)(msg.wParam) == id) 85 | { 86 | OnHotKeyPressed(); 87 | handled = true; 88 | } 89 | } 90 | } 91 | 92 | private void OnHotKeyPressed() 93 | { 94 | if (HotKeyPressed != null) 95 | HotKeyPressed(this); 96 | } 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Native/DEVMODE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Carnac.Logic.Native 5 | { 6 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 7 | public struct DEVMODE 8 | { 9 | // You can define the following constant 10 | // but OUTSIDE the structure because you know 11 | // that size and layout of the structure 12 | // is very important 13 | // CCHDEVICENAME = 32 = 0x50 14 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 15 | public string dmDeviceName; 16 | // In addition you can define the last character array 17 | // as following: 18 | //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 19 | //public Char[] dmDeviceName; 20 | 21 | // After the 32-bytes array 22 | [MarshalAs(UnmanagedType.U2)] 23 | public UInt16 dmSpecVersion; 24 | 25 | [MarshalAs(UnmanagedType.U2)] 26 | public UInt16 dmDriverVersion; 27 | 28 | [MarshalAs(UnmanagedType.U2)] 29 | public UInt16 dmSize; 30 | 31 | [MarshalAs(UnmanagedType.U2)] 32 | public UInt16 dmDriverExtra; 33 | 34 | [MarshalAs(UnmanagedType.U4)] 35 | public UInt32 dmFields; 36 | 37 | public POINTL dmPosition; 38 | 39 | [MarshalAs(UnmanagedType.U4)] 40 | public UInt32 dmDisplayOrientation; 41 | 42 | [MarshalAs(UnmanagedType.U4)] 43 | public UInt32 dmDisplayFixedOutput; 44 | 45 | [MarshalAs(UnmanagedType.I2)] 46 | public Int16 dmColor; 47 | 48 | [MarshalAs(UnmanagedType.I2)] 49 | public Int16 dmDuplex; 50 | 51 | [MarshalAs(UnmanagedType.I2)] 52 | public Int16 dmYResolution; 53 | 54 | [MarshalAs(UnmanagedType.I2)] 55 | public Int16 dmTTOption; 56 | 57 | [MarshalAs(UnmanagedType.I2)] 58 | public Int16 dmCollate; 59 | 60 | // CCHDEVICENAME = 32 = 0x50 61 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 62 | public string dmFormName; 63 | // Also can be defined as 64 | //[MarshalAs(UnmanagedType.ByValArray, 65 | // SizeConst = 32, ArraySubType = UnmanagedType.U1)] 66 | //public Byte[] dmFormName; 67 | 68 | [MarshalAs(UnmanagedType.U2)] 69 | public UInt16 dmLogPixels; 70 | 71 | [MarshalAs(UnmanagedType.U4)] 72 | public UInt32 dmBitsPerPel; 73 | 74 | [MarshalAs(UnmanagedType.U4)] 75 | public UInt32 dmPelsWidth; 76 | 77 | [MarshalAs(UnmanagedType.U4)] 78 | public UInt32 dmPelsHeight; 79 | 80 | [MarshalAs(UnmanagedType.U4)] 81 | public UInt32 dmDisplayFlags; 82 | 83 | [MarshalAs(UnmanagedType.U4)] 84 | public UInt32 dmDisplayFrequency; 85 | 86 | [MarshalAs(UnmanagedType.U4)] 87 | public UInt32 dmICMMethod; 88 | 89 | [MarshalAs(UnmanagedType.U4)] 90 | public UInt32 dmICMIntent; 91 | 92 | [MarshalAs(UnmanagedType.U4)] 93 | public UInt32 dmMediaType; 94 | 95 | [MarshalAs(UnmanagedType.U4)] 96 | public UInt32 dmDitherType; 97 | 98 | [MarshalAs(UnmanagedType.U4)] 99 | public UInt32 dmReserved1; 100 | 101 | [MarshalAs(UnmanagedType.U4)] 102 | public UInt32 dmReserved2; 103 | 104 | [MarshalAs(UnmanagedType.U4)] 105 | public UInt32 dmPanningWidth; 106 | 107 | [MarshalAs(UnmanagedType.U4)] 108 | public UInt32 dmPanningHeight; 109 | } 110 | } -------------------------------------------------------------------------------- /src/Carnac/Fody.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(NCrunchOriginalSolutionDir) 7 | 8 | 9 | 10 | 11 | $(SolutionDir) 12 | 13 | 14 | 15 | 16 | $(MSBuildProjectDirectory)\..\ 17 | 18 | 19 | 20 | 21 | 22 | 23 | $(KeyOriginatorFile) 24 | 25 | 26 | 27 | 28 | $(AssemblyOriginatorKeyFile) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | $(ProjectDir)$(IntermediateOutputPath) 39 | Low 40 | $(SignAssembly) 41 | $(MSBuildThisFileDirectory) 42 | 43 | 46 | 50 | 51 | 63 | 64 | 65 | 69 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Fody.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(NCrunchOriginalSolutionDir) 7 | 8 | 9 | 10 | 11 | $(SolutionDir) 12 | 13 | 14 | 15 | 16 | $(MSBuildProjectDirectory)\..\ 17 | 18 | 19 | 20 | 21 | 22 | 23 | $(KeyOriginatorFile) 24 | 25 | 26 | 27 | 28 | $(AssemblyOriginatorKeyFile) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | $(ProjectDir)$(IntermediateOutputPath) 39 | Low 40 | $(SignAssembly) 41 | $(MSBuildThisFileDirectory) 42 | 43 | 46 | 50 | 51 | 63 | 64 | 65 | 69 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/Carnac.Tests/KeyProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Carnac.Logic; 5 | using Carnac.Logic.Models; 6 | using Xunit; 7 | 8 | namespace Carnac.Tests 9 | { 10 | public class KeyProviderTests 11 | { 12 | private readonly IPasswordModeService passwordModeService; 13 | 14 | public KeyProviderTests() 15 | { 16 | passwordModeService = new PasswordModeService(); 17 | } 18 | 19 | [Fact] 20 | public void ctrlshiftl_is_processed_correctly() 21 | { 22 | // arrange 23 | var player = KeyStreams.CtrlShiftL(); 24 | var provider = new KeyProvider(player, passwordModeService); 25 | 26 | // act 27 | var processedKeys = ToEnumerable(provider, player); 28 | 29 | // assert 30 | Assert.Equal(new[] { "Ctrl", "Shift", "L" }, processedKeys.Single().Input); 31 | } 32 | 33 | [Fact] 34 | public void shift_is_not_outputted_when_is_being_used_as_a_modifier_key() 35 | { 36 | // arrange 37 | var player = KeyStreams.ShiftL(); 38 | var provider = new KeyProvider(player, passwordModeService); 39 | 40 | // act 41 | var processedKeys = ToEnumerable(provider, player); 42 | 43 | // assert 44 | 45 | Assert.Equal(new[] { "L" }, processedKeys.Single().Input); 46 | } 47 | 48 | [Fact] 49 | public void key_without_shift_is_lowercase() 50 | { 51 | // arrange 52 | var player = KeyStreams.LetterL(); 53 | var provider = new KeyProvider(player, passwordModeService); 54 | 55 | // act 56 | var processedKeys = ToEnumerable(provider, player); 57 | 58 | // assert 59 | Assert.Equal(new[] { "l" }, processedKeys.Single().Input); 60 | } 61 | 62 | [Fact] 63 | public void verify_number() 64 | { 65 | // arrange 66 | var player = KeyStreams.Number1(); 67 | var provider = new KeyProvider(player, passwordModeService); 68 | 69 | // act 70 | var processedKeys = ToEnumerable(provider, player); 71 | 72 | // assert 73 | Assert.Equal(new[] { "1" }, processedKeys.Single().Input); 74 | } 75 | 76 | [Fact] 77 | public void verify_shift_number() 78 | { 79 | // arrange 80 | var player = KeyStreams.ExclaimationMark(); 81 | var provider = new KeyProvider(player, passwordModeService); 82 | 83 | // act 84 | var processedKeys = ToEnumerable(provider, player); 85 | 86 | // assert 87 | Assert.Equal(new[] { "!" }, processedKeys.Single().Input); 88 | } 89 | 90 | [Fact] 91 | public void keyprovider_detects_windows_key_presses() 92 | { 93 | // arrange 94 | var player = KeyStreams.WinkeyE(); 95 | var provider = new KeyProvider(player, passwordModeService); 96 | 97 | // act 98 | var processedKeys = ToEnumerable(provider, player); 99 | 100 | // assert 101 | Assert.Equal(new[] { "Win", "e" }, processedKeys.Single().Input); 102 | } 103 | 104 | private static IEnumerable ToEnumerable(IObservable provider, KeyPlayer player) 105 | { 106 | var keys = new List(); 107 | 108 | provider.Subscribe(keys.Add); 109 | player.Play(); 110 | 111 | return keys; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /tools/xunit/NUnitXml.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | False 35 | True 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | False 54 | True 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | False 80 | True 81 | 82 | 83 | 84 | False 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/KeyStreamCapture/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom; 3 | using System.CodeDom.Compiler; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Reactive.Linq; 7 | using System.Text; 8 | using System.Windows; 9 | using System.Windows.Forms; 10 | using Carnac.Logic.KeyMonitor; 11 | using Carnac.Tests; 12 | using Microsoft.CSharp; 13 | 14 | namespace KeyStreamCapture 15 | { 16 | public partial class MainWindow : IObserver 17 | { 18 | private readonly List keys = new List(); 19 | private bool capturing; 20 | 21 | public MainWindow() 22 | { 23 | InitializeComponent(); 24 | InterceptKeys.Current.Subscribe(this); 25 | } 26 | 27 | private void StartCapture(object sender, RoutedEventArgs e) 28 | { 29 | keys.Clear(); 30 | capturing = true; 31 | } 32 | 33 | private void StopCapture(object sender, RoutedEventArgs e) 34 | { 35 | capturing = false; 36 | GenerateCode(); 37 | } 38 | 39 | private void GenerateCode() 40 | { 41 | keys.ToObservable(); 42 | var cgo = new CodeGeneratorOptions 43 | { 44 | BracingStyle = "C", 45 | BlankLinesBetweenMembers = false 46 | }; 47 | using (var provider = new CSharpCodeProvider()) 48 | { 49 | var method = new CodeMemberMethod 50 | { 51 | Name = "KeyStream", 52 | ReturnType = new CodeTypeReference(typeof(IObservable)) 53 | }; 54 | 55 | var player = 56 | new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(KeyPlayer)), "keys", 57 | new CodeObjectCreateExpression(typeof(KeyPlayer))); 58 | method.Statements.Add(player); 59 | foreach (var interceptKeyEventArgse in keys) 60 | { 61 | var key = new CodeObjectCreateExpression(new CodeTypeReference(typeof(InterceptKeyEventArgs)), 62 | new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(Keys)), interceptKeyEventArgse.Key.ToString()), 63 | new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(KeyDirection)), interceptKeyEventArgse.KeyDirection.ToString()), 64 | new CodePrimitiveExpression(interceptKeyEventArgse.AltPressed), 65 | new CodePrimitiveExpression(interceptKeyEventArgse.ControlPressed), 66 | new CodePrimitiveExpression(interceptKeyEventArgse.ShiftPressed)); 67 | 68 | var keyPress = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("keys"), "Add", key); 69 | method.Statements.Add(keyPress); 70 | } 71 | method.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("keys"))); 72 | 73 | var sb = new StringBuilder(); 74 | using(var stringWriter = new StringWriter(sb)) 75 | { 76 | provider.GenerateCodeFromMember(method, stringWriter, cgo); 77 | } 78 | textBox.Text = sb.ToString(); 79 | } 80 | } 81 | 82 | protected override void OnClosed(EventArgs e) 83 | { 84 | 85 | } 86 | 87 | public void OnNext(InterceptKeyEventArgs value) 88 | { 89 | if (capturing) 90 | keys.Add(value); 91 | } 92 | 93 | public void OnError(Exception error) 94 | { 95 | 96 | } 97 | 98 | public void OnCompleted() 99 | { 100 | 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Carnac.Tests/KeyStreams.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | using Carnac.Logic.KeyMonitor; 3 | 4 | namespace Carnac.Tests 5 | { 6 | public static class KeyStreams 7 | { 8 | public static KeyPlayer WinkeyE() 9 | { 10 | return new KeyPlayer 11 | { 12 | new InterceptKeyEventArgs(Keys.LWin, KeyDirection.Down, false, false, false), 13 | new InterceptKeyEventArgs(Keys.E, KeyDirection.Down, false, false, false), 14 | new InterceptKeyEventArgs(Keys.E, KeyDirection.Up, false, false, false), 15 | new InterceptKeyEventArgs(Keys.LWin, KeyDirection.Up, false, false, false), 16 | }; 17 | } 18 | 19 | public static KeyPlayer ExclaimationMark() 20 | { 21 | return new KeyPlayer 22 | { 23 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Down, false, false, false), 24 | new InterceptKeyEventArgs(Keys.D1, KeyDirection.Down, false, false, true), 25 | new InterceptKeyEventArgs(Keys.D1, KeyDirection.Up, false, false, true), 26 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Up, false, false, true), 27 | }; 28 | } 29 | 30 | public static KeyPlayer Number1() 31 | { 32 | return new KeyPlayer 33 | { 34 | new InterceptKeyEventArgs(Keys.D1, KeyDirection.Down, false, false, false), 35 | new InterceptKeyEventArgs(Keys.D1, KeyDirection.Up, false, false, false) 36 | }; 37 | } 38 | 39 | public static KeyPlayer LetterL() 40 | { 41 | return new KeyPlayer 42 | { 43 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Down, false, false, false), 44 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Up, false, false, false), 45 | }; 46 | } 47 | 48 | public static KeyPlayer ShiftL() 49 | { 50 | return new KeyPlayer 51 | { 52 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Down, false, false, false), 53 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Down, false, false, true), 54 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Up, false, false, true), 55 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Up, false, false, true), 56 | }; 57 | } 58 | 59 | public static KeyPlayer CtrlShiftL() 60 | { 61 | return new KeyPlayer 62 | { 63 | new InterceptKeyEventArgs(Keys.LControlKey, KeyDirection.Down, false, false, false), 64 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Down, false, true, false), 65 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Down, false, true, true), 66 | new InterceptKeyEventArgs(Keys.L, KeyDirection.Up, false, true, true), 67 | new InterceptKeyEventArgs(Keys.LShiftKey, KeyDirection.Up, false, true, true), 68 | new InterceptKeyEventArgs(Keys.LControlKey, KeyDirection.Up, false, true, false) 69 | }; 70 | } 71 | 72 | public static KeyPlayer CtrlU() 73 | { 74 | return new KeyPlayer 75 | { 76 | new InterceptKeyEventArgs(Keys.LControlKey, KeyDirection.Down, false, false, false), 77 | new InterceptKeyEventArgs(Keys.U, KeyDirection.Down, false, true, false), 78 | new InterceptKeyEventArgs(Keys.U, KeyDirection.Up, false, true, false), 79 | new InterceptKeyEventArgs(Keys.LControlKey, KeyDirection.Up, false, true, false) 80 | }; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/Models/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Windows.Documents; 5 | 6 | namespace Carnac.Logic.Models 7 | { 8 | public class Message : NotifyPropertyChanged 9 | { 10 | private readonly ObservableCollection textCollection; 11 | private readonly ObservableCollection keyCollection; 12 | private int lastTextRepeatCount = 1; 13 | private string lastText; 14 | private KeyPress lastKeyPress; 15 | 16 | public Message() 17 | { 18 | textCollection = new ObservableCollection(); 19 | keyCollection = new ObservableCollection(); 20 | Text = new ReadOnlyObservableCollection(textCollection); 21 | Keys = new ReadOnlyObservableCollection(keyCollection); 22 | } 23 | 24 | public string ProcessName { get; set; } 25 | 26 | public DateTime StartingTime { get; set; } 27 | public DateTime LastMessage { get; set; } 28 | public ReadOnlyObservableCollection Text { get; private set; } 29 | public ReadOnlyObservableCollection Keys { get; private set; } 30 | public int Count { get; set; } 31 | public bool IsDeleting { get; set; } 32 | 33 | private string shortcutName; 34 | readonly string[] repeatDetectionText = { "Back", "Left", "Right", "Down", "Up"}; 35 | 36 | public string ShortcutName 37 | { 38 | get { return shortcutName; } 39 | set 40 | { 41 | shortcutName = value; 42 | if (!string.IsNullOrEmpty(shortcutName)) 43 | AddText(string.Format(" [{0}]", value)); 44 | } 45 | } 46 | 47 | public void AddKey(KeyPress keyPress) 48 | { 49 | keyCollection.Add(keyPress); 50 | if (lastKeyPress != null && lastKeyPress.IsShortcut) 51 | textCollection.Add(", "); 52 | lastKeyPress = keyPress; 53 | var first = true; 54 | foreach (var text in keyPress.Input) 55 | { 56 | if (!first) 57 | AddText(" + "); 58 | AddText(text); 59 | first = false; 60 | } 61 | } 62 | 63 | private void AddText(string text) 64 | { 65 | var formattedText = Format(text, lastKeyPress.IsShortcut); 66 | 67 | if (repeatDetectionText.Contains(text) && Text.Any()) 68 | { 69 | var repeatText = string.Format(" x {0} ", ++lastTextRepeatCount); 70 | if (Text.Last() == lastText) 71 | textCollection.Add(repeatText); 72 | else 73 | textCollection[Text.Count - 1] = repeatText; 74 | } 75 | else 76 | { 77 | textCollection.Add(formattedText); 78 | lastText = formattedText; 79 | lastTextRepeatCount = 1; 80 | } 81 | } 82 | 83 | private static string Format(string text, bool isShortcut) 84 | { 85 | if (text == "Left") 86 | return GetString(8592); 87 | if (text == "Up") 88 | return GetString(8593); 89 | if (text == "Right") 90 | return GetString(8594); 91 | if (text == "Down") 92 | return GetString(8595); 93 | 94 | // If the space is part of a shortcut sequence 95 | // present it as a primitive key. E.g. Ctrl+Spc. 96 | // Otherwise we want to preserve a space as part of 97 | // what is probably a sentence. 98 | if (text == " " && isShortcut) 99 | return "Space"; 100 | 101 | return text; 102 | } 103 | 104 | private static string GetString(int decimalValue) 105 | { 106 | return new string(new[] { (char)decimalValue }); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Carnac.Tests/MessageProviderFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Subjects; 4 | using System.Windows.Forms; 5 | using Carnac.Logic; 6 | using Carnac.Logic.KeyMonitor; 7 | using Carnac.Logic.Models; 8 | using NSubstitute; 9 | using SettingsProviderNet; 10 | using Xunit; 11 | using Message = Carnac.Logic.Models.Message; 12 | 13 | namespace Carnac.Tests 14 | { 15 | public class MessageProviderFacts : IObserver 16 | { 17 | readonly Subject interceptKeysSource; 18 | readonly IShortcutProvider shortcutProvider; 19 | readonly MessageProvider messageProvider; 20 | readonly List messages = new List(); 21 | readonly ISettingsProvider settingsProvider; 22 | 23 | public MessageProviderFacts() 24 | { 25 | settingsProvider = Substitute.For(); 26 | settingsProvider.GetSettings().Returns(new PopupSettings()); 27 | shortcutProvider = Substitute.For(); 28 | interceptKeysSource = new Subject(); 29 | var keyProvider = new KeyProvider(interceptKeysSource, new PasswordModeService()); 30 | messageProvider = new MessageProvider(keyProvider, shortcutProvider, settingsProvider); 31 | } 32 | 33 | [Fact] 34 | public void key_with_modifiers_raises_a_new_message() 35 | { 36 | // arrange 37 | messageProvider.Subscribe(this); 38 | KeyStreams.LetterL().Play(interceptKeysSource); 39 | 40 | // act 41 | KeyStreams.CtrlShiftL().Play(interceptKeysSource); 42 | 43 | // assert 44 | Assert.Equal(2, messages.Count); 45 | } 46 | 47 | [Fact] 48 | public void recognises_shortcuts() 49 | { 50 | // arrange 51 | messageProvider.Subscribe(this); 52 | shortcutProvider.GetShortcutsMatching(Arg.Any>()) 53 | .Returns(new []{new KeyShortcut("MyShortcut", new KeyPressDefinition(Keys.L, shiftPressed:true, controlPressed:true))}); 54 | 55 | // act 56 | KeyStreams.CtrlShiftL().Play(interceptKeysSource); 57 | 58 | // assert 59 | Assert.Equal(1, messages.Count); 60 | Assert.Equal("MyShortcut", messages[0].ShortcutName); 61 | } 62 | 63 | [Fact] 64 | public void does_not_show_shortcut_name_on_partial_match() 65 | { 66 | // arrange 67 | messageProvider.Subscribe(this); 68 | shortcutProvider.GetShortcutsMatching(Arg.Any>()) 69 | .Returns(new[] { new KeyShortcut("SomeShortcut", 70 | new KeyPressDefinition(Keys.U, controlPressed: true), 71 | new KeyPressDefinition(Keys.L)) }); 72 | 73 | // act 74 | KeyStreams.CtrlU().Play(interceptKeysSource); 75 | 76 | // assert 77 | Assert.Equal(1, messages.Count); 78 | Assert.NotEqual("SomeShortcut", messages[0].ShortcutName); 79 | } 80 | 81 | [Fact] 82 | public void does_show_shortcut_name_on_full_match() 83 | { 84 | // arrange 85 | messageProvider.Subscribe(this); 86 | shortcutProvider.GetShortcutsMatching(Arg.Any>()) 87 | .Returns(new[] { new KeyShortcut("SomeShortcut", 88 | new KeyPressDefinition(Keys.U, controlPressed: true), 89 | new KeyPressDefinition(Keys.L)) }); 90 | 91 | // act 92 | KeyStreams.CtrlU().Play(interceptKeysSource); 93 | KeyStreams.LetterL().Play(interceptKeysSource); 94 | 95 | // assert 96 | Assert.Equal(1, messages.Count); 97 | Assert.Equal("SomeShortcut", messages[0].ShortcutName); 98 | } 99 | 100 | public void OnNext(Message value) { messages.Add(value); } 101 | public void OnError(Exception error) { } 102 | public void OnCompleted() { } 103 | } 104 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/ShortcutProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.Composition; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using Carnac.Logic.Models; 7 | using YamlDotNet.RepresentationModel; 8 | 9 | namespace Carnac.Logic 10 | { 11 | [Export(typeof(IShortcutProvider))] 12 | public class ShortcutProvider : IShortcutProvider 13 | { 14 | readonly List shortcuts = new List(); 15 | 16 | public ShortcutProvider() 17 | { 18 | string folder = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + @"\Keymaps\"; 19 | string filter = "*.yml"; 20 | if (!Directory.Exists(folder)) return; 21 | string[] files = Directory.GetFiles(folder, filter); 22 | 23 | var yaml = new YamlStream(); 24 | 25 | foreach (string file in files) 26 | { 27 | yaml.Load(File.OpenText(file)); 28 | var root = yaml.Documents[0].RootNode; 29 | 30 | var collection = root as YamlMappingNode; 31 | if (collection != null) 32 | { 33 | string group = GetValueByKey(collection, "group"); 34 | string process = GetValueByKey(collection, "process"); 35 | var shortcutCollection = new ShortcutCollection { Process = process, Group = group }; 36 | 37 | 38 | var groupShortcuts = collection.Children.First(n => n.Key.ToString() == "shortcuts").Value as YamlSequenceNode; 39 | 40 | foreach (YamlMappingNode entry in groupShortcuts.Children) 41 | { 42 | string name = GetValueByKey(entry, "name"); 43 | 44 | if (entry.Children.First(n => n.Key.ToString() == "keys").Value as YamlSequenceNode == null) 45 | continue; 46 | 47 | var keys = entry.Children.First(n => n.Key.ToString() == "keys").Value as YamlSequenceNode; 48 | 49 | foreach (var keyCombo in keys.Children) 50 | { 51 | var definitions = new List(); 52 | string[] combos = keyCombo.ToString().Split(','); 53 | foreach (string combo in combos) 54 | { 55 | var definition = GetKeyPressDefintion(combo); 56 | if (definition != null) 57 | definitions.Add(definition); 58 | } 59 | if (definitions.Count > 0) 60 | shortcutCollection.Add(new KeyShortcut(name, definitions.ToArray())); 61 | } 62 | } 63 | 64 | shortcuts.Add(shortcutCollection); 65 | } 66 | } 67 | } 68 | 69 | private string GetValueByKey(YamlMappingNode node, string name) 70 | { 71 | return node.Children.First(n => n.Key.ToString() == name).Value.ToString(); 72 | } 73 | 74 | private KeyPressDefinition GetKeyPressDefintion(string combo) 75 | { 76 | combo = combo.ToLower(); 77 | var key = combo.Split('+').Last(); 78 | var keys = ReplaceKey.ToKey(key); 79 | if (keys != null) 80 | return 81 | new KeyPressDefinition 82 | (keys.Value, 83 | shiftPressed: combo.Contains("shift"), 84 | controlPressed: combo.Contains("ctrl"), 85 | altPressed: combo.Contains("alt"), 86 | winkeyPressed: combo.Contains("win")); 87 | return null; 88 | } 89 | 90 | public IEnumerable GetShortcutsMatching(IEnumerable keys) 91 | { 92 | var keyPresses = keys.ToArray(); 93 | var processName = keyPresses.Last().Process.ProcessName; 94 | foreach (var shortcut in shortcuts.Where(s => (s.Process == processName) || string.IsNullOrWhiteSpace(s.Process))) 95 | { 96 | var match = shortcut.GetShortcutsMatching(keyPresses); 97 | if (match.Count() > 0) 98 | return match; 99 | } 100 | 101 | return Enumerable.Empty(); 102 | } 103 | 104 | } 105 | } -------------------------------------------------------------------------------- /src/Carnac.Tests/ViewModels/ShellViewModelFacts.cs: -------------------------------------------------------------------------------- 1 | using Caliburn.Micro; 2 | using Carnac.Logic; 3 | using Carnac.Logic.Models; 4 | using Carnac.Utilities; 5 | using Carnac.ViewModels; 6 | using NSubstitute; 7 | using SettingsProviderNet; 8 | using Xunit; 9 | using Action = System.Action; 10 | 11 | namespace Carnac.Tests.ViewModels 12 | { 13 | public class ShellViewModelFacts 14 | { 15 | public class when_creating_the_new_viewmodel : SpecificationFor 16 | { 17 | private readonly ISettingsProvider settingsService = Substitute.For(); 18 | private readonly IScreenManager screenManager = Substitute.For(); 19 | private readonly ITimerFactory timerFactory = Substitute.For(); 20 | private readonly IWindowManager windowManager = Substitute.For(); 21 | private readonly IMessageProvider messageProvider = Substitute.For(); 22 | 23 | public override ShellViewModel Given() 24 | { 25 | return new ShellViewModel(settingsService, screenManager, timerFactory, windowManager, messageProvider); 26 | } 27 | 28 | public override void When() 29 | { 30 | // do nothing 31 | } 32 | 33 | [Fact] 34 | public void TimerFactory_Always_StartsATimer() 35 | { 36 | timerFactory.Received().Start(1000, Arg.Any()); 37 | } 38 | 39 | [Fact] 40 | public void ScreenManager_Always_Fetches_CurrentScreens() 41 | { 42 | screenManager.Received().GetScreens(); 43 | } 44 | 45 | [Fact] 46 | public void SettingsService_Always_Fetches_ExistingSettings() 47 | { 48 | settingsService.Received().GetSettings(); 49 | } 50 | 51 | [Fact] 52 | public void Constructor_Always_ShowsKeyShowViewModel() 53 | { 54 | windowManager.ReceivedWithAnyArgs().ShowWindow(null); 55 | } 56 | } 57 | 58 | public class when_the_settings_file_is_defined : SpecificationFor 59 | { 60 | private readonly ISettingsProvider settingsService = Substitute.For(); 61 | private readonly IScreenManager screenManager = Substitute.For(); 62 | private readonly ITimerFactory timerFactory = Substitute.For(); 63 | private readonly IWindowManager windowManager = Substitute.For(); 64 | private readonly IMessageProvider messageProvider = Substitute.For(); 65 | private readonly PopupSettings popupSettings = new PopupSettings(); 66 | 67 | public override ShellViewModel Given() 68 | { 69 | settingsService.GetSettings().Returns(popupSettings); 70 | return new ShellViewModel(settingsService, screenManager, timerFactory, windowManager, messageProvider); 71 | } 72 | 73 | public override void When() 74 | { 75 | // do nothing 76 | } 77 | 78 | [Fact] 79 | public void the_settings_file_is_the_existing_instance() 80 | { 81 | Assert.Equal(popupSettings, Subject.Settings); 82 | } 83 | } 84 | 85 | public class when_the_settings_file_is_not_defined : SpecificationFor 86 | { 87 | private readonly ISettingsProvider settingsService = Substitute.For(); 88 | private readonly IScreenManager screenManager = Substitute.For(); 89 | private readonly ITimerFactory timerFactory = Substitute.For(); 90 | private readonly IWindowManager windowManager = Substitute.For(); 91 | private readonly IMessageProvider messageProvider = Substitute.For(); 92 | private readonly PopupSettings popupSettings = Substitute.For(); 93 | 94 | public override ShellViewModel Given() 95 | { 96 | settingsService.GetSettings().Returns(popupSettings); 97 | return new ShellViewModel(settingsService, screenManager, timerFactory, windowManager, messageProvider); 98 | } 99 | 100 | public override void When() 101 | { 102 | // do nothing 103 | } 104 | 105 | [Fact] 106 | public void the_settings_file_is_the_existing_instance() 107 | { 108 | Assert.NotNull(Subject.Settings); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Carnac.Logic/MessageProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.Linq; 5 | using System.Reactive.Subjects; 6 | using Carnac.Logic.Models; 7 | using SettingsProviderNet; 8 | using Message = Carnac.Logic.Models.Message; 9 | 10 | namespace Carnac.Logic 11 | { 12 | [Export(typeof(IMessageProvider))] 13 | public class MessageProvider : IMessageProvider, IObserver 14 | { 15 | readonly Subject subject = new Subject(); 16 | private readonly IShortcutProvider shortcutProvider; 17 | private IDisposable keyStream; 18 | private readonly PopupSettings settings; 19 | 20 | [ImportingConstructor] 21 | public MessageProvider(IKeyProvider keyProvider, IShortcutProvider shortcutProvider, ISettingsProvider settingsProvider) 22 | { 23 | this.shortcutProvider = shortcutProvider; 24 | settings = settingsProvider.GetSettings(); 25 | keyStream = keyProvider.Subscribe(this); 26 | } 27 | 28 | public Message CurrentMessage { get; private set; } 29 | 30 | public IDisposable Subscribe(IObserver observer) 31 | { 32 | return subject.Subscribe(observer); 33 | } 34 | 35 | public void OnNext(KeyPress value) 36 | { 37 | Message message; 38 | 39 | var currentKeyPress = new[] {value}; 40 | var keyPresses = CurrentMessage == null ? currentKeyPress : CurrentMessage.Keys.Concat(currentKeyPress).ToArray(); 41 | var possibleShortcuts = GetPossibleShortcuts(keyPresses).ToList(); 42 | if (possibleShortcuts.Any()) 43 | { 44 | var shortcut = possibleShortcuts.FirstOrDefault(s => s.IsMatch(keyPresses)); 45 | if (shortcut != null) 46 | { 47 | message = CurrentMessage ?? CreateNewMessage(value); 48 | message.AddKey(value); 49 | message.ShortcutName = shortcut.Name; 50 | message.LastMessage = DateTime.Now; 51 | message.Count++; 52 | //Have duplicated as it was easier for now, this should be cleaned up 53 | return; 54 | } 55 | } 56 | 57 | // Haven't matched a Chord, try just the last keypress 58 | var keyShortcuts = GetPossibleShortcuts(currentKeyPress).ToList(); 59 | if (keyShortcuts.Any()) 60 | { 61 | var shortcut = keyShortcuts.FirstOrDefault(s => s.IsMatch(currentKeyPress)); 62 | if (shortcut != null) 63 | { 64 | //For matching last keypress, we want a new message 65 | message = CreateNewMessage(value); 66 | message.AddKey(value); 67 | message.ShortcutName = shortcut.Name; 68 | message.LastMessage = DateTime.Now; 69 | message.Count++; 70 | //Have duplicated as it was easier for now, this should be cleaned up 71 | return; 72 | } 73 | } 74 | 75 | if (!value.IsShortcut && settings.DetectShortcutsOnly) 76 | return; 77 | 78 | if (ShouldCreateNewMessage(value)) 79 | { 80 | message = CreateNewMessage(value); 81 | } 82 | else 83 | message = CurrentMessage ?? CreateNewMessage(value); 84 | 85 | message.AddKey(value); 86 | message.LastMessage = DateTime.Now; 87 | message.Count++; 88 | } 89 | 90 | private Message CreateNewMessage(KeyPress value) 91 | { 92 | var message = new Message 93 | { 94 | StartingTime = DateTime.Now, 95 | ProcessName = value.Process.ProcessName 96 | }; 97 | 98 | CurrentMessage = message; 99 | subject.OnNext(message); 100 | return message; 101 | } 102 | 103 | private bool ShouldCreateNewMessage(KeyPress value) 104 | { 105 | return 106 | CurrentMessage == null || 107 | IsDifferentProcess(value) || 108 | IsOlderThanOneSecond() || 109 | LastKeyPressWasShortcut() || 110 | value.IsShortcut; 111 | } 112 | 113 | private bool LastKeyPressWasShortcut() 114 | { 115 | return CurrentMessage.Keys.Last().IsShortcut; 116 | } 117 | 118 | private IEnumerable GetPossibleShortcuts(IEnumerable keyPresses) 119 | { 120 | return shortcutProvider.GetShortcutsMatching(keyPresses); 121 | } 122 | 123 | private bool IsOlderThanOneSecond() 124 | { 125 | return CurrentMessage.LastMessage < DateTime.Now.AddSeconds(-1); 126 | } 127 | 128 | private bool IsDifferentProcess(KeyPress value) 129 | { 130 | return CurrentMessage.ProcessName != value.Process.ProcessName; 131 | } 132 | 133 | public void OnError(Exception error) 134 | { 135 | 136 | } 137 | 138 | public void OnCompleted() 139 | { 140 | 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /src/KeyStreamCapture/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyMonitor/InterceptKeys.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reactive.Subjects; 4 | using System.Runtime.InteropServices; 5 | using System.Security.Permissions; 6 | using System.Windows.Forms; 7 | 8 | namespace Carnac.Logic.KeyMonitor 9 | { 10 | [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] 11 | [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")] 12 | public class InterceptKeys : IObservable, IDisposable 13 | { 14 | static volatile InterceptKeys current; 15 | static readonly object CurrentLock = new object(); 16 | readonly Win32Methods.LowLevelKeyboardProc callback; 17 | readonly Subject subject; 18 | bool disposed; 19 | IntPtr hookId = IntPtr.Zero; 20 | decimal subscriberCount; 21 | 22 | InterceptKeys() 23 | { 24 | subject = new Subject(); 25 | callback = HookCallback; 26 | } 27 | 28 | public static InterceptKeys Current 29 | { 30 | get 31 | { 32 | if (current == null) 33 | { 34 | lock (CurrentLock) 35 | { 36 | if (current == null) 37 | { 38 | current = new InterceptKeys(); 39 | } 40 | } 41 | } 42 | return current; 43 | } 44 | } 45 | 46 | public void Dispose() 47 | { 48 | Dispose(true); 49 | GC.SuppressFinalize(this); 50 | } 51 | 52 | public IDisposable Subscribe(IObserver observer) 53 | { 54 | Debug.WriteLine("Subscribed"); 55 | IDisposable dispose = subject.Subscribe(observer); 56 | subscriberCount++; 57 | if (subscriberCount == 1) 58 | hookId = SetHook(callback); 59 | return new DelegateDisposable(() => 60 | { 61 | subscriberCount--; 62 | if (subscriberCount == 0) 63 | Win32Methods.UnhookWindowsHookEx(hookId); 64 | dispose.Dispose(); 65 | }); 66 | } 67 | 68 | IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 69 | { 70 | if (nCode >= 0) 71 | { 72 | bool alt = (Control.ModifierKeys & Keys.Alt) != 0; 73 | bool control = (Control.ModifierKeys & Keys.Control) != 0; 74 | bool shift = (Control.ModifierKeys & Keys.Shift) != 0; 75 | bool keyDown = wParam == (IntPtr)Win32Methods.WM_KEYDOWN; 76 | bool keyUp = wParam == (IntPtr)Win32Methods.WM_KEYUP; 77 | int vkCode = Marshal.ReadInt32(lParam); 78 | var key = (Keys)vkCode; 79 | //http://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx 80 | if (key != Keys.RMenu && key != Keys.LMenu && wParam == (IntPtr)Win32Methods.WM_SYSKEYDOWN) 81 | { 82 | alt = true; 83 | keyDown = true; 84 | } 85 | if (key != Keys.RMenu && key != Keys.LMenu && wParam == (IntPtr)Win32Methods.WM_SYSKEYUP) 86 | { 87 | alt = true; 88 | keyUp = true; 89 | } 90 | 91 | var interceptKeyEventArgs = new InterceptKeyEventArgs( 92 | key, 93 | keyDown ? 94 | KeyDirection.Down: keyUp 95 | ? KeyDirection.Up: KeyDirection.Unknown, 96 | alt, control, shift); 97 | 98 | subject.OnNext(interceptKeyEventArgs); 99 | Debug.Write(key); 100 | if (interceptKeyEventArgs.Handled) 101 | { 102 | Debug.WriteLine(" handled"); 103 | return (IntPtr)1; //handled 104 | } 105 | } 106 | 107 | return Win32Methods.CallNextHookEx(hookId, nCode, wParam, lParam); 108 | } 109 | 110 | static IntPtr SetHook(Win32Methods.LowLevelKeyboardProc proc) 111 | { 112 | //TODO: This requires FullTrust to use the Process class - is there any options for doing this in MediumTrust? 113 | // 114 | using (Process curProcess = Process.GetCurrentProcess()) 115 | using (ProcessModule curModule = curProcess.MainModule) 116 | { 117 | return Win32Methods.SetWindowsHookEx(Win32Methods.WH_KEYBOARD_LL, proc, 118 | Win32Methods.GetModuleHandle(curModule.ModuleName), 0); 119 | } 120 | } 121 | 122 | protected virtual void Dispose(bool disposing) 123 | { 124 | if (!disposed) 125 | { 126 | if (disposing) 127 | { 128 | if (subject != null) 129 | { 130 | subject.Dispose(); 131 | } 132 | } 133 | 134 | disposed = true; 135 | } 136 | } 137 | 138 | class DelegateDisposable : IDisposable 139 | { 140 | readonly Action dispose; 141 | 142 | public DelegateDisposable(Action dispose) 143 | { 144 | this.dispose = dispose; 145 | } 146 | 147 | public void Dispose() 148 | { 149 | dispose(); 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /src/Carnac.Logic/KeyProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reactive.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Windows.Forms; 7 | using Carnac.Logic.KeyMonitor; 8 | using Carnac.Logic.Models; 9 | 10 | namespace Carnac.Logic 11 | { 12 | public class KeyProvider : IKeyProvider 13 | { 14 | private readonly IObservable interceptKeysSource; 15 | private readonly Dictionary processes; 16 | private readonly IPasswordModeService passwordModeService; 17 | private readonly IList modifierKeys = 18 | new List 19 | { 20 | Keys.LControlKey, 21 | Keys.RControlKey, 22 | Keys.LShiftKey, 23 | Keys.RShiftKey, 24 | Keys.LMenu, 25 | Keys.RMenu, 26 | Keys.ShiftKey, 27 | Keys.Shift, 28 | Keys.Alt, 29 | Keys.LWin, 30 | Keys.RWin 31 | }; 32 | 33 | private bool winKeyPressed; 34 | 35 | [DllImport("User32.dll")] 36 | private static extern int GetForegroundWindow(); 37 | 38 | [DllImport("user32.dll")] 39 | private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 40 | 41 | public KeyProvider(IObservable interceptKeysSource, IPasswordModeService passwordModeService) 42 | { 43 | processes = new Dictionary(); 44 | this.interceptKeysSource = interceptKeysSource; 45 | this.passwordModeService = passwordModeService; 46 | } 47 | 48 | public IDisposable Subscribe(IObserver observer) 49 | { 50 | return interceptKeysSource 51 | .Select(DetectWindowsKey) 52 | .Where(k => !IsModifierKeyPress(k) && k.KeyDirection == KeyDirection.Down) 53 | .Select(ToCarnacKeyPress) 54 | .Where(k => !passwordModeService.CheckPasswordMode(k.InterceptKeyEventArgs)) 55 | .Subscribe(observer); 56 | } 57 | 58 | private InterceptKeyEventArgs DetectWindowsKey(InterceptKeyEventArgs interceptKeyEventArgs) 59 | { 60 | if (interceptKeyEventArgs.Key == Keys.LWin || interceptKeyEventArgs.Key == Keys.RWin) 61 | { 62 | if (interceptKeyEventArgs.KeyDirection == KeyDirection.Up) 63 | winKeyPressed = false; 64 | else if (interceptKeyEventArgs.KeyDirection == KeyDirection.Down) 65 | winKeyPressed = true; 66 | } 67 | 68 | return interceptKeyEventArgs; 69 | } 70 | 71 | private bool IsModifierKeyPress(InterceptKeyEventArgs interceptKeyEventArgs) 72 | { 73 | return modifierKeys.Contains(interceptKeyEventArgs.Key); 74 | } 75 | 76 | private KeyPress ToCarnacKeyPress(InterceptKeyEventArgs interceptKeyEventArgs) 77 | { 78 | var process = GetAssociatedProcess(); 79 | 80 | var isLetter = interceptKeyEventArgs.Key >= Keys.A && 81 | interceptKeyEventArgs.Key <= Keys.Z; 82 | 83 | var inputs = ToInputs(isLetter, winKeyPressed, interceptKeyEventArgs); 84 | 85 | return new KeyPress(process, interceptKeyEventArgs, winKeyPressed, inputs); 86 | } 87 | 88 | private static IEnumerable ToInputs(bool isLetter, bool isWinKeyPressed, InterceptKeyEventArgs interceptKeyEventArgs) 89 | { 90 | var controlPressed = interceptKeyEventArgs.ControlPressed; 91 | var altPressed = interceptKeyEventArgs.AltPressed; 92 | var shiftPressed = interceptKeyEventArgs.ShiftPressed; 93 | if (controlPressed) 94 | yield return "Ctrl"; 95 | if (altPressed) 96 | yield return "Alt"; 97 | if (isWinKeyPressed) 98 | yield return "Win"; 99 | 100 | if (controlPressed || altPressed) 101 | { 102 | //Treat as a shortcut, don't be too smart 103 | if (shiftPressed) 104 | yield return "Shift"; 105 | 106 | yield return interceptKeyEventArgs.Key.Sanitise(); 107 | } 108 | else 109 | { 110 | string input; 111 | var shiftModifiesInput = interceptKeyEventArgs.Key.SanitiseShift(out input); 112 | 113 | if (!isLetter && !shiftModifiesInput && shiftPressed) 114 | yield return "Shift"; 115 | 116 | if (interceptKeyEventArgs.ShiftPressed && shiftModifiesInput) 117 | yield return input; 118 | else if (isLetter && !interceptKeyEventArgs.ShiftPressed) 119 | yield return interceptKeyEventArgs.Key.ToString().ToLower(); 120 | else 121 | yield return interceptKeyEventArgs.Key.Sanitise(); 122 | } 123 | } 124 | 125 | private Process GetAssociatedProcess() 126 | { 127 | Process process; 128 | 129 | var handle = GetForegroundWindow(); 130 | 131 | if (!processes.ContainsKey(handle)) 132 | { 133 | uint processID; 134 | GetWindowThreadProcessId(new IntPtr(handle), out processID); 135 | var p = Process.GetProcessById(Convert.ToInt32(processID)); 136 | processes.Add(handle, p); 137 | process = p; 138 | } 139 | else 140 | process = processes[handle]; 141 | return process; 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /src/Carnac/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | Exit 119 | 120 | -------------------------------------------------------------------------------- /src/Carnac.Tests/Carnac.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {820B7770-FCD1-4987-A88C-E5DAE74F4AD6} 9 | Library 10 | Properties 11 | Carnac.Tests 12 | Carnac.Tests 13 | v4.0 14 | 512 15 | ..\..\src\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | False 38 | ..\packages\Caliburn.Micro.1.5.2\lib\net40\Caliburn.Micro.dll 39 | 40 | 41 | False 42 | ..\packages\Rx-Testing.2.2.2\lib\net40\Microsoft.Reactive.Testing.dll 43 | 44 | 45 | 46 | False 47 | ..\packages\NSubstitute.1.7.0.0\lib\NET40\NSubstitute.dll 48 | 49 | 50 | False 51 | ..\packages\SettingsProviderNet.2.0.2\lib\net40\SettingsProviderNet.dll 52 | 53 | 54 | 55 | 56 | False 57 | ..\packages\Rx-Core.2.2.2\lib\net40\System.Reactive.Core.dll 58 | 59 | 60 | False 61 | ..\packages\Rx-Interfaces.2.2.2\lib\net40\System.Reactive.Interfaces.dll 62 | 63 | 64 | False 65 | ..\packages\Rx-Linq.2.2.2\lib\net40\System.Reactive.Linq.dll 66 | 67 | 68 | False 69 | ..\packages\Rx-PlatformServices.2.2.2\lib\net40\System.Reactive.PlatformServices.dll 70 | 71 | 72 | 73 | ..\packages\Caliburn.Micro.1.5.2\lib\net40\System.Windows.Interactivity.dll 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | False 82 | ..\packages\xunit.1.9.2\lib\net20\xunit.dll 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | {614470A0-432C-46EB-B0EB-DE0A9062FC22} 101 | Carnac.Logic 102 | 103 | 104 | {3D54543F-9FF2-4298-A621-0C66A36412CD} 105 | Carnac 106 | 107 | 108 | 109 | 110 | 117 | -------------------------------------------------------------------------------- /tools/xunit/HTML.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ]]> 6 | 7 | 8 | 9 | xUnit.net Test Results - 10 | 11 | 30 | 41 | 42 | 43 | Assemblies Run 44 | 45 | 46 | Summary 47 | 48 | Tests run: 49 | Failures: , 50 | Skipped: , 51 | Run time: s 52 | 53 | 54 | 55 | Failed tests 56 | 57 | 58 | 59 | 60 | Failed fixtures 61 | 62 | 63 | 64 | 65 | Skipped tests 66 | 67 | 68 | 69 | All tests 70 | Click test class name to expand/collapse test details 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | altrow 83 | s 84 | Skipped❢ 85 | ✘ 86 | ✔ 87 | 88 | : 89 | 90 | 91 | 92 | 93 | 94 | Output 95 | 96 | 97 | 98 | 99 | 100 | 101 | ✘ : 102 | Stack Trace: 103 | 104 | 105 | 106 | 107 | 108 | s 109 | 110 | ToggleClass('class') 111 | ToggleClass('class') 112 | ✘ 113 | ✔ 114 | 115 | ( tests) 116 | 117 | 118 | 119 | 120 | display: none; 121 | class 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/Carnac.Logic/Keymaps/resharper.yml: -------------------------------------------------------------------------------- 1 | group: Resharper 2 | process: devenv 3 | 4 | shortcuts: 5 | - name: Reshaper context actions 6 | keys: 7 | - Alt+Enter 8 | 9 | - name: Code cleanup 10 | keys: 11 | - Ctrl+E,C 12 | 13 | - name: Silent code cleanup 14 | keys: 15 | - Ctrl+E,F 16 | 17 | - name: Symbol code completion 18 | keys: 19 | - Ctrl+Space 20 | 21 | - name: Smart code completion 22 | keys: 23 | - Ctrl+Alt+Space 24 | 25 | - name: Import symbol completion 26 | keys: 27 | - Shift+Alt+Space 28 | 29 | - name: Complete statement 30 | keys: 31 | - Ctrl+Shift+Enter 32 | 33 | - name: Parameter information 34 | keys: 35 | - Ctrl+Shift+Space 36 | 37 | - name: Quick documentation 38 | keys: 39 | - Ctrl+Shift+F1 40 | 41 | - name: Insert live template 42 | keys: 43 | - Ctrl+E,L 44 | 45 | - name: Surround with template 46 | keys: 47 | - Ctrl+E,U 48 | 49 | - name: Generate code 50 | keys: 51 | - Alt+Insert 52 | 53 | - name: Create file from template 54 | keys: 55 | - Ctrl+Alt+Insert 56 | 57 | - name: Move code up 58 | keys: 59 | - Ctrl+Shift+Alt+Up 60 | 61 | - name: Move code down 62 | keys: 63 | - Ctrl+Shift+Alt+Down 64 | 65 | - name: Move code left 66 | keys: 67 | - Ctrl+Shift+Alt+Left 68 | 69 | - name: Move code right 70 | keys: 71 | - Ctrl+Shift+Alt+Right 72 | 73 | - name: Extend selection 74 | keys: 75 | - Ctrl+Alt+Right 76 | 77 | - name: Shrink selection 78 | keys: 79 | - Ctrl+Alt+Left 80 | 81 | - name: Duplicate a line or selection 82 | keys: 83 | - Ctrl+D 84 | 85 | - name: Comment with line comment 86 | keys: 87 | - Ctrl+Alt+/ 88 | 89 | - name: Comment with block comment 90 | keys: 91 | - Ctrl+Shift+/ 92 | 93 | - name: Inspect this 94 | keys: 95 | - Ctrl+Shift+Alt+A 96 | 97 | - name: Inspection Results window 98 | keys: 99 | - Ctrl+Alt+V 100 | 101 | - name: Turn code analysis on/off 102 | keys: 103 | - Ctrl+Shift+Alt+8 104 | 105 | - name: Find Results window 106 | keys: 107 | - Ctrl+Alt+F12 108 | 109 | - name: Hierarchies window 110 | keys: 111 | - Ctrl+Alt+H 112 | 113 | - name: View type hierarchy 114 | keys: 115 | - Ctrl+E,H 116 | 117 | - name: File structure 118 | keys: 119 | - Ctrl+Alt+F 120 | 121 | - name: To-do items 122 | keys: 123 | - Ctrl+Alt+D 124 | 125 | - name: Browse stack trace 126 | keys: 127 | - Ctrl+E,T 128 | 129 | - name: Locate in Solution Explorer 130 | keys: 131 | - Shift+Alt+L 132 | 133 | - name: View recent files 134 | keys: 135 | - Ctrl+, 136 | 137 | - name: View recent edits 138 | keys: 139 | - Ctrl+Shift+, 140 | 141 | - name: Go to previous edit 142 | keys: 143 | - Ctrl+Shift+Backspace 144 | 145 | - name: Go to related files 146 | keys: 147 | - Ctrl+Alt+F7 148 | 149 | - name: View bookmarks 150 | keys: 151 | - Ctrl+` 152 | 153 | - name: Go to bookmark 154 | keys: 155 | - Ctrl+[numeric+key] 156 | 157 | - name: Set/remove bookmark 158 | keys: 159 | - Ctrl+Shift+[numeric+key] 160 | 161 | - name: Go to type 162 | keys: 163 | - Ctrl+T 164 | 165 | - name: Go to file 166 | keys: 167 | - Ctrl+Shift+T 168 | 169 | - name: Go to file member 170 | keys: 171 | - Alt+\ 172 | 173 | - name: Go to symbol 174 | keys: 175 | - Shift+Alt+T 176 | 177 | - name: Navigate to 178 | keys: 179 | - Alt+` 180 | 181 | - name: Go to type of symbol 182 | keys: 183 | - Ctrl+Shift+F11 184 | 185 | - name: Go to declaration 186 | keys: 187 | - F12 188 | 189 | - name: Go to implementation 190 | keys: 191 | - Ctrl+F12 192 | 193 | - name: Go to base symbols 194 | keys: 195 | - Alt+Home 196 | 197 | - name: Go to derived symbols 198 | keys: 199 | - Alt+End 200 | 201 | - name: Go to usage 202 | keys: 203 | - Shift+Alt+F12 204 | 205 | - name: Go to next member/tag 206 | keys: 207 | - Alt+Down 208 | 209 | - name: Go to previous member/tag 210 | keys: 211 | - Alt+Up 212 | 213 | - name: Go to next highlight (error, warning or suggestion) 214 | keys: 215 | - Alt+PageDown 216 | 217 | - name: Go to previous highlight (error, warning or suggestion) 218 | keys: 219 | - Alt+PageUp 220 | 221 | - name: Go to next error 222 | keys: 223 | - Shift+Alt+PageDown 224 | 225 | - name: Go to next error in solution 226 | keys: 227 | - Shift+Alt+PageDown 228 | 229 | - name: Go to previous error 230 | keys: 231 | - Shift+Alt+PageUp 232 | 233 | - name: Go to previous error in solution 234 | keys: 235 | - Shift+Alt+PageUp 236 | 237 | - name: Go to containing declaration 238 | keys: 239 | - Ctrl+[ 240 | 241 | - name: Find usages 242 | keys: 243 | - Shift+F12 244 | 245 | - name: Find usages (advanced) 246 | keys: 247 | - Ctrl+Shift+Alt+F12 248 | 249 | - name: Highlight usages in file 250 | keys: 251 | - Shift+Alt+F11 252 | 253 | - name: Go to previous usage 254 | keys: 255 | - Ctrl+Alt+PageUp 256 | 257 | - name: Go to next usage 258 | keys: 259 | - Ctrl+Alt+PageDown 260 | 261 | - name: Refactor this 262 | keys: 263 | - Ctrl+Shift+R 264 | 265 | - name: Rename 266 | keys: 267 | - Ctrl+R,R 268 | 269 | - name: Move 270 | keys: 271 | - Ctrl+R,O 272 | 273 | - name: Safe delete 274 | keys: 275 | - Ctrl+R,D 276 | 277 | - name: Safe delete 278 | keys: 279 | - Alt+Del 280 | 281 | - name: Extract method 282 | keys: 283 | - Ctrl+R,M 284 | 285 | - name: Introduce variable 286 | keys: 287 | - Ctrl+R,V 288 | 289 | - name: Introduce field 290 | keys: 291 | - Ctrl+R,F 292 | 293 | - name: Introduce parameter 294 | keys: 295 | - Ctrl+R,P 296 | 297 | - name: Inline variable/method/field 298 | keys: 299 | - Ctrl+R,I 300 | 301 | - name: Encapsulate field 302 | keys: 303 | - Ctrl+R,E 304 | 305 | - name: Change signature 306 | keys: 307 | - Ctrl+R,S 308 | 309 | - name: Unit Test Explorer 310 | keys: 311 | - Ctrl+Alt+U 312 | 313 | - name: Unit Test Sessions 314 | keys: 315 | - Ctrl+Alt+T 316 | 317 | - name: Close recent tool 318 | keys: 319 | - Ctrl+Shift+F4 320 | 321 | - name: Activate recent tool 322 | keys: 323 | - Ctrl+Alt+Backspace -------------------------------------------------------------------------------- /src/Carnac.Logic/Carnac.Logic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {614470A0-432C-46EB-B0EB-DE0A9062FC22} 9 | Library 10 | Properties 11 | Carnac.Logic 12 | Carnac.Logic 13 | v4.0 14 | 512 15 | ..\ 16 | true 17 | 18 | 19 | ..\packages\Fody.1.19.1.0 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | ..\packages\PropertyChanged.Fody.1.43.1.0\Lib\NET35\PropertyChanged.dll 42 | False 43 | 44 | 45 | False 46 | ..\packages\SettingsProviderNet.2.0.2\lib\net40\SettingsProviderNet.dll 47 | 48 | 49 | 50 | 51 | 52 | False 53 | ..\packages\Rx-Core.2.2.2\lib\net40\System.Reactive.Core.dll 54 | 55 | 56 | False 57 | ..\packages\Rx-Interfaces.2.2.2\lib\net40\System.Reactive.Interfaces.dll 58 | 59 | 60 | False 61 | ..\packages\Rx-Linq.2.2.2\lib\net40\System.Reactive.Linq.dll 62 | 63 | 64 | False 65 | ..\packages\Rx-PlatformServices.2.2.2\lib\net40\System.Reactive.PlatformServices.dll 66 | 67 | 68 | 69 | 70 | 71 | False 72 | ..\packages\YamlDotNet.Core.2.2.0\lib\net35\YamlDotNet.Core.dll 73 | 74 | 75 | False 76 | ..\packages\YamlDotNet.RepresentationModel.2.2.0\lib\net35\YamlDotNet.RepresentationModel.dll 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Designer 115 | 116 | 117 | Always 118 | 119 | 120 | 121 | Always 122 | 123 | 124 | Always 125 | 126 | 127 | Always 128 | 129 | 130 | Designer 131 | 132 | 133 | 134 | 135 | 142 | 143 | --------------------------------------------------------------------------------