refactor: clean up windows code
This commit is contained in:
@@ -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,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"";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user