Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/windows/include/displaydevice/windows/settingsmanager.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <memory>

// local includes
Expand DownExpand Up@@ -52,21 +53,23 @@ namespace display_device {
* @param config Configuration to be used for preparing topology.
* @param topology_before_changes The current topology before any changes.
* @param release_context Specifies whether the audio context should be released at the very end IF everything else has succeeded.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return A tuple of (new_state that is to be updated/persisted, device_to_configure, additional_devices_to_configure).
*/
[[nodiscard]] std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context);
prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched);

/**
* @brief Changes or restores the primary device based on the cached state, new state and configuration.
* @param config Configuration to be used for preparing primary device.
* @param device_to_configure The main device to be used for preparation.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the display modes based on the cached state, new state and configuration.
Expand All@@ -75,10 +78,11 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Changes or restores the HDR states based on the cached state, new state and configuration.
Expand All@@ -87,22 +91,29 @@ namespace display_device {
* @param additional_devices_to_configure Additional devices that should be configured.
* @param guard_fn Reference to the guard function which will be set to restore original state (if needed) in case something else fails down the line.
* @param new_state Reference to the new state which is to be updated accordingly.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @return True if no errors have occured, false otherwise.
*/
[[nodiscard]] bool
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state);
prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched);

/**
* @brief Try to revert the modified settings.
* @param current_topology Topology before this method is called.
* @param system_settings_touched Inticates whether a "write" operation could have been performed on the OS.
* @returns True on success, false otherwise.
* @warning The method assumes that the caller will ensure restoring the topology
* in case of a failure!
*/
[[nodiscard]] bool
revertModifiedSettings();
revertModifiedSettings(const ActiveTopology &current_topology, bool &system_settings_touched);

std::shared_ptr<WinDisplayDeviceInterface> m_dd_api;
std::shared_ptr<AudioContextInterface> m_audio_context_api;
std::unique_ptr<PersistentState> m_persistence_state;

private:
/** @see win_utils::blankHdrStates for more details. */
std::chrono::milliseconds m_hdr_blank_delay { 500 }; // 500ms should be more than enough...
};
} // namespace display_device
24 changes: 24 additions & 0 deletions src/windows/include/displaydevice/windows/settingsutils.h
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
#pragma once

// system includes
#include <chrono>
#include <tuple>

// local includes
Expand DownExpand Up@@ -133,6 +134,29 @@ namespace display_device::win_utils {
const std::set<std::string> &additional_devices_to_configure,
const HdrStateMap &original_states);

/**
* @brief Toggle enabled HDR states off and on again if quick succession.
*
* This is a workaround for a HDR highcontrast color bug which prominently
* happens for IDD HDR displays, but also sometimes (very VERY rarely) for
* dongles.
*
* The bug is cause my more or less any change to the display settings, such as
* enabling HDR display, enabling HDR state, changing display mode of OTHER
* device and so on.
*
* The workaround is to simply turn of HDR, wait a little and then turn it back
* on.
*
* This is what this function does, but only if there are HDR enabled displays
* at the moment.
*
* @param win_dd Interface for interacting with the OS.
* @param delay Delay between OFF and ON states (ON -> OFF -> DELAY -> ON).
*/
void
blankHdrStates(WinDisplayDeviceInterface &win_dd, std::chrono::milliseconds delay);

