refactor: clean up windows code

This commit is contained in:
edde746
2026-03-10 22:24:14 +01:00
parent bfc7be2b80
commit 7543a8c55b
6 changed files with 13 additions and 180 deletions
+6 -39
View File
@@ -2,20 +2,10 @@
#include <dwmapi.h>
#include "mpv_core.h"
#include "utils.h"
namespace mpv {
MpvContainer* MpvContainer::GetInstance() { return instance_.get(); }
MpvContainer::~MpvContainer() {
if (taskbar_) {
taskbar_->Release();
taskbar_ = nullptr;
}
}
HWND MpvContainer::Create() {
auto window_class = WNDCLASSEX{};
::SecureZeroMemory(&window_class, sizeof(window_class));
@@ -59,15 +49,6 @@ HWND MpvContainer::Get(HWND flutter_window) {
::SetWindowLongPtr(handle_, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(flutter_window));
// 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);
@@ -83,12 +64,13 @@ LRESULT CALLBACK MpvContainer::WindowProc(HWND const window,
::PostQuitMessage(0);
return 0;
}
case WM_MOUSEMOVE: {
case WM_MOUSEMOVE:
case WM_SIZE:
case WM_MOVE:
case WM_MOVING:
case WM_ACTIVATE:
case WM_WINDOWPOSCHANGED: {
// Redirect focus to Flutter window.
auto* core = MpvCore::GetInstance();
if (core) {
core->SetHitTestBehavior(0);
}
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
::SetForegroundWindow(reinterpret_cast<HWND>(user_data));
@@ -99,21 +81,6 @@ LRESULT CALLBACK MpvContainer::WindowProc(HWND const window,
// Prevent erasing to avoid flicker.
return 1;
}
case WM_SIZE:
case WM_MOVE:
case WM_MOVING:
case WM_ACTIVATE:
case WM_WINDOWPOSCHANGED: {
auto* core = MpvCore::GetInstance();
if (core) {
core->SetHitTestBehavior(0);
}
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
::SetForegroundWindow(reinterpret_cast<HWND>(user_data));
}
break;
}
default:
break;
}
+1 -6
View File
@@ -1,16 +1,12 @@
#ifndef MPV_CONTAINER_H_
#define MPV_CONTAINER_H_
#include <ShObjIdl.h>
#include <Windows.h>
#include <memory>
namespace mpv {
// Forward declaration
class MpvCore;
// Container window that holds the mpv video window behind Flutter.
// This is a singleton that creates a hidden window with no taskbar entry.
class MpvContainer {
@@ -18,7 +14,7 @@ class MpvContainer {
static MpvContainer* GetInstance();
MpvContainer() = default;
~MpvContainer();
~MpvContainer() = default;
// Creates the container window.
HWND Create();
@@ -34,7 +30,6 @@ 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"";
+2 -32
View File
@@ -13,13 +13,8 @@ void MpvCore::SetInstance(std::unique_ptr<MpvCore> instance) {
instance_ = std::move(instance);
}
std::optional<int32_t> MpvCore::GetProcId() { return proc_id_; }
void MpvCore::SetProcId(std::optional<int32_t> proc_id) { proc_id_ = proc_id; }
MpvCore::MpvCore(HWND flutter_window, HWND flutter_child_window)
: flutter_window_(flutter_window),
flutter_child_window_(flutter_child_window) {}
MpvCore::MpvCore(HWND flutter_window)
: flutter_window_(flutter_window) {}
MpvCore::~MpvCore() {
// Close all mpv views.
@@ -71,24 +66,6 @@ void MpvCore::DisposeMpvView(HWND mpv_hwnd) {
mpv_views_.erase(mpv_hwnd);
}
void MpvCore::SetHitTestBehavior(int32_t hittest_behavior) {
LONG ex_style = ::GetWindowLong(flutter_window_, GWL_EXSTYLE);
if (hittest_behavior) {
ex_style |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
} else {
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) {
visible_ = visible;
if (container_) {
@@ -173,8 +150,6 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
}
break;
}
case WM_MOVE:
case WM_MOVING:
case WM_WINDOWPOSCHANGED: {
RECT window_rect;
::GetWindowRect(flutter_window_, &window_rect);
@@ -209,10 +184,6 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
return std::nullopt;
}
void MpvCore::RedrawMpvViews() {
::RedrawWindow(container_, 0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN);
}
RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
// Expand client area to prevent transparent gaps.
@@ -232,6 +203,5 @@ RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right,
}
std::unique_ptr<MpvCore> MpvCore::instance_ = nullptr;
std::optional<int32_t> MpvCore::proc_id_ = std::nullopt;
} // namespace mpv
+1 -9
View File
@@ -19,10 +19,8 @@ class MpvCore {
static MpvCore* GetInstance();
static void SetInstance(std::unique_ptr<MpvCore> instance);
static std::optional<int32_t> GetProcId();
static void SetProcId(std::optional<int32_t> proc_id);
MpvCore(HWND flutter_window, HWND flutter_child_window);
explicit MpvCore(HWND flutter_window);
~MpvCore();
// Initializes transparency on the Flutter window.
@@ -37,9 +35,6 @@ class MpvCore {
// Disposes the mpv view.
void DisposeMpvView(HWND mpv_hwnd);
// Sets hit test behavior for mouse passthrough.
void SetHitTestBehavior(int32_t hittest_behavior);
// Shows or hides the mpv view.
void SetVisible(bool visible);
@@ -48,11 +43,9 @@ class MpvCore {
LPARAM lparam);
private:
void RedrawMpvViews();
RECT GetGlobalRect(int32_t left, int32_t top, int32_t right, int32_t bottom);
HWND flutter_window_ = nullptr;
HWND flutter_child_window_ = nullptr;
HWND container_ = nullptr;
double device_pixel_ratio_ = 1.0;
std::map<HWND, RECT> mpv_views_;
@@ -62,7 +55,6 @@ class MpvCore {
bool composition_enabled_ = false;
static std::unique_ptr<MpvCore> instance_;
static std::optional<int32_t> proc_id_;
};
} // namespace mpv
+2 -55
View File
@@ -1,7 +1,6 @@
#include "mpv_player.h"
#include <cstring>
#include <fstream>
#include <simdutf.h>
// Sanitize a C string that may contain invalid UTF-8 sequences.
@@ -44,16 +43,6 @@ static std::string SanitizeUtf8(const char* input) {
return result;
}
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 {
MpvPlayer::MpvPlayer() {}
@@ -61,56 +50,34 @@ MpvPlayer::MpvPlayer() {}
MpvPlayer::~MpvPlayer() { Dispose(); }
bool MpvPlayer::Initialize(HWND container, HWND flutter_window) {
LogToFile("MpvPlayer::Initialize called");
if (mpv_) {
LogToFile("MpvPlayer::Initialize - already initialized");
return true; // Already initialized.
}
container_ = container;
flutter_window_ = flutter_window;
char msg[256];
snprintf(msg, sizeof(msg), "MpvPlayer::Initialize - container: %p", container);
LogToFile(msg);
// Create mpv instance.
LogToFile("MpvPlayer::Initialize - calling mpv_create()");
mpv_ = mpv_create();
if (!mpv_) {
LogToFile("MPV: mpv_create() failed");
return false;
}
LogToFile("MpvPlayer::Initialize - mpv_create() succeeded");
// Create a child window for mpv to render into.
LogToFile("MpvPlayer::Initialize - creating child window");
hwnd_ = ::CreateWindowW(L"STATIC", L"", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100,
container, nullptr, GetModuleHandle(nullptr),
nullptr);
if (!hwnd_) {
DWORD error = GetLastError();
snprintf(msg, sizeof(msg), "MPV: CreateWindowW failed with error %lu", error);
LogToFile(msg);
mpv_destroy(mpv_);
mpv_ = nullptr;
return false;
}
snprintf(msg, sizeof(msg), "MpvPlayer::Initialize - child window created: %p", hwnd_);
LogToFile(msg);
// Set the wid option to embed mpv in our window.
int64_t wid = reinterpret_cast<int64_t>(hwnd_);
int err = mpv_set_option(mpv_, "wid", MPV_FORMAT_INT64, &wid);
if (err < 0) {
snprintf(msg, sizeof(msg), "MPV: Failed to set wid option: %d %s", err, mpv_error_string(err));
LogToFile(msg);
} else {
LogToFile("MpvPlayer::Initialize - wid option set successfully");
}
mpv_set_option(mpv_, "wid", MPV_FORMAT_INT64, &wid);
// Configure mpv for embedded playback.
LogToFile("MpvPlayer::Initialize - setting mpv options");
// Use gpu-next + Vulkan to avoid D3D11 driver crashes (GH-653)
mpv_set_option_string(mpv_, "vo", "gpu-next");
mpv_set_option_string(mpv_, "gpu-api", "vulkan");
@@ -132,12 +99,8 @@ bool MpvPlayer::Initialize(HWND container, HWND flutter_window) {
mpv_request_log_messages(mpv_, "warn");
// Initialize mpv.
LogToFile("MpvPlayer::Initialize - calling mpv_initialize()");
err = mpv_initialize(mpv_);
int err = mpv_initialize(mpv_);
if (err < 0) {
snprintf(msg, sizeof(msg), "MPV: mpv_initialize() failed with error %d: %s",
err, mpv_error_string(err));
LogToFile(msg);
::DestroyWindow(hwnd_);
hwnd_ = nullptr;
mpv_destroy(mpv_);
@@ -145,14 +108,11 @@ bool MpvPlayer::Initialize(HWND container, HWND flutter_window) {
return false;
}
LogToFile("MPV: Initialization successful");
// Observe video-params/sig-peak for HDR detection
mpv_observe_property(mpv_, 0, "video-params/sig-peak", MPV_FORMAT_DOUBLE);
// Start event loop.
StartEventLoop();
LogToFile("MpvPlayer::Initialize - event loop started");
return true;
}
@@ -523,9 +483,6 @@ void MpvPlayer::SendEvent(const std::string& name,
void MpvPlayer::SetHDREnabled(bool enabled) {
hdr_enabled_ = enabled;
char msg[128];
snprintf(msg, sizeof(msg), "[MpvPlayer] HDR enabled: %s", enabled ? "true" : "false");
LogToFile(msg);
if (mpv_) {
mpv_set_property_string(mpv_, "target-colorspace-hint", enabled ? "yes" : "no");
@@ -535,16 +492,6 @@ void MpvPlayer::SetHDREnabled(bool enabled) {
}
void MpvPlayer::UpdateHDRMode(double sigPeak) {
bool isHDRContent = sigPeak > 1.0;
char msg[256];
snprintf(msg, sizeof(msg),
"[MpvPlayer] HDR mode update (hdrEnabled: %s, sigPeak: %.2f, isHDR: %s)",
hdr_enabled_ ? "true" : "false",
sigPeak,
isHDRContent ? "true" : "false");
LogToFile(msg);
// On Windows, mpv handles HDR passthrough automatically when:
// - target-colorspace-hint=yes
// - Windows HDR is enabled in Display Settings
+1 -39
View File
@@ -1,27 +1,13 @@
#include "mpv_plugin.h"
#include <fstream>
#include "mpv_container.h"
#include "mpv_core.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");
}
void MpvPlayerPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
LogToFile("MpvPlayerPlugin: RegisterWithRegistrar called");
mpv::MpvPlayerPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
LogToFile("MpvPlayerPlugin: RegisterWithRegistrar completed");
}
namespace mpv {
@@ -34,22 +20,14 @@ void MpvPlayerPlugin::RegisterWithRegistrar(
MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar)
: registrar_(registrar) {
LogToFile("MpvPlayerPlugin: Constructor called");
// Create method channel.
method_channel_ =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "com.plezy/mpv_player",
&flutter::StandardMethodCodec::GetInstance());
LogToFile("MpvPlayerPlugin: Method channel created");
method_channel_->SetMethodCallHandler(
[this](const auto& call, auto result) {
char msg[256];
snprintf(msg, sizeof(msg), "MpvPlayerPlugin: Method call received: %s",
call.method_name().c_str());
LogToFile(msg);
HandleMethodCall(call, std::move(result));
});
@@ -100,8 +78,6 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& method = method_call.method_name();
if (method == "initialize") {
LogToFile("MPV Plugin: initialize called");
// Set up MpvCore for z-order management.
if (proc_id_) {
registrar_->UnregisterTopLevelWindowProcDelegate(proc_id_.value());
@@ -109,15 +85,9 @@ void MpvPlayerPlugin::HandleMethodCall(
}
HWND flutter_window = GetWindow();
HWND child_window = GetChildWindow();
char msg[256];
snprintf(msg, sizeof(msg), "MPV Plugin: Flutter window: %p, Child window: %p",
flutter_window, child_window);
LogToFile(msg);
MpvCore::SetInstance(
std::make_unique<MpvCore>(flutter_window, child_window));
std::make_unique<MpvCore>(flutter_window));
proc_id_ = registrar_->RegisterTopLevelWindowProcDelegate(
[](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
@@ -128,27 +98,20 @@ void MpvPlayerPlugin::HandleMethodCall(
return std::optional<HRESULT>(std::nullopt);
});
LogToFile("MPV Plugin: Calling EnsureInitialized");
MpvCore::GetInstance()->EnsureInitialized();
// Create player - use the container from MpvCore which was set up by EnsureInitialized
player_ = std::make_unique<MpvPlayer>();
HWND container = MpvContainer::GetInstance()->handle();
snprintf(msg, sizeof(msg), "MPV Plugin: Container handle: %p", container);
LogToFile(msg);
if (!container) {
LogToFile("MPV Plugin: ERROR - container is null");
result->Error("INIT_FAILED", "Failed to create container window");
return;
}
LogToFile("MPV Plugin: Initializing player");
bool success = player_->Initialize(container, flutter_window);
if (success) {
LogToFile("MPV Plugin: Player initialized successfully");
// Set up event callback.
player_->SetEventCallback([this](const flutter::EncodableValue& event) {
SendEvent(event);
@@ -162,7 +125,6 @@ void MpvPlayerPlugin::HandleMethodCall(
MpvCore::GetInstance()->SetVisible(false);
result->Success(flutter::EncodableValue(true));
} else {
OutputDebugStringA("MPV Plugin: Player initialization FAILED\n");
player_.reset(); // Clear the player so we don't have a half-initialized state
result->Error("INIT_FAILED", "Failed to initialize MPV player");
}