diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index c387d23b..24f4fb89 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -57,5 +57,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, } ::CoUninitialize(); + CloseHandle(mutex); return EXIT_SUCCESS; } diff --git a/windows/runner/mpv/display_mode_manager.cpp b/windows/runner/mpv/display_mode_manager.cpp index f0a3b1d5..4501f3e8 100644 --- a/windows/runner/mpv/display_mode_manager.cpp +++ b/windows/runner/mpv/display_mode_manager.cpp @@ -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_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_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_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_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; diff --git a/windows/runner/mpv/display_mode_manager.h b/windows/runner/mpv/display_mode_manager.h index f32dbb19..620fa433 100644 --- a/windows/runner/mpv/display_mode_manager.h +++ b/windows/runner/mpv/display_mode_manager.h @@ -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); diff --git a/windows/runner/mpv/mpv_core.cpp b/windows/runner/mpv/mpv_core.cpp index 27a634ed..b146048e 100644 --- a/windows/runner/mpv/mpv_core.cpp +++ b/windows/runner/mpv/mpv_core.cpp @@ -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 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 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 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); } diff --git a/windows/runner/mpv/mpv_core.h b/windows/runner/mpv/mpv_core.h index 95c25ce9..06b68198 100644 --- a/windows/runner/mpv/mpv_core.h +++ b/windows/runner/mpv/mpv_core.h @@ -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; diff --git a/windows/runner/mpv/mpv_plugin.cpp b/windows/runner/mpv/mpv_plugin.cpp index 1069862f..2031f306 100644 --- a/windows/runner/mpv/mpv_plugin.cpp +++ b/windows/runner/mpv/mpv_plugin.cpp @@ -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(mode.width)); + m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast(mode.height)); + m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast(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(mode.width)); - m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast(mode.height)); - m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast(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(mode.width)); - m[flutter::EncodableValue("height")] = flutter::EncodableValue(static_cast(mode.height)); - m[flutter::EncodableValue("refreshRate")] = flutter::EncodableValue(static_cast(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(*args)) { diff --git a/windows/runner/mpv/utils.cpp b/windows/runner/mpv/utils.cpp index 4960d9c1..1be4e467 100644 --- a/windows/runner/mpv/utils.cpp +++ b/windows/runner/mpv/utils.cpp @@ -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, diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp index 3a0b4651..be639d6e 100644 --- a/windows/runner/utils.cpp +++ b/windows/runner/utils.cpp @@ -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,