fix: windows stretch issue
This commit is contained in:
@@ -1,33 +1,25 @@
|
||||
#include "mpv_container.h"
|
||||
|
||||
#include <ShObjIdl.h>
|
||||
#include <dwmapi.h>
|
||||
#include <fstream>
|
||||
|
||||
#include "mpv_core.h"
|
||||
#include "utils.h"
|
||||
|
||||
static void LogToFile(const char* message) {
|
||||
std::ofstream log("C:\\Users\\admin\\mpv_debug.log", std::ios::app);
|
||||
if (log.is_open()) {
|
||||
log << message << std::endl;
|
||||
log.close();
|
||||
}
|
||||
OutputDebugStringA(message);
|
||||
OutputDebugStringA("\n");
|
||||
}
|
||||
|
||||
namespace mpv {
|
||||
|
||||
MpvContainer* MpvContainer::GetInstance() { return instance_.get(); }
|
||||
|
||||
HWND MpvContainer::Create() {
|
||||
LogToFile("MpvContainer::Create called");
|
||||
MpvContainer::~MpvContainer() {
|
||||
if (taskbar_) {
|
||||
taskbar_->Release();
|
||||
taskbar_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HWND MpvContainer::Create() {
|
||||
auto window_class = WNDCLASSEX{};
|
||||
::SecureZeroMemory(&window_class, sizeof(window_class));
|
||||
window_class.cbSize = sizeof(window_class);
|
||||
// Don't use CS_DROPSHADOW, and avoid redraw styles that might cause issues
|
||||
window_class.style = 0;
|
||||
window_class.lpfnWndProc = WindowProc;
|
||||
window_class.hInstance = GetModuleHandle(nullptr);
|
||||
@@ -35,10 +27,7 @@ HWND MpvContainer::Create() {
|
||||
window_class.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
|
||||
window_class.hbrBackground = ::CreateSolidBrush(RGB(0, 0, 0));
|
||||
|
||||
ATOM atom = ::RegisterClassExW(&window_class);
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Create - RegisterClassExW returned: %d", atom);
|
||||
LogToFile(msg);
|
||||
::RegisterClassExW(&window_class);
|
||||
|
||||
// Use WS_POPUP for a borderless window without title bar.
|
||||
// Use WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP to prevent shadow and DWM effects.
|
||||
@@ -48,15 +37,6 @@ HWND MpvContainer::Create() {
|
||||
0, 0, 100, 100, nullptr, nullptr,
|
||||
GetModuleHandle(nullptr), nullptr);
|
||||
|
||||
if (!handle_) {
|
||||
DWORD error = GetLastError();
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Create - CreateWindow failed with error %lu", error);
|
||||
LogToFile(msg);
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Create - handle_: %p", handle_);
|
||||
LogToFile(msg);
|
||||
}
|
||||
|
||||
// Disable DWM animations on the container.
|
||||
auto disable_window_transitions = TRUE;
|
||||
DwmSetWindowAttribute(handle_, DWMWA_TRANSITIONS_FORCEDISABLED,
|
||||
@@ -67,42 +47,30 @@ HWND MpvContainer::Create() {
|
||||
}
|
||||
|
||||
HWND MpvContainer::Get(HWND flutter_window) {
|
||||
LogToFile("MpvContainer::Get called");
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Get - flutter_window: %p, handle_: %p", flutter_window, handle_);
|
||||
LogToFile(msg);
|
||||
|
||||
if (!handle_) {
|
||||
LogToFile("MpvContainer::Get - handle_ is null, calling Create()");
|
||||
Create();
|
||||
}
|
||||
|
||||
RECT window_rect;
|
||||
::GetWindowRect(flutter_window, &window_rect);
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Get - window_rect: %ld,%ld,%ld,%ld",
|
||||
window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||
LogToFile(msg);
|
||||
|
||||
::SetWindowPos(handle_, flutter_window, window_rect.left, window_rect.top,
|
||||
window_rect.right - window_rect.left,
|
||||
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
|
||||
::SetWindowLongPtr(handle_, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(flutter_window));
|
||||
|
||||
// Remove taskbar entry using ITaskbarList3.
|
||||
ITaskbarList3* taskbar = nullptr;
|
||||
HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&taskbar));
|
||||
if (SUCCEEDED(hr) && taskbar) {
|
||||
taskbar->DeleteTab(handle_);
|
||||
taskbar->Release();
|
||||
// Remove taskbar entry using cached ITaskbarList3.
|
||||
if (!taskbar_) {
|
||||
::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&taskbar_));
|
||||
}
|
||||
if (taskbar_) {
|
||||
taskbar_->DeleteTab(handle_);
|
||||
}
|
||||
|
||||
::ShowWindow(handle_, SW_SHOWNOACTIVATE);
|
||||
::SetFocus(flutter_window);
|
||||
|
||||
snprintf(msg, sizeof(msg), "MpvContainer::Get - returning handle_: %p", handle_);
|
||||
LogToFile(msg);
|
||||
return handle_;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef MPV_CONTAINER_H_
|
||||
#define MPV_CONTAINER_H_
|
||||
|
||||
#include <ShObjIdl.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -17,6 +18,7 @@ class MpvContainer {
|
||||
static MpvContainer* GetInstance();
|
||||
|
||||
MpvContainer() = default;
|
||||
~MpvContainer();
|
||||
|
||||
// Creates the container window.
|
||||
HWND Create();
|
||||
@@ -32,6 +34,7 @@ class MpvContainer {
|
||||
LPARAM lparam) noexcept;
|
||||
|
||||
HWND handle_ = nullptr;
|
||||
ITaskbarList3* taskbar_ = nullptr;
|
||||
|
||||
static constexpr wchar_t kClassName[] = L"MPV_CONTAINER";
|
||||
static constexpr wchar_t kWindowName[] = L"";
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
#include "mpv_core.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <dwmapi.h>
|
||||
|
||||
#include "mpv_container.h"
|
||||
#include "utils.h"
|
||||
|
||||
static void LogToFile(const char* message) {
|
||||
std::ofstream log("C:\\Users\\admin\\mpv_debug.log", std::ios::app);
|
||||
if (log.is_open()) {
|
||||
log << message << std::endl;
|
||||
log.close();
|
||||
}
|
||||
OutputDebugStringA(message);
|
||||
OutputDebugStringA("\n");
|
||||
}
|
||||
|
||||
namespace mpv {
|
||||
|
||||
MpvCore* MpvCore::GetInstance() { return instance_.get(); }
|
||||
@@ -40,20 +30,8 @@ MpvCore::~MpvCore() {
|
||||
}
|
||||
|
||||
void MpvCore::EnsureInitialized() {
|
||||
LogToFile("MpvCore::EnsureInitialized called");
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "MpvCore::EnsureInitialized - flutter_window_: %p", flutter_window_);
|
||||
LogToFile(msg);
|
||||
|
||||
// Enable per-pixel transparency on Flutter window.
|
||||
LogToFile("MpvCore::EnsureInitialized - calling SetWindowComposition");
|
||||
SetWindowComposition(flutter_window_, 6, 0);
|
||||
|
||||
LogToFile("MpvCore::EnsureInitialized - getting container");
|
||||
// Get container - composition will be enabled in SetVisible() to batch DwmFlush calls
|
||||
container_ = MpvContainer::GetInstance()->Get(flutter_window_);
|
||||
|
||||
snprintf(msg, sizeof(msg), "MpvCore::EnsureInitialized - container: %p", container_);
|
||||
LogToFile(msg);
|
||||
}
|
||||
|
||||
void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect,
|
||||
@@ -101,23 +79,34 @@ void MpvCore::SetHitTestBehavior(int32_t hittest_behavior) {
|
||||
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
|
||||
}
|
||||
::SetWindowLong(flutter_window_, GWL_EXSTYLE, ex_style);
|
||||
|
||||
// Force Windows to recalculate window frame after extended style changes.
|
||||
// This ensures the render surface dimensions match the new window state.
|
||||
::SetWindowPos(flutter_window_, nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
// Sync with DWM to ensure render target is updated
|
||||
::DwmFlush();
|
||||
}
|
||||
|
||||
void MpvCore::SetVisible(bool visible) {
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "MpvCore::SetVisible - visible: %d, container_: %p", visible, container_);
|
||||
LogToFile(msg);
|
||||
|
||||
visible_ = visible;
|
||||
if (container_) {
|
||||
if (visible) {
|
||||
SetWindowComposition(flutter_window_, 6, 0);
|
||||
// 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_, 6, 0);
|
||||
composition_enabled_ = true;
|
||||
}
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
LogToFile("MpvCore::SetVisible - showed container, set composition to 6");
|
||||
::DwmFlush(); // Single sync with DWM after all operations
|
||||
} else {
|
||||
SetWindowComposition(flutter_window_, 0, 0);
|
||||
composition_enabled_ = false;
|
||||
::ShowWindow(container_, SW_HIDE);
|
||||
LogToFile("MpvCore::SetVisible - hid container, set composition to 0");
|
||||
::DwmFlush(); // Sync with DWM after hiding
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,68 +124,55 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
|
||||
break;
|
||||
}
|
||||
case WM_SIZE: {
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "WM_SIZE - wparam: %llu, last: %llu, visible_: %d, was_hidden: %d",
|
||||
(unsigned long long)wparam, (unsigned long long)last_wm_size_wparam_,
|
||||
visible_, was_window_hidden_due_to_minimize_);
|
||||
LogToFile(msg);
|
||||
|
||||
// Handle Windows's minimize & maximize animations properly.
|
||||
// During these transitions, we hide the container and make Flutter opaque,
|
||||
// then restore after the animation completes.
|
||||
// then restore after the animation completes using a Windows timer.
|
||||
if (wparam != SIZE_RESTORED || last_wm_size_wparam_ == SIZE_MINIMIZED ||
|
||||
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);
|
||||
LogToFile("WM_SIZE - hiding container, starting delay thread");
|
||||
last_thread_time_ =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
std::thread(
|
||||
[this](uint64_t time) {
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(kPositionAndShowDelay));
|
||||
::DwmFlush(); // Single sync after hiding
|
||||
|
||||
// Check if this thread is still the latest (another WM_SIZE may have come in)
|
||||
if (time != last_thread_time_) {
|
||||
LogToFile("WM_SIZE thread - superseded by newer thread, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
char msg2[256];
|
||||
snprintf(msg2, sizeof(msg2), "WM_SIZE thread - after delay, visible_: %d",
|
||||
visible_);
|
||||
LogToFile(msg2);
|
||||
|
||||
// Update container position to match current Flutter window bounds
|
||||
RECT window_rect;
|
||||
::GetWindowRect(flutter_window_, &window_rect);
|
||||
snprintf(msg2, sizeof(msg2), "WM_SIZE thread - flutter rect: %ld,%ld,%ld,%ld",
|
||||
window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||
LogToFile(msg2);
|
||||
|
||||
::SetWindowPos(container_, flutter_window_, window_rect.left,
|
||||
window_rect.top, window_rect.right - window_rect.left,
|
||||
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
|
||||
LogToFile("WM_SIZE thread - updated container position");
|
||||
|
||||
// Always restore transparency if video is visible
|
||||
if (visible_) {
|
||||
SetWindowComposition(flutter_window_, 6, 0);
|
||||
LogToFile("WM_SIZE thread - restored composition to 6");
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
LogToFile("WM_SIZE thread - showed container");
|
||||
}
|
||||
},
|
||||
last_thread_time_)
|
||||
.detach();
|
||||
// Cancel any pending timer and set a new one.
|
||||
::KillTimer(flutter_window_, kCompositionRestoreTimerId);
|
||||
::SetTimer(flutter_window_, kCompositionRestoreTimerId, kPositionAndShowDelay, nullptr);
|
||||
}
|
||||
last_wm_size_wparam_ = wparam;
|
||||
break;
|
||||
}
|
||||
case WM_TIMER: {
|
||||
if (wparam == kCompositionRestoreTimerId) {
|
||||
::KillTimer(flutter_window_, kCompositionRestoreTimerId);
|
||||
|
||||
// Update container position to match current Flutter window bounds
|
||||
RECT window_rect;
|
||||
::GetWindowRect(flutter_window_, &window_rect);
|
||||
::SetWindowPos(container_, flutter_window_, window_rect.left,
|
||||
window_rect.top, window_rect.right - window_rect.left,
|
||||
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
|
||||
|
||||
// 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_, 6, 0);
|
||||
composition_enabled_ = true;
|
||||
}
|
||||
::ShowWindow(container_, SW_SHOWNOACTIVATE);
|
||||
::DwmFlush(); // Single sync after all operations
|
||||
|
||||
// Force a redraw to ensure Flutter's render surface is correctly sized
|
||||
::RedrawWindow(flutter_window_, nullptr, nullptr,
|
||||
RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_MOVE:
|
||||
case WM_MOVING:
|
||||
case WM_WINDOWPOSCHANGED: {
|
||||
@@ -211,7 +187,9 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
|
||||
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
|
||||
was_window_hidden_due_to_minimize_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
namespace mpv {
|
||||
|
||||
@@ -17,6 +15,7 @@ namespace mpv {
|
||||
class MpvCore {
|
||||
public:
|
||||
static constexpr auto kPositionAndShowDelay = 300;
|
||||
static constexpr UINT_PTR kCompositionRestoreTimerId = 1001;
|
||||
|
||||
static MpvCore* GetInstance();
|
||||
static void SetInstance(std::unique_ptr<MpvCore> instance);
|
||||
@@ -57,10 +56,10 @@ class MpvCore {
|
||||
HWND container_ = nullptr;
|
||||
double device_pixel_ratio_ = 1.0;
|
||||
std::map<HWND, RECT> mpv_views_;
|
||||
uint64_t last_thread_time_ = 0;
|
||||
WPARAM last_wm_size_wparam_ = SIZE_RESTORED;
|
||||
bool was_window_hidden_due_to_minimize_ = false;
|
||||
bool visible_ = true;
|
||||
bool composition_enabled_ = false;
|
||||
|
||||
static std::unique_ptr<MpvCore> instance_;
|
||||
static std::optional<int32_t> proc_id_;
|
||||
|
||||
Reference in New Issue
Block a user