fix: Windows native code bugs and dedup
- fix unsigned underflow in Utf8FromUtf16 when WideCharToMultiByte fails - fix inverted freopen_s logic in console attach - remove misleading WS_EX_APPWINDOW from GWL_STYLE mask - close mutex handle on normal exit - extract SetHDRStateForTarget, EnableComposition/DisableComposition, DisplayModeToMap helpers - cache GetWindowsVersion result
This commit is contained in:
@@ -57,5 +57,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
}
|
||||
|
||||
::CoUninitialize();
|
||||
CloseHandle(mutex);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -324,26 +324,7 @@ bool DisplayModeManager::SetHDREnabled(HWND window, bool enabled) {
|
||||
EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &pre_toggle_dm);
|
||||
|
||||
// Toggle HDR.
|
||||
LONG result;
|
||||
if (IsWin11_24H2OrNewer()) {
|
||||
DISPLAYCONFIG_SET_HDR_STATE state = {};
|
||||
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableHdr = enabled ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
} else {
|
||||
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE state = {};
|
||||
state.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableAdvancedColor = enabled ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
}
|
||||
|
||||
LONG result = SetHDRStateForTarget(*target_id, enabled);
|
||||
if (result != ERROR_SUCCESS) return false;
|
||||
|
||||
// Restore DEVMODEW after toggle — Windows may have changed the display mode.
|
||||
@@ -378,26 +359,7 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
|
||||
pre_toggle_dm.dmSize = sizeof(pre_toggle_dm);
|
||||
EnumDisplaySettingsW(original_hdr_device_name_.c_str(), ENUM_CURRENT_SETTINGS, &pre_toggle_dm);
|
||||
|
||||
LONG result;
|
||||
if (IsWin11_24H2OrNewer()) {
|
||||
DISPLAYCONFIG_SET_HDR_STATE state = {};
|
||||
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableHdr = original_hdr_enabled_ ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
} else {
|
||||
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE state = {};
|
||||
state.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableAdvancedColor = original_hdr_enabled_ ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
}
|
||||
|
||||
LONG result = SetHDRStateForTarget(*target_id, original_hdr_enabled_);
|
||||
if (result != ERROR_SUCCESS) return false;
|
||||
|
||||
// Restore DEVMODEW after toggle.
|
||||
@@ -414,6 +376,27 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
|
||||
|
||||
// --- Crash recovery (Windows Registry) ---
|
||||
|
||||
LONG DisplayModeManager::SetHDRStateForTarget(const DisplayConfigId& target, bool enabled) {
|
||||
if (IsWin11_24H2OrNewer()) {
|
||||
DISPLAYCONFIG_SET_HDR_STATE state = {};
|
||||
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target.adapter_id;
|
||||
state.header.id = target.id;
|
||||
state.enableHdr = enabled ? TRUE : FALSE;
|
||||
return DisplayConfigSetDeviceInfo(&state.header);
|
||||
} else {
|
||||
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE state = {};
|
||||
state.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target.adapter_id;
|
||||
state.header.id = target.id;
|
||||
state.enableAdvancedColor = enabled ? TRUE : FALSE;
|
||||
return DisplayConfigSetDeviceInfo(&state.header);
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayModeManager::WriteRegistryDWORD(const wchar_t* value_name, DWORD value) {
|
||||
HKEY key;
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr,
|
||||
@@ -552,25 +535,7 @@ bool DisplayModeManager::RecoverIfNeeded(HWND window) {
|
||||
pre_dm.dmSize = sizeof(pre_dm);
|
||||
EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &pre_dm);
|
||||
|
||||
LONG result;
|
||||
if (IsWin11_24H2OrNewer()) {
|
||||
DISPLAYCONFIG_SET_HDR_STATE state = {};
|
||||
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
|
||||
DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableHdr = hdr_was_enabled ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
} else {
|
||||
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE state = {};
|
||||
state.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
|
||||
state.header.size = sizeof(state);
|
||||
state.header.adapterId = target_id->adapter_id;
|
||||
state.header.id = target_id->id;
|
||||
state.enableAdvancedColor = hdr_was_enabled ? TRUE : FALSE;
|
||||
result = DisplayConfigSetDeviceInfo(&state.header);
|
||||
}
|
||||
LONG result = SetHDRStateForTarget(*target_id, hdr_was_enabled != 0);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
recovered = true;
|
||||
|
||||
@@ -116,6 +116,9 @@ class DisplayModeManager {
|
||||
// Check if running on Win11 24H2 or newer.
|
||||
static bool IsWin11_24H2OrNewer();
|
||||
|
||||
// Toggle HDR via DisplayConfig (version-dispatched).
|
||||
static LONG SetHDRStateForTarget(const DisplayConfigId& target, bool enabled);
|
||||
|
||||
// Registry helpers for crash recovery.
|
||||
static bool WriteRegistryDWORD(const wchar_t* value_name, DWORD value);
|
||||
static bool WriteRegistryString(const wchar_t* value_name, const std::wstring& value);
|
||||
|
||||
@@ -36,8 +36,7 @@ void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect,
|
||||
|
||||
// Remove window decorations.
|
||||
auto style = ::GetWindowLongPtr(mpv_hwnd, GWL_STYLE);
|
||||
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
|
||||
WS_EX_APPWINDOW);
|
||||
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
|
||||
::SetWindowLongPtr(mpv_hwnd, GWL_STYLE, style);
|
||||
|
||||
device_pixel_ratio_ = device_pixel_ratio;
|
||||
@@ -70,20 +69,9 @@ void MpvCore::SetVisible(bool visible) {
|
||||
visible_ = visible;
|
||||
if (container_) {
|
||||
if (visible) {
|
||||
// Batch all window operations, single DwmFlush at end
|
||||
::SetWindowPos(flutter_window_, nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
if (!composition_enabled_) {
|
||||
SetWindowComposition(flutter_window_, 2, 0);
|
||||
composition_enabled_ = true;
|
||||
}
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
::DwmFlush(); // Single sync with DWM after all operations
|
||||
EnableComposition();
|
||||
} else {
|
||||
SetWindowComposition(flutter_window_, 0, 0);
|
||||
composition_enabled_ = false;
|
||||
::ShowWindow(container_, SW_HIDE);
|
||||
::DwmFlush(); // Sync with DWM after hiding
|
||||
DisableComposition();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,10 +96,7 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
|
||||
last_wm_size_wparam_ == SIZE_MAXIMIZED ||
|
||||
was_window_hidden_due_to_minimize_) {
|
||||
was_window_hidden_due_to_minimize_ = false;
|
||||
SetWindowComposition(flutter_window_, 0, 0);
|
||||
composition_enabled_ = false;
|
||||
::ShowWindow(container_, SW_HIDE);
|
||||
::DwmFlush(); // Single sync after hiding
|
||||
DisableComposition();
|
||||
|
||||
// Cancel any pending timer and set a new one.
|
||||
::KillTimer(flutter_window_, kCompositionRestoreTimerId);
|
||||
@@ -133,15 +118,7 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
|
||||
|
||||
// Restore transparency if video is visible
|
||||
if (visible_) {
|
||||
// Batch all operations, single DwmFlush at end
|
||||
::SetWindowPos(flutter_window_, nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
if (!composition_enabled_) {
|
||||
SetWindowComposition(flutter_window_, 2, 0);
|
||||
composition_enabled_ = true;
|
||||
}
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
::DwmFlush(); // Single sync after all operations
|
||||
EnableComposition();
|
||||
|
||||
// Force a redraw to ensure Flutter's render surface is correctly sized
|
||||
::RedrawWindow(flutter_window_, nullptr, nullptr,
|
||||
@@ -166,10 +143,7 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
|
||||
// Window is minimized (negative coordinates).
|
||||
if (window_rect.left < 0 && window_rect.top < 0 &&
|
||||
window_rect.right < 0 && window_rect.bottom < 0) {
|
||||
SetWindowComposition(flutter_window_, 0, 0);
|
||||
composition_enabled_ = false;
|
||||
::ShowWindow(container_, SW_HIDE);
|
||||
::DwmFlush(); // Single sync after hiding
|
||||
DisableComposition();
|
||||
was_window_hidden_due_to_minimize_ = true;
|
||||
}
|
||||
}
|
||||
@@ -215,6 +189,24 @@ RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right,
|
||||
return rect;
|
||||
}
|
||||
|
||||
void MpvCore::EnableComposition() {
|
||||
::SetWindowPos(flutter_window_, nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
if (!composition_enabled_) {
|
||||
SetWindowComposition(flutter_window_, 2, 0);
|
||||
composition_enabled_ = true;
|
||||
}
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
::DwmFlush();
|
||||
}
|
||||
|
||||
void MpvCore::DisableComposition() {
|
||||
SetWindowComposition(flutter_window_, 0, 0);
|
||||
composition_enabled_ = false;
|
||||
::ShowWindow(container_, SW_HIDE);
|
||||
::DwmFlush();
|
||||
}
|
||||
|
||||
void MpvCore::SetPowerResumeCallback(PowerResumeCallback callback) {
|
||||
power_resume_cb_ = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ class MpvCore {
|
||||
|
||||
private:
|
||||
RECT GetGlobalRect(int32_t left, int32_t top, int32_t right, int32_t bottom);
|
||||
void EnableComposition();
|
||||
void DisableComposition();
|
||||
|
||||
HWND flutter_window_ = nullptr;
|
||||
HWND container_ = nullptr;
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
#include "mpv_container.h"
|
||||
#include "mpv_core.h"
|
||||
|
||||
static flutter::EncodableMap DisplayModeToMap(const mpv::DisplayMode& mode) {
|
||||
flutter::EncodableMap m;
|
||||
m[flutter::EncodableValue("width")] = flutter::EncodableValue(static_cast<int32_t>(mode.width));
|
||||
m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast<int32_t>(mode.height));
|
||||
m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast<int32_t>(mode.refresh_rate));
|
||||
return m;
|
||||
}
|
||||
|
||||
void MpvPlayerPluginRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar) {
|
||||
mpv::MpvPlayerPlugin::RegisterWithRegistrar(
|
||||
@@ -397,21 +405,13 @@ void MpvPlayerPlugin::HandleMethodCall(
|
||||
auto modes = display_mode_manager_.EnumerateDisplayModes(hwnd);
|
||||
flutter::EncodableList list;
|
||||
for (const auto& mode : modes) {
|
||||
flutter::EncodableMap m;
|
||||
m[flutter::EncodableValue("width")] = flutter::EncodableValue(static_cast<int32_t>(mode.width));
|
||||
m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast<int32_t>(mode.height));
|
||||
m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast<int32_t>(mode.refresh_rate));
|
||||
list.push_back(flutter::EncodableValue(m));
|
||||
list.push_back(flutter::EncodableValue(DisplayModeToMap(mode)));
|
||||
}
|
||||
result->Success(flutter::EncodableValue(list));
|
||||
} else if (method == "getCurrentDisplayMode") {
|
||||
HWND hwnd = GetWindow();
|
||||
auto mode = display_mode_manager_.GetCurrentMode(hwnd);
|
||||
flutter::EncodableMap m;
|
||||
m[flutter::EncodableValue("width")] = flutter::EncodableValue(static_cast<int32_t>(mode.width));
|
||||
m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast<int32_t>(mode.height));
|
||||
m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast<int32_t>(mode.refresh_rate));
|
||||
result->Success(flutter::EncodableValue(m));
|
||||
result->Success(flutter::EncodableValue(DisplayModeToMap(mode)));
|
||||
} else if (method == "setDisplayMode") {
|
||||
const auto* args = method_call.arguments();
|
||||
if (!args || !std::holds_alternative<flutter::EncodableMap>(*args)) {
|
||||
|
||||
@@ -68,20 +68,23 @@ typedef LONG NTSTATUS, *PNTSTATUS;
|
||||
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
|
||||
static RTL_OSVERSIONINFOW GetWindowsVersion() {
|
||||
HMODULE hmodule = ::GetModuleHandleW(L"ntdll.dll");
|
||||
if (hmodule) {
|
||||
RtlGetVersionPtr rtl_get_version_ptr =
|
||||
(RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
|
||||
if (rtl_get_version_ptr != nullptr) {
|
||||
RTL_OSVERSIONINFOW rovi = {0};
|
||||
rovi.dwOSVersionInfoSize = sizeof(rovi);
|
||||
if (STATUS_SUCCESS == rtl_get_version_ptr(&rovi)) {
|
||||
return rovi;
|
||||
static RTL_OSVERSIONINFOW cached = []() {
|
||||
HMODULE hmodule = ::GetModuleHandleW(L"ntdll.dll");
|
||||
if (hmodule) {
|
||||
RtlGetVersionPtr rtl_get_version_ptr =
|
||||
(RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
|
||||
if (rtl_get_version_ptr != nullptr) {
|
||||
RTL_OSVERSIONINFOW rovi = {0};
|
||||
rovi.dwOSVersionInfoSize = sizeof(rovi);
|
||||
if (STATUS_SUCCESS == rtl_get_version_ptr(&rovi)) {
|
||||
return rovi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RTL_OSVERSIONINFOW rovi = {0};
|
||||
return rovi;
|
||||
RTL_OSVERSIONINFOW rovi = {0};
|
||||
return rovi;
|
||||
}();
|
||||
return cached;
|
||||
}
|
||||
|
||||
void SetWindowComposition(HWND window, int32_t accent_state,
|
||||
|
||||
@@ -10,12 +10,8 @@
|
||||
void CreateAndAttachConsole() {
|
||||
if (::AllocConsole()) {
|
||||
FILE *unused;
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
|
||||
_dup2(_fileno(stdout), 1);
|
||||
}
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
|
||||
_dup2(_fileno(stdout), 2);
|
||||
}
|
||||
freopen_s(&unused, "CONOUT$", "w", stdout);
|
||||
freopen_s(&unused, "CONOUT$", "w", stderr);
|
||||
std::ios::sync_with_stdio();
|
||||
FlutterDesktopResyncOutputStreams();
|
||||
}
|
||||
@@ -45,15 +41,15 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) {
|
||||
if (utf16_string == nullptr) {
|
||||
return std::string();
|
||||
}
|
||||
unsigned int target_length = ::WideCharToMultiByte(
|
||||
int raw_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
-1, nullptr, 0, nullptr, nullptr)
|
||||
-1; // remove the trailing null character
|
||||
-1, nullptr, 0, nullptr, nullptr);
|
||||
if (raw_length <= 1) {
|
||||
return std::string();
|
||||
}
|
||||
unsigned int target_length = raw_length - 1; // exclude trailing null
|
||||
int input_length = (int)wcslen(utf16_string);
|
||||
std::string utf8_string;
|
||||
if (target_length == 0 || target_length > utf8_string.max_size()) {
|
||||
return utf8_string;
|
||||
}
|
||||
utf8_string.resize(target_length);
|
||||
int converted_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
|
||||
Reference in New Issue
Block a user