using System.IO; using System.Windows; using System.Windows.Interop; namespace DeskCore; public partial class App : Application { private const string MutexName = "DeskCore.Instanz"; public static MainWindow? Win { get; private set; } public static Tray? TrayIcon { get; private set; } public static bool Quitting { get; private set; } private static Hotkey? _hotkey; private static Hotkey? _peekHotkey; private static Hotkey? _pagePrev, _pageNext, _quickSearch; private static System.Windows.Threading.DispatcherTimer? _desktopTimer; private static Watcher? _watcher; private Mutex? _mutex; protected override void OnStartup(StartupEventArgs e) { // Nur eine Instanz: weitere Aufrufe reichen ihren Befehl (oder "Fenster zeigen") an die laufende weiter _mutex = new Mutex(true, MutexName, out bool first); if (!first) { Commands.Send(e.Args); Shutdown(); return; } Migration.Run(); // alte "Desktop Sortierer"-Ordner übernehmen – vor allem anderen Commands.Listen(args => Dispatcher.BeginInvoke(() => HandleCommand(args))); DispatcherUnhandledException += (_, args) => { var log = Path.Combine(Storage.Root, "fehler.log"); try { File.AppendAllText(log, $"[{DateTime.Now}] {args.Exception}\n\n"); } catch { } MessageBox.Show($"Unerwarteter Fehler:\n\n{args.Exception.Message}\n\nDetails: {log}", "DeskCore", MessageBoxButton.OK, MessageBoxImage.Error); args.Handled = true; }; base.OnStartup(e); ShutdownMode = ShutdownMode.OnExplicitShutdown; Storage.FlushHolding(); // Reste eines Absturzes in den Papierkorb Theme.Init(); Theme.Changed += FenceManager.RefreshAll; Win = new MainWindow(); MainWindow = Win; var hwnd = new WindowInteropHelper(Win).EnsureHandle(); _hotkey = new Hotkey(hwnd); _hotkey.Pressed += () => Win.ToggleFromHotkey(); ApplyHotkey(); _peekHotkey = new Hotkey(hwnd, 0xB002); _peekHotkey.Pressed += FenceManager.TogglePeek; ApplyPeekHotkey(); // Desktop-Seiten: Win + Alt + Pfeil links/rechts _pagePrev = new Hotkey(hwnd, 0xB003); _pagePrev.Pressed += () => FenceManager.NextPage(-1); _pagePrev.Register(Hotkey.ModWin | Hotkey.ModAlt, 0x25); _pageNext = new Hotkey(hwnd, 0xB004); _pageNext.Pressed += () => FenceManager.NextPage(1); _pageNext.Register(Hotkey.ModWin | Hotkey.ModAlt, 0x27); _quickSearch = new Hotkey(hwnd, 0xB005); _quickSearch.Pressed += QuickSearch.Toggle; ApplyQuickSearchHotkey(); TrayIcon = new Tray(); TrayIcon.Clicked += () => Win.ShowFromBackground(); TrayIcon.BalloonClicked += () => Win.ShowFromBackground(); TrayIcon.MenuRequested += () => Win.ShowTrayMenu(); ApplyTray(); _watcher = new Watcher(Dispatcher); _watcher.Changed += () => { Win.OnStorageChanged(); FenceManager.RefreshAll(); Win.ScheduleDeadShortcutScan(); // z. B. Deinstallation hat die Desktop-Verknüpfung entfernt }; _watcher.NewOnDesktop += Win.AutoSortNew; Autostart.RefreshPath(); // Mit einem Befehl gestartet (z. B. Start-Gruppe per Desktop-Verknüpfung)? Dann ohne Fenster. if (Commands.WantsWindow(e.Args) || !AppSettings.Current.TrayEnabled) Win.Show(); FenceManager.Sync(); // Bildschirm abgesteckt/Auflösung geändert → Fenster und Boxen wieder in den sichtbaren Bereich holen Microsoft.Win32.SystemEvents.DisplaySettingsChanged += (_, _) => Dispatcher.BeginInvoke(() => { Win.EnsureOnScreen(); FenceManager.OnDisplayChanged(); }); Win.CheckForUpdatesOnStartup(); if (!Commands.WantsWindow(e.Args)) Dispatcher.BeginInvoke(() => HandleCommand(e.Args)); Win.StartDeadShortcutWatch(); DesktopToggle.Apply(); // Positionen der Desktop-Icons regelmäßig lesen (für Aufziehen, Doppelklick und die Icon-Tönung) _desktopTimer = new System.Windows.Threading.DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; _desktopTimer.Tick += (_, _) => RefreshDesktopIcons(); _desktopTimer.Start(); RefreshDesktopIcons(); } private static void RefreshDesktopIcons() { var s = AppSettings.Current; if (Quitting) return; PageScheduler.Tick(); // Nur lesen, wenn eine Funktion die Positionen braucht if (s.DesktopTint is null && !s.DrawFences && !s.QuickHide) return; try { DesktopIcons.RefreshCache(); } catch { /* Explorer gerade neu gestartet o. Ä. */ } DesktopTint.Refresh(); } /// Befehl von der Befehlszeile oder einem weiteren Aufruf ausführen. private static void HandleCommand(string[] args) { if (Win is null || Quitting) return; if (Commands.Value(args, "--launch") is { } group) Win.LaunchGroupByName(group); else if (Commands.Value(args, "--snapshot") is { } snap) Win.RestoreSnapshotByName(snap); else if (Commands.Has(args, "--toggle-desktop")) DesktopToggle.Toggle(); else if (Commands.Has(args, "--peek")) FenceManager.TogglePeek(); else if (Commands.Has(args, "--search")) QuickSearch.Toggle(); else if (Commands.Value(args, "--page") is { } page) SwitchPageByName(page); else if (Commands.WantsWindow(args)) Win.ShowFromBackground(); } /// DeskCore.exe --page "Name" oder --page 2. private static void SwitchPageByName(string page) { var pages = AppSettings.Current.Pages; int i = pages.FindIndex(p => string.Equals(p, page, StringComparison.OrdinalIgnoreCase)); if (i < 0 && int.TryParse(page, out var n)) i = n - 1; if (i >= 0 && i < pages.Count) FenceManager.SwitchPage(i); else TrayIcon?.Notify("Seite nicht gefunden", $"Es gibt keine Desktop-Seite „{page}“."); } public static bool QuickSearchRegistered { get; private set; } /// Tastenkürzel der Schnellstart-Suche neu registrieren. false = von anderem Programm belegt. public static bool ApplyQuickSearchHotkey() { if (_quickSearch is null) return false; var s = AppSettings.Current; if (!s.QuickSearchEnabled) { _quickSearch.Unregister(); return QuickSearchRegistered = true; } return QuickSearchRegistered = _quickSearch.Register(s.QuickSearchModifiers, s.QuickSearchKey); } /// Tastenkürzel neu registrieren. false = von anderem Programm belegt. public static bool ApplyHotkey() { if (_hotkey is null) return false; var s = AppSettings.Current; if (!s.HotkeyEnabled) { _hotkey.Unregister(); return true; } return _hotkey.Register(s.HotkeyModifiers, s.HotkeyKey); } /// Peek-Kürzel neu registrieren. false = von anderem Programm belegt. public static bool ApplyPeekHotkey() { if (_peekHotkey is null) return false; var s = AppSettings.Current; if (!s.PeekEnabled) { _peekHotkey.Unregister(); return true; } return _peekHotkey.Register(s.PeekModifiers, s.PeekKey); } public static void ApplyTray() { if (TrayIcon is not null) TrayIcon.Visible = AppSettings.Current.TrayEnabled; } public static void Quit() { if (Quitting) return; Quitting = true; Win?.CancelBusyAndWait(); // z. B. große Kopie abbrechen, halbe Dateien aufräumen Win?.SavePlacement(); _desktopTimer?.Stop(); DesktopToggle.Stop(); DesktopToggle.RestoreOnExit(); _peekHotkey?.Dispose(); _pagePrev?.Dispose(); _quickSearch?.Dispose(); Usage.Flush(); _pageNext?.Dispose(); Win?.Undo.FlushAll(); FenceManager.CloseAll(); _watcher?.Dispose(); _hotkey?.Dispose(); TrayIcon?.Dispose(); Win?.Close(); Current.Shutdown(); } protected override void OnExit(ExitEventArgs e) { TrayIcon?.Dispose(); _mutex?.Dispose(); base.OnExit(e); } }