/ src / modules / MeasureTool / MeasureToolCore / PowerToys.MeasureToolCore.cpp
PowerToys.MeasureToolCore.cpp
  1  #include "pch.h"
  2  
  3  #include <common/display/dpi_aware.h>
  4  #include <common/display/monitors.h>
  5  #include <common/utils/logger_helper.h>
  6  #include <common/utils/UnhandledExceptionHandler.h>
  7  #include <common/logger/logger.h>
  8  
  9  #include "../MeasureToolModuleInterface/trace.h"
 10  #include "constants.h"
 11  #include "PowerToys.MeasureToolCore.h"
 12  #include "Core.g.cpp"
 13  #include "OverlayUI.h"
 14  #include "ScreenCapturing.h"
 15  
 16  //#define DEBUG_PRIMARY_MONITOR_ONLY
 17  
 18  namespace winrt::PowerToys::MeasureToolCore::implementation
 19  {
 20      void Core::MouseCaptureThread()
 21      {
 22          while (!_stopMouseCaptureThreadSignal.is_signaled())
 23          {
 24              static_assert(sizeof(_commonState.cursorPosSystemSpace) == sizeof(LONG64));
 25              POINT cursorPos = {};
 26              GetCursorPos(&cursorPos);
 27              InterlockedExchange64(reinterpret_cast<LONG64*>(&_commonState.cursorPosSystemSpace), std::bit_cast<LONG64>(cursorPos));
 28              std::this_thread::sleep_for(consts::TARGET_FRAME_DURATION);
 29          }
 30      }
 31  
 32      Core::Core() :
 33          _stopMouseCaptureThreadSignal{ wil::EventOptions::ManualReset },
 34          _mouseCaptureThread{ [this] { MouseCaptureThread(); } }
 35      {
 36      }
 37  
 38      Core::~Core()
 39      {
 40          Close();
 41      }
 42  
 43      void Core::Close()
 44      {
 45          ResetState();
 46  
 47          // avoid triggering d2d debug layer leak on shutdown
 48          dxgiAPI = DxgiAPI{ DxgiAPI::Uninitialized{} };
 49  
 50  #if 0
 51          winrt::com_ptr<IDXGIDebug> dxgiDebug;
 52          winrt::check_hresult(DXGIGetDebugInterface1({},
 53                                                      winrt::guid_of<IDXGIDebug>(),
 54                                                      dxgiDebug.put_void()));
 55          dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
 56  #endif
 57  
 58          if (!_stopMouseCaptureThreadSignal.is_signaled())
 59              _stopMouseCaptureThreadSignal.SetEvent();
 60  
 61          if (_mouseCaptureThread.joinable())
 62              _mouseCaptureThread.join();
 63      }
 64  
 65      void Core::InitResources()
 66      {
 67          Measurement::InitResources();
 68      }
 69  
 70      void Core::ResetState()
 71      {
 72          _commonState.closeOnOtherMonitors = true;
 73          _overlayUIStates.clear();
 74          _boundsToolState = { .commonState = &_commonState };
 75          for (auto& thread : _screenCaptureThreads)
 76          {
 77              if (thread.joinable())
 78              {
 79                  thread.join();
 80              }
 81          }
 82          _screenCaptureThreads.clear();
 83          _measureToolState.Reset();
 84          _measureToolState.Access([&](MeasureToolState& s) {
 85              s.commonState = &_commonState;
 86          });
 87  
 88          _settings = Settings::LoadFromFile();
 89  
 90          _commonState.units = _settings.units;
 91          _commonState.lineColor.r = _settings.lineColor[0] / 255.f;
 92          _commonState.lineColor.g = _settings.lineColor[1] / 255.f;
 93          _commonState.lineColor.b = _settings.lineColor[2] / 255.f;
 94          _commonState.closeOnOtherMonitors = false;
 95      }
 96  
 97      void Core::StartBoundsTool()
 98      {
 99          ResetState();
100  
101  #if defined(DEBUG_PRIMARY_MONITOR_ONLY)
102          std::vector<MonitorInfo> monitors = { MonitorInfo::GetPrimaryMonitor() };
103          const auto& monitorInfo = monitors[0];
104  #else
105          const auto monitors = MonitorInfo::GetMonitors(true);
106          for (const auto& monitorInfo : monitors)
107  #endif
108          {
109              auto overlayUI = OverlayUIState::Create(&dxgiAPI,
110                                                      _boundsToolState,
111                                                      _commonState,
112                                                      monitorInfo);
113  #if !defined(DEBUG_PRIMARY_MONITOR_ONLY)
114              if (!overlayUI)
115                  continue;
116  #endif
117              _overlayUIStates.push_back(std::move(overlayUI));
118          }
119  
120          trace.UpdateState(true);
121          Trace::BoundsToolActivated();
122          trace.Flush();
123          trace.UpdateState(false);
124      }
125  
126      void Core::StartMeasureTool(const bool horizontal, const bool vertical)
127      {
128          ResetState();
129  
130          _measureToolState.Access([horizontal, vertical, this](MeasureToolState& state) {
131              if (horizontal)
132                  state.global.mode = vertical ? MeasureToolState::Mode::Cross : MeasureToolState::Mode::Horizontal;
133              else
134                  state.global.mode = MeasureToolState::Mode::Vertical;
135  
136              state.global.continuousCapture = _settings.continuousCapture;
137              state.global.drawFeetOnCross = _settings.drawFeetOnCross;
138              state.global.pixelTolerance = _settings.pixelTolerance;
139              state.global.perColorChannelEdgeDetection = _settings.perColorChannelEdgeDetection;
140          });
141  
142  #if defined(DEBUG_PRIMARY_MONITOR_ONLY)
143          std::vector<MonitorInfo> monitors = { MonitorInfo::GetPrimaryMonitor() };
144          const auto& monitorInfo = monitors[0];
145  #else
146          const auto monitors = MonitorInfo::GetMonitors(true);
147          for (const auto& monitorInfo : monitors)
148  #endif
149          {
150              auto overlayUI = OverlayUIState::Create(&dxgiAPI,
151                                                      _measureToolState,
152                                                      _commonState,
153                                                      monitorInfo);
154  #if !defined(DEBUG_PRIMARY_MONITOR_ONLY)
155              if (!overlayUI)
156                  return;
157  #endif
158              _overlayUIStates.push_back(std::move(overlayUI));
159          }
160  
161          for (size_t i = 0; i < monitors.size(); ++i)
162          {
163              auto thread = StartCapturingThread(
164                  &dxgiAPI,
165                  _commonState,
166                  _measureToolState,
167                  _overlayUIStates[i]->overlayWindowHandle(),
168                  monitors[i]);
169              _screenCaptureThreads.emplace_back(std::move(thread));
170          }
171  
172          trace.UpdateState(true);
173          Trace::MeasureToolActivated();
174          trace.Flush();
175          trace.UpdateState(false);
176      }
177  
178      void MeasureToolCore::implementation::Core::SetToolCompletionEvent(ToolSessionCompleted sessionCompletedTrigger)
179      {
180          _commonState.sessionCompletedCallback = [trigger = std::move(sessionCompletedTrigger)] {
181              trigger();
182          };
183      }
184  
185      void MeasureToolCore::implementation::Core::SetToolbarBoundingBox(const uint32_t fromX,
186                                                                        const uint32_t fromY,
187                                                                        const uint32_t toX,
188                                                                        const uint32_t toY)
189      {
190          _commonState.toolbarBoundingBox = Box{ RECT{ .left = static_cast<long>(fromX),
191                                                       .top = static_cast<long>(fromY),
192                                                       .right = static_cast<long>(toX),
193                                                       .bottom = static_cast<long>(toY) } };
194      }
195  
196      float MeasureToolCore::implementation::Core::GetDPIScaleForWindow(uint64_t windowHandle)
197      {
198          UINT dpi = DPIAware::DEFAULT_DPI;
199          DPIAware::GetScreenDPIForWindow(std::bit_cast<HWND>(windowHandle), dpi);
200          return static_cast<float>(dpi) / DPIAware::DEFAULT_DPI;
201      }
202  }