/ src / settings-ui / QuickAccess.UI / Services / QuickAccessCoordinator.cs
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  }