QuickAccessCoordinator.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Threading.Tasks; 7 using Common.UI; 8 using ManagedCommon; 9 using Microsoft.PowerToys.QuickAccess.Helpers; 10 using Microsoft.PowerToys.Settings.UI.Library; 11 using Microsoft.PowerToys.Settings.UI.Library.Helpers; 12 using PowerToys.Interop; 13 14 namespace Microsoft.PowerToys.QuickAccess.Services; 15 16 internal sealed class QuickAccessCoordinator : IQuickAccessCoordinator, IDisposable 17 { 18 private readonly MainWindow _window; 19 private readonly QuickAccessLaunchContext _launchContext; 20 private readonly SettingsUtils _settingsUtils = SettingsUtils.Default; 21 private readonly object _generalSettingsLock = new(); 22 private readonly object _ipcLock = new(); 23 private TwoWayPipeMessageIPCManaged? _ipcManager; 24 private bool _ipcUnavailableLogged; 25 26 public QuickAccessCoordinator(MainWindow window, QuickAccessLaunchContext launchContext) 27 { 28 _window = window; 29 _launchContext = launchContext; 30 InitializeIpc(); 31 } 32 33 public bool IsRunnerElevated => false; // TODO: wire up real elevation state. 34 35 public void HideFlyout() 36 { 37 _window.RequestHide(); 38 } 39 40 public void OpenSettings() 41 { 42 Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Dashboard); 43 _window.RequestHide(); 44 } 45 46 public void OpenGeneralSettingsForUpdates() 47 { 48 Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Overview); 49 _window.RequestHide(); 50 } 51 52 public Task<bool> ShowDocumentationAsync() 53 { 54 Logger.LogInfo("QuickAccessCoordinator.ShowDocumentationAsync is not yet connected."); 55 return Task.FromResult(false); 56 } 57 58 public bool UpdateModuleEnabled(ModuleType moduleType, bool isEnabled) 59 => TrySendIpcMessage($"{{\"module_status\": {{\"{ModuleHelper.GetModuleKey(moduleType)}\": {isEnabled.ToString().ToLowerInvariant()}}}}}", "module status update"); 60 61 public void ReportBug() 62 { 63 if (!TrySendIpcMessage("{\"bugreport\": 0 }", "bug report request")) 64 { 65 Logger.LogWarning("QuickAccessCoordinator: failed to dispatch bug report request; IPC unavailable."); 66 } 67 } 68 69 public void OnModuleLaunched(ModuleType moduleType) 70 { 71 Logger.LogInfo($"QuickAccessLauncher invoked module {moduleType}."); 72 } 73 74 public void Dispose() 75 { 76 DisposeIpc(); 77 } 78 79 private void InitializeIpc() 80 { 81 if (string.IsNullOrEmpty(_launchContext.RunnerPipeName) || string.IsNullOrEmpty(_launchContext.AppPipeName)) 82 { 83 Logger.LogWarning("QuickAccessCoordinator: IPC pipe names not provided. Runner will not receive updates."); 84 return; 85 } 86 87 try 88 { 89 _ipcManager = new TwoWayPipeMessageIPCManaged(_launchContext.AppPipeName, _launchContext.RunnerPipeName, OnIpcMessageReceived); 90 _ipcManager.Start(); 91 _ipcUnavailableLogged = false; 92 } 93 catch (Exception ex) 94 { 95 Logger.LogError("QuickAccessCoordinator: failed to start IPC channel to runner.", ex); 96 DisposeIpc(); 97 } 98 } 99 100 private void OnIpcMessageReceived(string message) 101 { 102 Logger.LogDebug($"QuickAccessCoordinator received IPC payload: {message}"); 103 } 104 105 public void SendSortOrderUpdate(GeneralSettings generalSettings) 106 { 107 var outgoing = new OutGoingGeneralSettings(generalSettings); 108 TrySendIpcMessage(outgoing.ToString(), "sort order update"); 109 } 110 111 private bool TrySendIpcMessage(string payload, string operationDescription) 112 { 113 lock (_ipcLock) 114 { 115 if (_ipcManager == null) 116 { 117 if (!_ipcUnavailableLogged) 118 { 119 _ipcUnavailableLogged = true; 120 Logger.LogWarning($"QuickAccessCoordinator: unable to send {operationDescription} because IPC is not available."); 121 } 122 123 return false; 124 } 125 126 try 127 { 128 _ipcManager.Send(payload); 129 return true; 130 } 131 catch (Exception ex) 132 { 133 Logger.LogError($"QuickAccessCoordinator: failed to send {operationDescription}.", ex); 134 return false; 135 } 136 } 137 } 138 139 private void DisposeIpc() 140 { 141 lock (_ipcLock) 142 { 143 if (_ipcManager == null) 144 { 145 return; 146 } 147 148 try 149 { 150 _ipcManager.End(); 151 } 152 catch (Exception ex) 153 { 154 Logger.LogWarning($"QuickAccessCoordinator: exception while shutting down IPC. {ex.Message}"); 155 } 156 157 _ipcManager.Dispose(); 158 _ipcManager = null; 159 _ipcUnavailableLogged = false; 160 } 161 } 162 }