Files
plezy/windows/runner/mpv/mpv_core.cpp
T

188 lines
6.3 KiB
C++

#include "mpv_core.h"
#include <dwmapi.h>
#include "mpv_container.h"
#include "utils.h"
namespace mpv {
MpvCore* MpvCore::GetInstance() { return instance_.get(); }
void MpvCore::SetInstance(std::unique_ptr<MpvCore> instance) { instance_ = std::move(instance); }
MpvCore::MpvCore(HWND flutter_window) : flutter_window_(flutter_window) {}
MpvCore::~MpvCore() {
// Close all mpv views.
for (const auto& [mpv_view, rect] : mpv_views_) {
::SendMessage(mpv_view, WM_CLOSE, 0, 0);
}
mpv_views_.clear();
}
void MpvCore::EnsureInitialized() {
// Get container - composition will be enabled in SetVisible() to batch DwmFlush calls
container_ = MpvContainer::GetInstance()->Get(flutter_window_);
}
void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect, double device_pixel_ratio) {
::SetParent(mpv_hwnd, container_);
::ShowWindow(mpv_hwnd, SW_SHOW);
// Remove window decorations.
auto style = ::GetWindowLongPtr(mpv_hwnd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
::SetWindowLongPtr(mpv_hwnd, GWL_STYLE, style);
device_pixel_ratio_ = device_pixel_ratio;
mpv_views_[mpv_hwnd] = rect;
// Position the mpv view behind the Flutter window.
auto global_rect = GetGlobalRect(rect.left, rect.top, rect.right, rect.bottom);
::SetWindowPos(
mpv_hwnd, flutter_window_, global_rect.left, global_rect.top, global_rect.right - global_rect.left,
global_rect.bottom - global_rect.top, SWP_NOACTIVATE);
}
void MpvCore::ResizeMpvView(HWND mpv_hwnd, RECT rect) {
mpv_views_[mpv_hwnd] = rect;
auto global_rect = GetGlobalRect(rect.left, rect.top, rect.right, rect.bottom);
// Use MoveWindow to trigger redraw.
::MoveWindow(
mpv_hwnd, global_rect.left, global_rect.top, global_rect.right - global_rect.left,
global_rect.bottom - global_rect.top, TRUE);
}
void MpvCore::DisposeMpvView(HWND mpv_hwnd) {
::SendMessage(mpv_hwnd, WM_CLOSE, 0, 0);
mpv_views_.erase(mpv_hwnd);
}
void MpvCore::SetVisible(bool visible) {
visible_ = visible;
if (container_) {
if (visible) {
EnableComposition();
} else {
DisableComposition();
}
}
}
std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
switch (message) {
case WM_ACTIVATE: {
RECT window_rect;
::GetWindowRect(flutter_window_, &window_rect);
// Position container behind Flutter window.
::SetWindowPos(
container_, flutter_window_, window_rect.left, window_rect.top, window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
break;
}
case WM_SIZE: {
// Handle Windows's minimize & maximize animations properly.
// During these transitions, we hide the container and make Flutter opaque,
// 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;
DisableComposition();
// 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_) {
EnableComposition();
// 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_WINDOWPOSCHANGED: {
RECT window_rect;
::GetWindowRect(flutter_window_, &window_rect);
if (window_rect.right - window_rect.left > 0 && window_rect.bottom - window_rect.top > 0) {
::SetWindowPos(
container_, flutter_window_, window_rect.left, window_rect.top, window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
// Window is minimized (negative coordinates).
if (window_rect.left < 0 && window_rect.top < 0 && window_rect.right < 0 && window_rect.bottom < 0) {
DisableComposition();
was_window_hidden_due_to_minimize_ = true;
}
}
break;
}
case WM_CLOSE: {
::SendMessage(container_, WM_CLOSE, 0, 0);
for (const auto& [mpv_view, rect] : mpv_views_) {
::SendMessage(mpv_view, WM_CLOSE, 0, 0);
}
mpv_views_.clear();
break;
}
default:
break;
}
return std::nullopt;
}
RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
// Expand client area to prevent transparent gaps.
left -= static_cast<int32_t>(ceil(device_pixel_ratio_));
top -= static_cast<int32_t>(ceil(device_pixel_ratio_));
right += static_cast<int32_t>(ceil(device_pixel_ratio_));
bottom += static_cast<int32_t>(ceil(device_pixel_ratio_));
RECT window_rect;
::GetClientRect(flutter_window_, &window_rect);
RECT rect;
rect.left = window_rect.left + left;
rect.top = window_rect.top + top;
rect.right = window_rect.left + right;
rect.bottom = window_rect.top + bottom;
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();
}
std::unique_ptr<MpvCore> MpvCore::instance_ = nullptr;
} // namespace mpv