@@ -38,6 +38,26 @@ namespace {
3838 toString (const LUID &id) {
3939 return std::to_string (id.HighPart ) + std::to_string (id.LowPart );
4040 }
41+
42+ /* *
43+ * @brief Check if the source modes are duplicated (cloned).
44+ * @param lhs First mode to check.
45+ * @param rhs Second mode to check.
46+ * @returns True if both mode have the same origin point, false otherwise.
47+ * @note Windows enforces the behaviour that only the duplicate devices can
48+ * have the same origin point as otherwise the configuration is considered invalid by the OS.
49+ *
50+ * EXAMPLES:
51+ * ```cpp
52+ * DISPLAYCONFIG_SOURCE_MODE mode_a;
53+ * DISPLAYCONFIG_SOURCE_MODE mode_b;
54+ * const bool are_duplicated = are_modes_duplicated(mode_a, mode_b);
55+ * ```
56+ */
57+ bool
58+ are_modes_duplicated (const DISPLAYCONFIG_SOURCE_MODE &lhs, const DISPLAYCONFIG_SOURCE_MODE &rhs) {
59+ return lhs.position .x == rhs.position .x && lhs.position .y == rhs.position .y ;
60+ }
4161} // namespace
4262
4363namespace display_device ::win_utils {
@@ -169,13 +189,13 @@ namespace display_device::win_utils {
169189 }
170190
171191 std::optional<ValidatedDeviceInfo>
172- getDeviceInfoForValidPath (const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, bool must_be_active ) {
192+ getDeviceInfoForValidPath (const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, const ValidatedPathType type ) {
173193 if (!isAvailable (path)) {
174194 // Could be transient issue according to MSDOCS (no longer available, but still "active")
175195 return std::nullopt ;
176196 }
177197
178- if (must_be_active ) {
198+ if (type == ValidatedPathType::Active ) {
179199 if (!isActive (path)) {
180200 return std::nullopt ;
181201 }
@@ -199,6 +219,27 @@ namespace display_device::win_utils {
199219 return ValidatedDeviceInfo { device_path, device_id };
200220 }
201221
222+ const DISPLAYCONFIG_PATH_INFO *
223+ getActivePath (const WinApiLayerInterface &w_api, const std::string &device_id, const std::vector<DISPLAYCONFIG_PATH_INFO> &paths) {
224+ for (const auto &path : paths) {
225+ const auto device_info { getDeviceInfoForValidPath (w_api, path, ValidatedPathType::Active) };
226+ if (!device_info) {
227+ continue ;
228+ }
229+
230+ if (device_info->m_device_id == device_id) {
231+ return &path;
232+ }
233+ }
234+
235+ return nullptr ;
236+ }
237+
238+ DISPLAYCONFIG_PATH_INFO *
239+ getActivePath (const WinApiLayerInterface &w_api, const std::string &device_id, std::vector<DISPLAYCONFIG_PATH_INFO> &paths) {
240+ return const_cast <DISPLAYCONFIG_PATH_INFO *>(getActivePath (w_api, device_id, const_cast <const std::vector<DISPLAYCONFIG_PATH_INFO> &>(paths)));
241+ }
242+
202243 PathSourceIndexDataMap
203244 collectSourceDataForMatchingPaths (const WinApiLayerInterface &w_api, const std::vector<DISPLAYCONFIG_PATH_INFO> &paths) {
204245 PathSourceIndexDataMap path_data;
@@ -207,7 +248,7 @@ namespace display_device::win_utils {
207248 for (std::size_t index = 0 ; index < paths.size (); ++index) {
208249 const auto &path { paths[index] };
209250
210- const auto device_info { getDeviceInfoForValidPath (w_api, path, false ) };
251+ const auto device_info { getDeviceInfoForValidPath (w_api, path, ValidatedPathType::Any ) };
211252 if (!device_info) {
212253 // Path is not valid
213254 continue ;
@@ -376,4 +417,79 @@ namespace display_device::win_utils {
376417 }
377418 return new_paths;
378419 }
420+
421+ std::set<std::string>
422+ getAllDeviceIdsAndMatchingDuplicates (const WinApiLayerInterface &w_api, const std::set<std::string> &device_ids) {
423+ const auto display_data { w_api.queryDisplayConfig (QueryType::Active) };
424+ if (!display_data) {
425+ // Error already logged
426+ return {};
427+ }
428+
429+ std::set<std::string> all_device_ids;
430+ for (const auto &device_id : device_ids) {
431+ if (device_id.empty ()) {
432+ DD_LOG (error) << " Device it is empty!" ;
433+ return {};
434+ }
435+
436+ const auto provided_path { getActivePath (w_api, device_id, display_data->m_paths ) };
437+ if (!provided_path) {
438+ DD_LOG (warning) << " Failed to find device for " << device_id << " !" ;
439+ return {};
440+ }
441+
442+ const auto provided_path_source_mode { getSourceMode (getSourceIndex (*provided_path, display_data->m_modes ), display_data->m_modes ) };
443+ if (!provided_path_source_mode) {
444+ DD_LOG (error) << " Active device does not have a source mode: " << device_id << " !" ;
445+ return {};
446+ }
447+
448+ // We will now iterate over all the active paths (provided path included) and check if
449+ // any of them are duplicated.
450+ for (const auto &path : display_data->m_paths ) {
451+ const auto device_info { getDeviceInfoForValidPath (w_api, path, ValidatedPathType::Active) };
452+ if (!device_info) {
453+ continue ;
454+ }
455+
456+ if (all_device_ids.contains (device_info->m_device_id )) {
457+ // Already checked
458+ continue ;
459+ }
460+
461+ const auto source_mode { getSourceMode (getSourceIndex (path, display_data->m_modes ), display_data->m_modes ) };
462+ if (!source_mode) {
463+ DD_LOG (error) << " Active device does not have a source mode: " << device_info->m_device_id << " !" ;
464+ return {};
465+ }
466+
467+ if (!are_modes_duplicated (*provided_path_source_mode, *source_mode)) {
468+ continue ;
469+ }
470+
471+ all_device_ids.insert (device_info->m_device_id );
472+ }
473+ }
474+
475+ return all_device_ids;
476+ }
477+
478+ bool
479+ fuzzyCompareRefreshRates (const Rational &lhs, const Rational &rhs) {
480+ if (lhs.m_denominator > 0 && rhs.m_denominator > 0 ) {
481+ const float lhs_f { static_cast <float >(lhs.m_numerator ) / static_cast <float >(lhs.m_denominator ) };
482+ const float rhs_f { static_cast <float >(rhs.m_numerator ) / static_cast <float >(rhs.m_denominator ) };
483+ return (std::abs (lhs_f - rhs_f) <= 0 .9f );
484+ }
485+
486+ return false ;
487+ }
488+
489+ bool
490+ fuzzyCompareModes (const DisplayMode &lhs, const DisplayMode &rhs) {
491+ return lhs.m_resolution .m_width == rhs.m_resolution .m_width &&
492+ lhs.m_resolution .m_height == rhs.m_resolution .m_height &&
493+ fuzzyCompareRefreshRates (lhs.m_refresh_rate , rhs.m_refresh_rate );
494+ }
379495} // namespace display_device::win_utils
0 commit comments