More graceful(?) HDR reset routine
Modified from b6f8573d35/src/platform/windows/display_device/windows_utils.cpp
This commit is contained in:
153
src/platform/windows/utils.cpp
Normal file
153
src/platform/windows/utils.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "utils.h"
|
||||
|
||||
std::string convertUtf8ToCurrentCodepage(const std::string& utf8Str) {
|
||||
if (GetACP() == CP_UTF8) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
// Step 1: Convert UTF-8 to UTF-16
|
||||
int utf16Len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
|
||||
if (utf16Len == 0) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
|
||||
std::wstring utf16Str(utf16Len, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &utf16Str[0], utf16Len);
|
||||
|
||||
// Step 2: Convert UTF-16 to the current Windows codepage
|
||||
int codepageLen = WideCharToMultiByte(GetACP(), 0, utf16Str.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
if (codepageLen == 0) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
|
||||
std::string codepageStr(codepageLen, '\0');
|
||||
WideCharToMultiByte(GetACP(), 0, utf16Str.c_str(), -1, &codepageStr[0], codepageLen, NULL, NULL);
|
||||
|
||||
return codepageStr;
|
||||
}
|
||||
|
||||
// Modified from https://github.com/FrogTheFrog/Sunshine/blob/b6f8573d35eff7c55da6965dfa317dc9722bd4ef/src/platform/windows/display_device/windows_utils.cpp
|
||||
|
||||
std::string
|
||||
get_error_string(LONG error_code) {
|
||||
std::stringstream error;
|
||||
error << "[code: ";
|
||||
switch (error_code) {
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
error << "ERROR_INVALID_PARAMETER";
|
||||
break;
|
||||
case ERROR_NOT_SUPPORTED:
|
||||
error << "ERROR_NOT_SUPPORTED";
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
error << "ERROR_ACCESS_DENIED";
|
||||
break;
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
error << "ERROR_INSUFFICIENT_BUFFER";
|
||||
break;
|
||||
case ERROR_GEN_FAILURE:
|
||||
error << "ERROR_GEN_FAILURE";
|
||||
break;
|
||||
case ERROR_SUCCESS:
|
||||
error << "ERROR_SUCCESS";
|
||||
break;
|
||||
default:
|
||||
error << error_code;
|
||||
break;
|
||||
}
|
||||
error << ", message: " << std::system_category().message(static_cast<int>(error_code)) << "]";
|
||||
return error.str();
|
||||
}
|
||||
|
||||
bool
|
||||
query_display_config(std::vector<DISPLAYCONFIG_PATH_INFO>& paths, std::vector<DISPLAYCONFIG_MODE_INFO>& modes, bool active_only) {
|
||||
LONG result = ERROR_SUCCESS;
|
||||
|
||||
// When we want to enable/disable displays, we need to get all paths as they will not be active.
|
||||
// This will require some additional filtering of duplicate and otherwise useless paths.
|
||||
UINT32 flags = active_only ? QDC_ONLY_ACTIVE_PATHS : QDC_ALL_PATHS;
|
||||
flags |= QDC_VIRTUAL_MODE_AWARE; // supported from W10 onwards
|
||||
|
||||
do {
|
||||
UINT32 path_count { 0 };
|
||||
UINT32 mode_count { 0 };
|
||||
|
||||
result = GetDisplayConfigBufferSizes(flags, &path_count, &mode_count);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
BOOST_LOG(error) << get_error_string(result) << " failed to get display paths and modes!";
|
||||
return false;
|
||||
}
|
||||
|
||||
paths.resize(path_count);
|
||||
modes.resize(mode_count);
|
||||
result = QueryDisplayConfig(flags, &path_count, paths.data(), &mode_count, modes.data(), nullptr);
|
||||
|
||||
// The function may have returned fewer paths/modes than estimated
|
||||
paths.resize(path_count);
|
||||
modes.resize(mode_count);
|
||||
|
||||
// It's possible that between the call to GetDisplayConfigBufferSizes and QueryDisplayConfig
|
||||
// that the display state changed, so loop on the case of ERROR_INSUFFICIENT_BUFFER.
|
||||
} while (result == ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
BOOST_LOG(error) << get_error_string(result) << " failed to query display paths and modes!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_user_session_locked() {
|
||||
LPWSTR buffer { nullptr };
|
||||
const auto cleanup_guard {
|
||||
util::fail_guard([&buffer]() {
|
||||
if (buffer) {
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
DWORD buffer_size_in_bytes { 0 };
|
||||
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTSGetActiveConsoleSessionId(), WTSSessionInfoEx, &buffer, &buffer_size_in_bytes)) {
|
||||
if (buffer_size_in_bytes > 0) {
|
||||
const auto wts_info { reinterpret_cast<const WTSINFOEXW *>(buffer) };
|
||||
if (wts_info && wts_info->Level == 1) {
|
||||
const bool is_locked { wts_info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK };
|
||||
BOOST_LOG(debug) << "is_user_session_locked: " << is_locked;
|
||||
return is_locked;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG(warning) << "Failed to get session info in is_user_session_locked.";
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << get_error_string(GetLastError()) << " failed while calling WTSQuerySessionInformationW!";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
test_no_access_to_ccd_api() {
|
||||
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
|
||||
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
|
||||
if (!query_display_config(paths, modes, true)) {
|
||||
BOOST_LOG(debug) << "test_no_access_to_ccd_api failed in query_display_config.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Here we are supplying the retrieved display data back to SetDisplayConfig (with VALIDATE flag only, so that we make no actual changes).
|
||||
// Unless something is really broken on Windows, this call should never fail under normal circumstances - the configuration is 100% correct, since it was
|
||||
// provided by Windows.
|
||||
const UINT32 flags { SDC_VALIDATE | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_VIRTUAL_MODE_AWARE };
|
||||
const LONG result { SetDisplayConfig(paths.size(), paths.data(), modes.size(), modes.data(), flags) };
|
||||
|
||||
BOOST_LOG(debug) << "test_no_access_to_ccd_api result: " << get_error_string(result);
|
||||
return result == ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
bool
|
||||
is_changing_settings_going_to_fail() {
|
||||
return is_user_session_locked() || test_no_access_to_ccd_api();
|
||||
}
|
||||
@@ -1,29 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <SetupApi.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
std::string convertUtf8ToCurrentCodepage(const std::string& utf8Str) {
|
||||
if (GetACP() == CP_UTF8) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
// Step 1: Convert UTF-8 to UTF-16
|
||||
int utf16Len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
|
||||
if (utf16Len == 0) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
#include "src/utility.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
std::wstring utf16Str(utf16Len, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &utf16Str[0], utf16Len);
|
||||
std::string convertUtf8ToCurrentCodepage(const std::string& utf8Str);
|
||||
|
||||
// Step 2: Convert UTF-16 to the current Windows codepage
|
||||
int codepageLen = WideCharToMultiByte(GetACP(), 0, utf16Str.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
if (codepageLen == 0) {
|
||||
return std::string(utf8Str);
|
||||
}
|
||||
std::string get_error_string(LONG error_code);
|
||||
|
||||
std::string codepageStr(codepageLen, '\0');
|
||||
WideCharToMultiByte(GetACP(), 0, utf16Str.c_str(), -1, &codepageStr[0], codepageLen, NULL, NULL);
|
||||
bool query_display_config(std::vector<DISPLAYCONFIG_PATH_INFO>& paths, std::vector<DISPLAYCONFIG_MODE_INFO>& modes, bool active_only);
|
||||
|
||||
return codepageStr;
|
||||
}
|
||||
bool is_user_session_locked();
|
||||
|
||||
bool test_no_access_to_ccd_api();
|
||||
|
||||
bool is_changing_settings_going_to_fail();
|
||||
|
||||
Reference in New Issue
Block a user