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