diff --git a/AuthenticatorChooser/Startup.cs b/AuthenticatorChooser/Startup.cs index 557b19e..7c312ac 100644 --- a/AuthenticatorChooser/Startup.cs +++ b/AuthenticatorChooser/Startup.cs @@ -90,8 +90,9 @@ public int OnExecute() { logger.Info("Operating system is {name} {marketingVersion} {version} {arch}", os.name, os.marketingVersion, os.version, os.architecture); logger.Info("{Locales are} {locales}", I18N.LOCALE_NAMES.Count == 1 ? "Locale is" : "Locales are", string.Join(", ", I18N.LOCALE_NAMES)); + using TrayIcon trayIcon = new TrayIcon(() => { EXITING_TRIGGER.Cancel(); Application.Exit(); }); using WindowOpeningListener windowOpeningListener = new WindowOpeningListenerImpl(); - WindowsSecurityKeyChooser securityKeyChooser = new(new ChooserOptions(skipAllNonSecurityKeyOptions, autosubmitPinLength)); + WindowsSecurityKeyChooser securityKeyChooser = new(new ChooserOptions(skipAllNonSecurityKeyOptions, autosubmitPinLength), trayIcon); windowOpeningListener.windowOpened += (_, window) => securityKeyChooser.chooseUsbSecurityKey(window); diff --git a/AuthenticatorChooser/TrayIcon.cs b/AuthenticatorChooser/TrayIcon.cs new file mode 100644 index 0000000..4040a97 --- /dev/null +++ b/AuthenticatorChooser/TrayIcon.cs @@ -0,0 +1,41 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace AuthenticatorChooser; + +public class TrayIcon: IDisposable { + + private readonly NotifyIcon notifyIcon; + private readonly ToolStripMenuItem enabledMenuItem; + + public bool isEnabled => enabledMenuItem.Checked; + + public TrayIcon(Action onExit) { + enabledMenuItem = new ToolStripMenuItem("Enabled") { + Checked = true, + CheckOnClick = true + }; + + ToolStripMenuItem exitMenuItem = new("Exit"); + exitMenuItem.Click += (_, _) => onExit(); + + ContextMenuStrip contextMenu = new(); + contextMenu.Items.Add(enabledMenuItem); + contextMenu.Items.Add(new ToolStripSeparator()); + contextMenu.Items.Add(exitMenuItem); + + notifyIcon = new NotifyIcon { + Text = nameof(AuthenticatorChooser), + Icon = Icon.ExtractAssociatedIcon(Environment.ProcessPath!)!, + ContextMenuStrip = contextMenu, + Visible = true + }; + } + + public void Dispose() { + notifyIcon.Visible = false; + notifyIcon.Dispose(); + GC.SuppressFinalize(this); + } + +} diff --git a/AuthenticatorChooser/Windows11/WindowsSecurityKeyChooser.cs b/AuthenticatorChooser/Windows11/WindowsSecurityKeyChooser.cs index 4767650..2b21cab 100644 --- a/AuthenticatorChooser/Windows11/WindowsSecurityKeyChooser.cs +++ b/AuthenticatorChooser/Windows11/WindowsSecurityKeyChooser.cs @@ -7,7 +7,7 @@ namespace AuthenticatorChooser.Windows11; -public class WindowsSecurityKeyChooser(ChooserOptions options): AbstractSecurityKeyChooser { +public class WindowsSecurityKeyChooser(ChooserOptions options, TrayIcon trayIcon): AbstractSecurityKeyChooser { // #4: unfortunately, this class name is shared with the UAC prompt, detectable when desktop dimming is disabled private const string WINDOW_CLASS_NAME = "Credential Dialog Xaml Host"; @@ -71,7 +71,7 @@ public override void chooseUsbSecurityKey(SystemWindow fidoPrompt) { bool isShiftDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); - strategy.handleWindow(actualTitle!, fidoEl, outerScrollViewer, isShiftDown); + strategy.handleWindow(actualTitle!, fidoEl, outerScrollViewer, isShiftDown || !trayIcon.isEnabled); } catch (ElementNotAvailableException e) { LOGGER.Error(e, "Element in Windows Security dialog box disappeared before this program could interact with it, skipping this dialog box instance");