using System.IO; using System.Threading; using System.Windows; using Forms = System.Windows.Forms; namespace OmniNotify; public partial class App : System.Windows.Application { private const string SingleInstanceMutexName = @"Local\OmniNotify.SingleInstance"; private const string ActivateEventName = @"Local\OmniNotify.Activate"; private Forms.NotifyIcon? _notifyIcon; private AppStore? _store; private LocalHttpServer? _server; private Mutex? _singleInstanceMutex; private bool _ownsSingleInstanceMutex; private EventWaitHandle? _activateEvent; private RegisteredWaitHandle? _activateWait; public AppState State { get; private set; } = new(); public NotificationRouter Router { get; private set; } = null!; public PopupCoordinator PopupCoordinator { get; private set; } = null!; public string ListenUrl => _server?.Url ?? ""; public string? ServerError => _server?.LastError; protected override void OnStartup(StartupEventArgs e) { _singleInstanceMutex = new Mutex(true, SingleInstanceMutexName, out var createdNew); _ownsSingleInstanceMutex = createdNew; _activateEvent = new EventWaitHandle(false, EventResetMode.AutoReset, ActivateEventName); if (!createdNew) { _activateEvent.Set(); Shutdown(); return; } RegisterActivationListener(); base.OnStartup(e); _store = new AppStore(); State = _store.Load(); PopupCoordinator = new PopupCoordinator(); Router = new NotificationRouter(State, _store, PopupCoordinator); Router.StateChanged += UpdateTrayMenu; _server = new LocalHttpServer(Router); _server.Start(State.Settings.LocalPort); CreateTrayIcon(); ShowMainWindow(); } public void SaveState() { _store?.Save(State); UpdateTrayMenu(); } public void ShowMainWindow() { if (MainWindow is null) { MainWindow = new MainWindow(); } MainWindow.Show(); MainWindow.WindowState = WindowState.Normal; MainWindow.Activate(); } private void RegisterActivationListener() { if (_activateEvent is null) { return; } _activateWait = ThreadPool.RegisterWaitForSingleObject( _activateEvent, (_, _) => Dispatcher.Invoke(ShowMainWindow), null, Timeout.InfiniteTimeSpan, false); } private void CreateTrayIcon() { var iconPath = Path.Combine(AppContext.BaseDirectory, "app.ico"); _notifyIcon = new Forms.NotifyIcon { Icon = new System.Drawing.Icon(iconPath), Text = "Omni-Notify", Visible = true }; _notifyIcon.MouseClick += (_, args) => { if (args.Button == Forms.MouseButtons.Left) { Dispatcher.Invoke(ShowMainWindow); } }; UpdateTrayMenu(); } private void UpdateTrayMenu() { if (_notifyIcon is null) { return; } var menu = new Forms.ContextMenuStrip(); menu.Items.Add("打开主控制面板", null, (_, _) => Dispatcher.Invoke(ShowMainWindow)); menu.Items.Add("全局设置", null, (_, _) => Dispatcher.Invoke(() => { ShowMainWindow(); if (MainWindow is MainWindow window) { window.FocusSettingsTab(); } })); menu.Items.Add(State.Settings.DndEnabled ? "关闭免打扰" : "开启免打扰", null, (_, _) => Dispatcher.Invoke(() => { State.Settings.DndEnabled = !State.Settings.DndEnabled; SaveState(); })); var resetBreaker = menu.Items.Add("解除熔断", null, (_, _) => Dispatcher.Invoke(() => Router.ResetCircuitBreaker())); resetBreaker.Enabled = State.Settings.CircuitBreakerOpen; resetBreaker.ForeColor = State.Settings.CircuitBreakerOpen ? System.Drawing.Color.Firebrick : System.Drawing.SystemColors.ControlText; menu.Items.Add(new Forms.ToolStripSeparator()); menu.Items.Add("退出软件", null, (_, _) => Dispatcher.Invoke(Shutdown)); _notifyIcon.ContextMenuStrip = menu; } protected override void OnExit(ExitEventArgs e) { _activateWait?.Unregister(null); _activateEvent?.Dispose(); if (_ownsSingleInstanceMutex) { _singleInstanceMutex?.ReleaseMutex(); } _singleInstanceMutex?.Dispose(); _notifyIcon?.Dispose(); _server?.Dispose(); _store?.Save(State); base.OnExit(e); } }