diff --git a/installer/Installer.iss b/installer/Installer.iss index aede1d0..84c6c6a 100644 --- a/installer/Installer.iss +++ b/installer/Installer.iss @@ -11,7 +11,7 @@ [Setup] ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) -AppId=68d780d4-b946-4154-8815-e4632e5ec5d8 +AppId=4B2411E5-1AE9-450C-AF2F-9A515ECBB9DF AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} diff --git a/installer/InvLock-Installer.exe b/installer/InvLock-Installer.exe index 5069323..839b0cb 100644 Binary files a/installer/InvLock-Installer.exe and b/installer/InvLock-Installer.exe differ diff --git a/src/InvLock/BlurWindow.xaml b/src/InvLock/BlurWindow.xaml new file mode 100644 index 0000000..749ff8d --- /dev/null +++ b/src/InvLock/BlurWindow.xaml @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/src/InvLock/BlurWindow.xaml.cs b/src/InvLock/BlurWindow.xaml.cs new file mode 100644 index 0000000..dbde331 --- /dev/null +++ b/src/InvLock/BlurWindow.xaml.cs @@ -0,0 +1,26 @@ +using InvLock.Utilities; + +using System.Windows; + +namespace InvLock; + +/// +/// Interaction logic for BlurWindow.xaml +/// +public partial class BlurWindow : Window +{ + public BlurWindow() + { + InitializeComponent(); + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + WindowsApi.EnableBlur(this); + } + + private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + DragMove(); + } +} \ No newline at end of file diff --git a/src/InvLock/DataContexts/MainWindowDataContext.cs b/src/InvLock/DataContexts/MainWindowDataContext.cs index 2de8097..9910baa 100644 --- a/src/InvLock/DataContexts/MainWindowDataContext.cs +++ b/src/InvLock/DataContexts/MainWindowDataContext.cs @@ -1,13 +1,19 @@ using CommunityToolkit.Mvvm.Input; +using InvLock.Utilities; + using SharpHook.Native; +using System.ComponentModel; using System.Reflection; +using System.Runtime.CompilerServices; namespace InvLock.DataContexts; -internal partial class MainWindowDataContext(Func lockWindowAccessor) +internal partial class MainWindowDataContext(Func lockWindowAccessor) : INotifyPropertyChanged { + public event PropertyChangedEventHandler? PropertyChanged; + #if DEBUG public string Title => $"{App.AppName} - Dev {AppVersion}"; #else @@ -16,10 +22,28 @@ internal partial class MainWindowDataContext(Func lockWindowAccesso public string AppName => App.AppName; - public string AppVersion => Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Unknown"; + public string AppVersion => Assembly.GetEntryAssembly()?.GetCustomAttribute()?.InformationalVersion ?? "Unknown"; public Settings Settings { get; } = Settings.Load(); + public bool IsAutoStartEnabled + { + get => StartupManager.IsAutoStartEnabled(); + set + { + if (value) + { + _ = StartupManager.EnableAutoStart(); + } + else + { + _ = StartupManager.DisableAutoStart(); + } + + OnPropertyChanged(); + } + } + [RelayCommand] public async Task RecordLockShortcut() { @@ -55,4 +79,9 @@ internal partial class MainWindowDataContext(Func lockWindowAccesso Settings.UnlockShortcut = unlockShortcut; } } + + protected void OnPropertyChanged([CallerMemberName] string? name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } } \ No newline at end of file diff --git a/src/InvLock/InvLock.csproj b/src/InvLock/InvLock.csproj index 37ee1ae..9f68947 100644 --- a/src/InvLock/InvLock.csproj +++ b/src/InvLock/InvLock.csproj @@ -8,6 +8,10 @@ true true Assets\logo.ico + 1.1.0.0 + 1.1.0.0 + 1.1.0.0 + false @@ -17,9 +21,11 @@ + + diff --git a/src/InvLock/LockWindow.xaml.cs b/src/InvLock/LockWindow.xaml.cs index 67c4750..1677755 100644 --- a/src/InvLock/LockWindow.xaml.cs +++ b/src/InvLock/LockWindow.xaml.cs @@ -19,7 +19,9 @@ public partial class LockWindow : Window private readonly SimpleGlobalHook hook = new SimpleGlobalHook(); private readonly Dictionary pressedKeys = []; private readonly Settings settings; + private readonly List blurWindows = []; private List windows = []; + private Point lastMousePosition; private bool isOpen = true; private bool suppressInput = false; @@ -120,11 +122,32 @@ public partial class LockWindow : Window MinimizeWindows(); } - isOpen = true; - suppressInput = true; - Dispatcher.Invoke(() => { + lastMousePosition = WpfScreenHelper.MouseHelper.MousePosition; + + if (settings.BlurScreen) + { + foreach (Rect bounds in WpfScreenHelper.Screen.AllScreens.Select(s => s.WpfBounds)) + { + BlurWindow blurWindow = new() + { + Width = bounds.Width, + Height = bounds.Height, + Left = bounds.Left, + Top = bounds.Top, + }; + + blurWindows.Add(blurWindow); + blurWindow.Show(); + } + + _ = WindowsApi.SetCursorPosition(new Point(100000, 100000)); + } + + isOpen = true; + suppressInput = true; + _ = Activate(); textBlock.Text = settings.LockText; @@ -155,6 +178,9 @@ public partial class LockWindow : Window isOpen = true; + _ = WindowsApi.SetCursorPosition(lastMousePosition); + blurWindows.ForEach(w => w.Close()); + textBlock.Text = settings.UnlockText; textBlock.Stroke = (Brush)FindResource("PaletteGreenBrush"); @@ -203,6 +229,9 @@ public partial class LockWindow : Window isOpen = true; + _ = WindowsApi.SetCursorPosition(lastMousePosition); + blurWindows.ForEach(w => w.Close()); + _ = Activate(); await Task.Delay(500); diff --git a/src/InvLock/MainWindow.xaml b/src/InvLock/MainWindow.xaml index 6ff43a4..b213e80 100644 --- a/src/InvLock/MainWindow.xaml +++ b/src/InvLock/MainWindow.xaml @@ -79,6 +79,20 @@ + + + + + + + + + + + + + + @@ -86,6 +100,13 @@ + + + + + + + diff --git a/src/InvLock/MainWindow.xaml.cs b/src/InvLock/MainWindow.xaml.cs index 926f921..a3c4b76 100644 --- a/src/InvLock/MainWindow.xaml.cs +++ b/src/InvLock/MainWindow.xaml.cs @@ -71,6 +71,7 @@ public partial class MainWindow : FluentWindow e.Cancel = true; ShowInTaskbar = false; + WindowState = WindowState.Minimized; Visibility = Visibility.Collapsed; } } diff --git a/src/InvLock/Settings.cs b/src/InvLock/Settings.cs index 24c960e..4a057d6 100644 --- a/src/InvLock/Settings.cs +++ b/src/InvLock/Settings.cs @@ -17,6 +17,7 @@ public class Settings : INotifyPropertyChanged private static readonly string settingsPath = Path.Combine(App.ApplicationDataPath, "settings.json"); private bool useWindowsLockScreen = true; private bool hideWindows = false; + private bool blurScreen = false; private string lockText = "Lock Screen Active"; private string theme = "Windows default"; private HashSet lockShortcut = [KeyCode.VcLeftControl, KeyCode.VcLeftShift, KeyCode.VcL]; @@ -34,6 +35,16 @@ public class Settings : INotifyPropertyChanged } } + public bool BlurScreen + { + get => blurScreen; + set + { + blurScreen = value; + OnPropertyChanged(); + } + } + public bool UseWindowsLockScreen { get => useWindowsLockScreen; diff --git a/src/InvLock/Utilities/StartupManager.cs b/src/InvLock/Utilities/StartupManager.cs new file mode 100644 index 0000000..0d99ca0 --- /dev/null +++ b/src/InvLock/Utilities/StartupManager.cs @@ -0,0 +1,81 @@ +using IWshRuntimeLibrary; + +using System.IO; +using System.Reflection; +using System.Windows; + +namespace InvLock.Utilities; + +public enum AutoStartStatus +{ + Success = 0, + InvalidParameters = 1, + AlreadyEnabled = 2, + AlreadyDisabled = 3, + UnexpectedError = 4 +} + +public static class StartupManager +{ + private static readonly string appPath = Path.ChangeExtension(Assembly.GetEntryAssembly()?.Location ?? throw new InvalidOperationException("Failed to get the application path."), ".exe"); + private static readonly string startupFolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup); + private static readonly string shortcutPath = Path.Combine(startupFolder, Path.GetFileNameWithoutExtension(appPath) + ".lnk"); + + public static bool IsAutoStartEnabled() + { + return System.IO.File.Exists(shortcutPath); + } + + public static AutoStartStatus EnableAutoStart() + { + if (IsAutoStartEnabled()) + { + return AutoStartStatus.AlreadyEnabled; + } + + try + { + WshShell shell = new WshShell(); + IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutPath); + shortcut.TargetPath = appPath; + shortcut.WorkingDirectory = Path.GetDirectoryName(appPath); + shortcut.Save(); + return AutoStartStatus.Success; + } + catch + { + return AutoStartStatus.UnexpectedError; + } + } + + public static AutoStartStatus DisableAutoStart() + { + if (!IsAutoStartEnabled()) + { + return AutoStartStatus.AlreadyDisabled; + } + + try + { + System.IO.File.Delete(shortcutPath); + return AutoStartStatus.Success; + } + catch + { + return AutoStartStatus.UnexpectedError; + } + } + + public static void ToggleAutoStart() + { + AutoStartStatus status = IsAutoStartEnabled() ? DisableAutoStart() : EnableAutoStart(); + + _ = status switch + { + AutoStartStatus.Success => MessageBox.Show("Auto-start setting changed successfully."), + AutoStartStatus.AlreadyEnabled => MessageBox.Show("Auto-start is already enabled."), + AutoStartStatus.AlreadyDisabled => MessageBox.Show("Auto-start is already disabled."), + _ => MessageBox.Show("Failed to change auto-start setting."), + }; + } +} \ No newline at end of file diff --git a/src/InvLock/Utilities/WindowsApi.cs b/src/InvLock/Utilities/WindowsApi.cs index e8cd4db..681046e 100644 --- a/src/InvLock/Utilities/WindowsApi.cs +++ b/src/InvLock/Utilities/WindowsApi.cs @@ -1,6 +1,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; +using System.Windows; +using System.Windows.Interop; namespace InvLock.Utilities; @@ -70,12 +72,46 @@ public static class WindowsApi _ = ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); } + public static bool SetCursorPosition(Point point) + { + return SetCursorPos((int)point.X, (int)point.Y); + } + + public static void EnableBlur(Window window) + { + WindowInteropHelper windowHelper = new WindowInteropHelper(window); + + AccentPolicy accent = new AccentPolicy + { + AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND + }; + + int accentStructSize = Marshal.SizeOf(accent); + + nint accentPtr = Marshal.AllocHGlobal(accentStructSize); + Marshal.StructureToPtr(accent, accentPtr, false); + + WindowCompositionAttributeData data = new WindowCompositionAttributeData + { + Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY, + SizeOfData = accentStructSize, + Data = accentPtr + }; + + _ = SetWindowCompositionAttribute(windowHelper.Handle, ref data); + + Marshal.FreeHGlobal(accentPtr); + } + [DllImport("user32.dll")] internal static extern bool LockWorkStation(); [DllImport("user32.dll")] internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + [DllImport("user32.dll")] + internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam); @@ -102,4 +138,41 @@ public static class WindowsApi [DllImport("user32.dll")] private static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool SetCursorPos(int x, int y); + + internal enum AccentState + { + ACCENT_DISABLED = 1, + ACCENT_ENABLE_GRADIENT = 0, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, + ACCENT_INVALID_STATE = 4 + } + + internal enum WindowCompositionAttribute + { + // ... + WCA_ACCENT_POLICY = 19 + + // ... + } + + [StructLayout(LayoutKind.Sequential)] + internal struct AccentPolicy + { + public AccentState AccentState; + public int AccentFlags; + public int GradientColor; + public int AnimationId; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WindowCompositionAttributeData + { + public WindowCompositionAttribute Attribute; + public IntPtr Data; + public int SizeOfData; + } } \ No newline at end of file