/**
* @brief Make guard function for the topology.
* @param win_dd Interface for interacting with the OS.
Expand Down
39 changes: 29 additions & 10 deletions src/windows/settingsmanagerapply.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,13 @@ namespace display_device {
DD_LOG(info) << "Active topology before any changes:\n"
<< toJson(topology_before_changes);

bool system_settings_touched { false };
boost::scope::scope_exit hdr_blank_always_executed_guard { [this, &system_settings_touched]() {
if (system_settings_touched) {
win_utils::blankHdrStates(*m_dd_api, m_hdr_blank_delay);
}
} };

bool release_context { false };
boost::scope::scope_exit topology_prep_guard { [this, topology = topology_before_changes, was_captured = m_audio_context_api->isCaptured(), &release_context]() {
// It is possible that during topology preparation, some settings will be reverted for the modified topology.
Expand All@@ -61,7 +68,7 @@ namespace display_device {
}
} };

const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context) };
const auto &prepped_topology_data { prepareTopology(config, topology_before_changes, release_context, system_settings_touched) };
if (!prepped_topology_data) {
// Error already logged
return ApplyResult::DevicePrepFailed;
Expand All@@ -70,21 +77,21 @@ namespace display_device {

DdGuardFn primary_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> primary_guard { primary_guard_fn };
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state)) {
if (!preparePrimaryDevice(config, device_to_configure, primary_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::PrimaryDevicePrepFailed;
}

DdGuardFn mode_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> mode_guard { mode_guard_fn };
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state)) {
if (!prepareDisplayModes(config, device_to_configure, additional_devices_to_configure, mode_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::DisplayModePrepFailed;
}

DdGuardFn hdr_state_guard_fn { noopFn };
boost::scope::scope_exit<DdGuardFn &> hdr_state_guard { hdr_state_guard_fn };
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state)) {
if (!prepareHdrStates(config, device_to_configure, additional_devices_to_configure, hdr_state_guard_fn, new_state, system_settings_touched)) {
// Error already logged
return ApplyResult::HdrStatePrepFailed;
}
Expand All@@ -110,7 +117,7 @@ namespace display_device {
}

std::optional<std::tuple<SingleDisplayConfigState, std::string, std::set<std::string>>>
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context) {
SettingsManager::prepareTopology(const SingleDisplayConfiguration &config, const ActiveTopology &topology_before_changes, bool &release_context, bool &system_settings_touched) {
const EnumeratedDeviceList devices { m_dd_api->enumAvailableDevices() };
if (devices.empty()) {
DD_LOG(error) << "Failed to enumerate display devices!";
Expand DownExpand Up@@ -161,7 +168,7 @@ namespace display_device {
if (change_is_needed) {
if (cached_state && !m_dd_api->isTopologyTheSame(cached_state->m_modified.m_topology, new_topology)) {
DD_LOG(warning) << "To apply new display device settings, previous modifications must be undone! Trying to undo them now.";
if (!revertModifiedSettings()) {
if (!revertModifiedSettings(topology_before_changes, system_settings_touched)) {
DD_LOG(error) << "Failed to apply new configuration, because the previous settings could not be reverted!";
return std::nullopt;
}
Expand All@@ -182,6 +189,7 @@ namespace display_device {
}
}

system_settings_touched = true;
if (!m_dd_api->setTopology(new_topology)) {
DD_LOG(error) << "Failed to apply new configuration, because a new topology could not be set!";
return std::nullopt;
Expand All@@ -196,7 +204,7 @@ namespace display_device {
}

bool
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::preparePrimaryDevice(const SingleDisplayConfiguration &config, const std::string &device_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_primary_device { cached_state ? cached_state->m_modified.m_original_primary_device : std::string {} };
const bool ensure_primary { config.m_device_prep == SingleDisplayConfiguration::DevicePreparation::EnsurePrimary };
Expand All@@ -214,6 +222,8 @@ namespace display_device {

const auto try_change { [&](const std::string &new_device, const auto info_preamble, const auto error_log) {
if (current_primary_device != new_device) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_device);
if (!m_dd_api->setAsPrimary(new_device)) {
DD_LOG(error) << error_log;
Expand DownExpand Up@@ -251,7 +261,7 @@ namespace display_device {
}

bool
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareDisplayModes(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_display_modes { cached_state ? cached_state->m_modified.m_original_modes : DeviceDisplayModeMap {} };
const bool change_required { config.m_resolution || config.m_refresh_rate };
Expand All@@ -270,11 +280,18 @@ namespace display_device {
if (current_display_modes != new_modes) {
DD_LOG(info) << info_preamble << toJson(new_modes);
if (!m_dd_api->setDisplayModes(new_modes)) {
system_settings_touched = true;
DD_LOG(error) << error_log;
return false;
}

guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
// It is possible that the display modes will not actually change even though the "current != new" condition is true.
// This is because of some additional internal checks that determine whether the change is actually needed.
// Therefore we should check the current display modes after the fact!
if (current_display_modes != m_dd_api->getCurrentDisplayModes(win_utils::flattenTopology(new_state.m_modified.m_topology))) {
system_settings_touched = true;
guard_fn = win_utils::modeGuardFn(*m_dd_api, current_display_modes);
}
}

return true;
Expand DownExpand Up@@ -307,7 +324,7 @@ namespace display_device {
}

[[nodiscard]] bool
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state) {
SettingsManager::prepareHdrStates(const SingleDisplayConfiguration &config, const std::string &device_to_configure, const std::set<std::string> &additional_devices_to_configure, DdGuardFn &guard_fn, SingleDisplayConfigState &new_state, bool &system_settings_touched) {
const auto &cached_state { m_persistence_state->getState() };
const auto cached_hdr_states { cached_state ? cached_state->m_modified.m_original_hdr_states : HdrStateMap {} };
const bool change_required { config.m_hdr_state };
Expand All@@ -324,6 +341,8 @@ namespace display_device {

const auto try_change { [&](const HdrStateMap &new_states, const auto info_preamble, const auto error_log) {
if (current_hdr_states != new_states) {
system_settings_touched = true;

DD_LOG(info) << info_preamble << toJson(new_states);
if (!m_dd_api->setHdrStates(new_states)) {
DD_LOG(error) << error_log;
Expand Down
Loading