chore: add native formatting checks

This commit is contained in:
edde746
2026-05-01 05:56:49 +02:00
parent 9bd5732f2b
commit 024af35bf5
69 changed files with 8848 additions and 8633 deletions
+45 -66
View File
@@ -30,11 +30,10 @@ static void CALLBACK SaveTimerProc(HWND, UINT, UINT_PTR, DWORD) {
// Write a WINDOWPLACEMENT struct directly to the registry.
static void WriteWindowPlacement(const WINDOWPLACEMENT& wp) {
HKEY hKey;
if (RegCreateKeyExW(HKEY_CURRENT_USER, kWindowPlacementKey, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hKey,
nullptr) == ERROR_SUCCESS) {
RegSetValueExW(hKey, kWindowPlacementValue, 0, REG_BINARY,
reinterpret_cast<const BYTE*>(&wp), sizeof(wp));
if (RegCreateKeyExW(
HKEY_CURRENT_USER, kWindowPlacementKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hKey,
nullptr) == ERROR_SUCCESS) {
RegSetValueExW(hKey, kWindowPlacementValue, 0, REG_BINARY, reinterpret_cast<const BYTE*>(&wp), sizeof(wp));
RegCloseKey(hKey);
}
}
@@ -51,17 +50,15 @@ static void SaveWindowPlacement(HWND hwnd) {
// Returns whether the window should be maximized
static bool LoadWindowPlacement(HWND hwnd) {
HKEY hKey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kWindowPlacementKey, 0, KEY_READ,
&hKey) != ERROR_SUCCESS)
return false;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kWindowPlacementKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return false;
WINDOWPLACEMENT wp{};
wp.length = sizeof(wp);
DWORD size = sizeof(wp);
bool wasMaximized = false;
if (RegQueryValueExW(hKey, kWindowPlacementValue, nullptr, nullptr,
reinterpret_cast<BYTE*>(&wp), &size) == ERROR_SUCCESS &&
if (RegQueryValueExW(hKey, kWindowPlacementValue, nullptr, nullptr, reinterpret_cast<BYTE*>(&wp), &size) ==
ERROR_SUCCESS &&
size == sizeof(wp)) {
// Prevent restoring as minimized
if (wp.showCmd == SW_SHOWMINIMIZED) wp.showCmd = SW_SHOWNORMAL;
@@ -80,8 +77,7 @@ static void DebounceSaveWindowPlacement(HWND hwnd) {
g_saveTimerId = SetTimer(nullptr, 0, 500, SaveTimerProc); // 500ms debounce
}
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::FlutterWindow(const flutter::DartProject& project) : project_(project) {}
FlutterWindow::~FlutterWindow() {}
@@ -94,8 +90,8 @@ bool FlutterWindow::OnCreate() {
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
flutter_controller_ =
std::make_unique<flutter::FlutterViewController>(frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
@@ -104,8 +100,7 @@ bool FlutterWindow::OnCreate() {
// Register mpv player plugin.
OutputDebugStringA("FlutterWindow: About to register MpvPlayerPlugin\n");
MpvPlayerPluginRegisterWithRegistrar(
flutter_controller_->engine()->GetRegistrarForPlugin("MpvPlayerPlugin"));
MpvPlayerPluginRegisterWithRegistrar(flutter_controller_->engine()->GetRegistrarForPlugin("MpvPlayerPlugin"));
OutputDebugStringA("FlutterWindow: MpvPlayerPlugin registered\n");
RegisterWindowChannel();
@@ -116,9 +111,8 @@ bool FlutterWindow::OnCreate() {
HWND hwnd = GetHandle();
bool maximized = LoadWindowPlacement(hwnd);
flutter_controller_->engine()->SetNextFrameCallback([this, maximized]() {
::ShowWindow(this->GetHandle(), maximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
});
flutter_controller_->engine()->SetNextFrameCallback(
[this, maximized]() { ::ShowWindow(this->GetHandle(), maximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); });
// Flutter can complete the first frame before the "show window" callback is
// registered. The following call ensures a frame is pending to ensure the
@@ -152,14 +146,10 @@ void FlutterWindow::OnDestroy() {
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
FlutterWindow::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
std::optional<LRESULT> result = flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, lparam);
if (result) {
return *result;
}
@@ -196,40 +186,34 @@ FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
// ---------------------------------------------------------------------------
void FlutterWindow::RegisterWindowChannel() {
auto messenger = flutter_controller_->engine()->messenger();
window_channel_ =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
messenger, "plezy/window",
&flutter::StandardMethodCodec::GetInstance());
window_channel_ = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
messenger, "plezy/window", &flutter::StandardMethodCodec::GetInstance());
window_channel_->SetMethodCallHandler(
[this](const flutter::MethodCall<flutter::EncodableValue>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
result) {
const std::string& name = call.method_name();
if (name == "setFullScreen") {
bool value = false;
if (const auto* args =
std::get_if<flutter::EncodableMap>(call.arguments())) {
auto it = args->find(flutter::EncodableValue("isFullScreen"));
if (it != args->end()) {
if (const bool* b = std::get_if<bool>(&it->second)) value = *b;
}
}
SetNativeFullScreen(value);
result->Success();
} else if (name == "isFullScreen") {
result->Success(flutter::EncodableValue(is_fullscreen_));
} else {
result->NotImplemented();
window_channel_->SetMethodCallHandler([this](
const flutter::MethodCall<flutter::EncodableValue>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
const std::string& name = call.method_name();
if (name == "setFullScreen") {
bool value = false;
if (const auto* args = std::get_if<flutter::EncodableMap>(call.arguments())) {
auto it = args->find(flutter::EncodableValue("isFullScreen"));
if (it != args->end()) {
if (const bool* b = std::get_if<bool>(&it->second)) value = *b;
}
});
}
SetNativeFullScreen(value);
result->Success();
} else if (name == "isFullScreen") {
result->Success(flutter::EncodableValue(is_fullscreen_));
} else {
result->NotImplemented();
}
});
}
void FlutterWindow::NotifyFullScreenChanged() {
if (!window_channel_) return;
window_channel_->InvokeMethod(
"onFullScreenChanged",
std::make_unique<flutter::EncodableValue>(is_fullscreen_));
window_channel_->InvokeMethod("onFullScreenChanged", std::make_unique<flutter::EncodableValue>(is_fullscreen_));
}
void FlutterWindow::SetNativeFullScreen(bool fullscreen) {
@@ -255,8 +239,7 @@ void FlutterWindow::SetNativeFullScreen(bool fullscreen) {
POINT center{(wr.left + wr.right) / 2, (wr.top + wr.bottom) / 2};
MONITORINFO mi{};
mi.cbSize = sizeof(mi);
if (!::GetMonitorInfoW(::MonitorFromPoint(center, MONITOR_DEFAULTTONEAREST),
&mi)) {
if (!::GetMonitorInfoW(::MonitorFromPoint(center, MONITOR_DEFAULTTONEAREST), &mi)) {
g_suppressPlacementSave = false;
return;
}
@@ -271,18 +254,15 @@ void FlutterWindow::SetNativeFullScreen(bool fullscreen) {
// Strip frame/caption. Stripping WS_OVERLAPPEDWINDOW alone is enough to
// make the following SetWindowPos use the given rect exactly — no need
// to ShowWindow(SW_SHOWNORMAL) first (would cause a second relayout).
::SetWindowLongPtr(
hwnd, GWL_STYLE, style_before_fullscreen_ & ~WS_OVERLAPPEDWINDOW);
::SetWindowLongPtr(hwnd, GWL_STYLE, style_before_fullscreen_ & ~WS_OVERLAPPEDWINDOW);
::SetWindowLongPtr(
hwnd, GWL_EXSTYLE,
ex_style_before_fullscreen_ &
~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE |
WS_EX_STATICEDGE));
ex_style_before_fullscreen_ & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
const RECT& r = mi.rcMonitor;
::SetWindowPos(hwnd, HWND_TOP, r.left, r.top, r.right - r.left,
r.bottom - r.top,
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
::SetWindowPos(
hwnd, HWND_TOP, r.left, r.top, r.right - r.left, r.bottom - r.top,
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
is_fullscreen_ = true;
} else {
@@ -298,9 +278,8 @@ void FlutterWindow::SetNativeFullScreen(bool fullscreen) {
}
// Force a frame refresh so restored chrome paints.
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE |
SWP_FRAMECHANGED);
::SetWindowPos(
hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
is_fullscreen_ = false;
placement_before_fullscreen_ = {};
+2 -4
View File
@@ -21,8 +21,7 @@ class FlutterWindow : public Win32Window {
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
private:
// The project to run.
@@ -32,8 +31,7 @@ class FlutterWindow : public Win32Window {
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
// Method channel exposing window controls to Dart (plezy/window).
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>>
window_channel_;
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> window_channel_;
// Fullscreen state tracking for monitor-aware native fullscreen.
// Maximize state lives inside `placement_before_fullscreen_.showCmd`.
+4 -6
View File
@@ -6,8 +6,8 @@
#include "mpv/display_mode_manager.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
int APIENTRY
wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t* command_line, _In_ int show_command) {
// Single instance enforcement
HANDLE mutex = CreateMutex(nullptr, TRUE, L"com.edde746.Plezy.SingleInstance");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
@@ -33,8 +33,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
flutter::DartProject project(L"data");
project.set_ui_thread_policy(flutter::UIThreadPolicy::RunOnSeparateThread);
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
std::vector<std::string> command_line_arguments = GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
@@ -47,8 +46,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
window.SetQuitOnClose(true);
// Recover display mode if a prior crash left it changed.
mpv::DisplayModeManager::RecoverIfNeeded(
::GetAncestor(window.GetHandle(), GA_ROOT));
mpv::DisplayModeManager::RecoverIfNeeded(::GetAncestor(window.GetHandle(), GA_ROOT));
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
+46 -70
View File
@@ -1,9 +1,9 @@
#include "display_mode_manager.h"
#include "sdk_26100.h"
#include <cmath>
#include <algorithm>
#include <cmath>
#include "sdk_26100.h"
namespace mpv {
@@ -44,14 +44,12 @@ std::vector<DISPLAYCONFIG_PATH_INFO> DisplayModeManager::GetDisplayConfigPaths()
// Retry loop for ERROR_INSUFFICIENT_BUFFER (Kodi pattern).
do {
if (GetDisplayConfigBufferSizes(flags, &path_count, &mode_count) != ERROR_SUCCESS)
return {};
if (GetDisplayConfigBufferSizes(flags, &path_count, &mode_count) != ERROR_SUCCESS) return {};
paths.resize(path_count);
modes.resize(mode_count);
result = QueryDisplayConfig(flags, &path_count, paths.data(),
&mode_count, modes.data(), nullptr);
result = QueryDisplayConfig(flags, &path_count, paths.data(), &mode_count, modes.data(), nullptr);
} while (result == ERROR_INSUFFICIENT_BUFFER);
if (result != ERROR_SUCCESS) return {};
@@ -60,8 +58,7 @@ std::vector<DISPLAYCONFIG_PATH_INFO> DisplayModeManager::GetDisplayConfigPaths()
return paths;
}
std::optional<DisplayConfigId> DisplayModeManager::GetDisplayTargetId(
const std::wstring& gdi_device_name) {
std::optional<DisplayConfigId> DisplayModeManager::GetDisplayTargetId(const std::wstring& gdi_device_name) {
// Follows Kodi's GetDisplayTargetId: iterate QueryDisplayConfig paths,
// match via DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.viewGdiDeviceName.
DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {};
@@ -72,8 +69,7 @@ std::optional<DisplayConfigId> DisplayModeManager::GetDisplayTargetId(
source.header.adapterId = path.sourceInfo.adapterId;
source.header.id = path.sourceInfo.id;
if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS &&
gdi_device_name == source.viewGdiDeviceName) {
if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS && gdi_device_name == source.viewGdiDeviceName) {
return DisplayConfigId{path.targetInfo.adapterId, path.targetInfo.id};
}
}
@@ -116,9 +112,13 @@ std::vector<DisplayMode> DisplayModeManager::EnumerateDisplayModes(HWND window)
if (a.height != b.height) return a.height < b.height;
return a.refresh_rate < b.refresh_rate;
});
modes.erase(std::unique(modes.begin(), modes.end(), [](const DisplayMode& a, const DisplayMode& b) {
return a.width == b.width && a.height == b.height && a.refresh_rate == b.refresh_rate;
}), modes.end());
modes.erase(
std::unique(
modes.begin(), modes.end(),
[](const DisplayMode& a, const DisplayMode& b) {
return a.width == b.width && a.height == b.height && a.refresh_rate == b.refresh_rate;
}),
modes.end());
return modes;
}
@@ -145,12 +145,10 @@ void DisplayModeManager::SaveOriginalMode(HWND window) {
original_devmode_ = {};
original_devmode_.dmSize = sizeof(original_devmode_);
EnumDisplaySettingsW(original_device_name_.c_str(), ENUM_CURRENT_SETTINGS,
&original_devmode_);
EnumDisplaySettingsW(original_device_name_.c_str(), ENUM_CURRENT_SETTINGS, &original_devmode_);
}
bool DisplayModeManager::SetDisplayMode(HWND window, DWORD width, DWORD height,
DWORD refresh_rate) {
bool DisplayModeManager::SetDisplayMode(HWND window, DWORD width, DWORD height, DWORD refresh_rate) {
std::wstring device_name = GetMonitorDeviceName(window);
if (device_name.empty()) return false;
@@ -175,25 +173,21 @@ bool DisplayModeManager::SetDisplayMode(HWND window, DWORD width, DWORD height,
DEVMODEW registry_dm = {};
registry_dm.dmSize = sizeof(registry_dm);
if (EnumDisplaySettingsW(device_name.c_str(), ENUM_REGISTRY_SETTINGS, &registry_dm)) {
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr,
CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) {
rc = ChangeDisplaySettingsExW(device_name.c_str(), nullptr, nullptr,
CDS_FULLSCREEN, nullptr);
rc = ChangeDisplaySettingsExW(device_name.c_str(), nullptr, nullptr, CDS_FULLSCREEN, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) changed = true;
// Restore original registry settings.
registry_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(device_name.c_str(), &registry_dm, nullptr,
CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
ChangeDisplaySettingsExW(device_name.c_str(), &registry_dm, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
}
}
}
// Standard path / fallback.
if (!changed) {
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr,
CDS_FULLSCREEN, nullptr);
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_FULLSCREEN, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) changed = true;
}
@@ -210,9 +204,8 @@ bool DisplayModeManager::RestoreOriginalMode(HWND window) {
original_devmode_.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
LONG rc = ChangeDisplaySettingsExW(original_device_name_.c_str(),
&original_devmode_, nullptr,
CDS_FULLSCREEN, nullptr);
LONG rc =
ChangeDisplaySettingsExW(original_device_name_.c_str(), &original_devmode_, nullptr, CDS_FULLSCREEN, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) {
mode_changed_ = false;
@@ -239,8 +232,7 @@ bool DisplayModeManager::IsHDRSupported(HWND window) {
// Follows Kodi's GetDisplayHDRStatus pattern.
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 info = {};
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.size = sizeof(info);
info.header.adapterId = target_id->adapter_id;
info.header.id = target_id->id;
@@ -275,8 +267,7 @@ bool DisplayModeManager::IsHDREnabled(HWND window) {
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 info = {};
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.size = sizeof(info);
info.header.adapterId = target_id->adapter_id;
info.header.id = target_id->id;
@@ -331,8 +322,7 @@ bool DisplayModeManager::SetHDREnabled(HWND window, bool enabled) {
// Source: Kodi WIN32Util.cpp:1276-1288.
if (pre_toggle_dm.dmDisplayFrequency != 0) {
pre_toggle_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(device_name.c_str(), &pre_toggle_dm, nullptr,
CDS_FULLSCREEN, nullptr);
ChangeDisplaySettingsExW(device_name.c_str(), &pre_toggle_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
hdr_changed_ = true;
@@ -365,8 +355,7 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
// Restore DEVMODEW after toggle.
if (pre_toggle_dm.dmDisplayFrequency != 0) {
pre_toggle_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(original_hdr_device_name_.c_str(), &pre_toggle_dm,
nullptr, CDS_FULLSCREEN, nullptr);
ChangeDisplaySettingsExW(original_hdr_device_name_.c_str(), &pre_toggle_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
hdr_changed_ = false;
@@ -379,8 +368,7 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
LONG DisplayModeManager::SetHDRStateForTarget(const DisplayConfigId& target, bool enabled) {
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_SET_HDR_STATE state = {};
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(
DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
state.header.size = sizeof(state);
state.header.adapterId = target.adapter_id;
state.header.id = target.id;
@@ -399,44 +387,39 @@ LONG DisplayModeManager::SetHDRStateForTarget(const DisplayConfigId& target, boo
bool DisplayModeManager::WriteRegistryDWORD(const wchar_t* value_name, DWORD value) {
HKEY key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr,
0, KEY_WRITE, nullptr, &key, nullptr) != ERROR_SUCCESS)
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr) !=
ERROR_SUCCESS)
return false;
LONG result = RegSetValueExW(key, value_name, 0, REG_DWORD,
reinterpret_cast<const BYTE*>(&value), sizeof(value));
LONG result = RegSetValueExW(key, value_name, 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
bool DisplayModeManager::WriteRegistryString(const wchar_t* value_name,
const std::wstring& value) {
bool DisplayModeManager::WriteRegistryString(const wchar_t* value_name, const std::wstring& value) {
HKEY key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr,
0, KEY_WRITE, nullptr, &key, nullptr) != ERROR_SUCCESS)
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr) !=
ERROR_SUCCESS)
return false;
LONG result = RegSetValueExW(key, value_name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()),
static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
LONG result = RegSetValueExW(
key, value_name, 0, REG_SZ, reinterpret_cast<const BYTE*>(value.c_str()),
static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
bool DisplayModeManager::ReadRegistryDWORD(const wchar_t* value_name, DWORD& value) {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS)
return false;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) return false;
DWORD size = sizeof(value);
DWORD type = 0;
LONG result = RegQueryValueExW(key, value_name, nullptr, &type,
reinterpret_cast<BYTE*>(&value), &size);
LONG result = RegQueryValueExW(key, value_name, nullptr, &type, reinterpret_cast<BYTE*>(&value), &size);
RegCloseKey(key);
return result == ERROR_SUCCESS && type == REG_DWORD;
}
bool DisplayModeManager::ReadRegistryString(const wchar_t* value_name, std::wstring& value) {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS)
return false;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) return false;
DWORD size = 0;
DWORD type = 0;
RegQueryValueExW(key, value_name, nullptr, &type, nullptr, &size);
@@ -445,8 +428,7 @@ bool DisplayModeManager::ReadRegistryString(const wchar_t* value_name, std::wstr
return false;
}
value.resize(size / sizeof(wchar_t));
LONG result = RegQueryValueExW(key, value_name, nullptr, nullptr,
reinterpret_cast<BYTE*>(&value[0]), &size);
LONG result = RegQueryValueExW(key, value_name, nullptr, nullptr, reinterpret_cast<BYTE*>(&value[0]), &size);
RegCloseKey(key);
if (result != ERROR_SUCCESS) return false;
// Remove trailing null.
@@ -456,8 +438,7 @@ bool DisplayModeManager::ReadRegistryString(const wchar_t* value_name, std::wstr
bool DisplayModeManager::DeleteRegistryValue(const wchar_t* value_name) {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_WRITE, &key) != ERROR_SUCCESS)
return false;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_WRITE, &key) != ERROR_SUCCESS) return false;
RegDeleteValueW(key, value_name);
RegCloseKey(key);
return true;
@@ -517,8 +498,7 @@ bool DisplayModeManager::RecoverIfNeeded(HWND window) {
dm.dmDisplayFrequency = refresh;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr,
CDS_FULLSCREEN, nullptr);
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_FULLSCREEN, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) recovered = true;
}
}
@@ -542,8 +522,7 @@ bool DisplayModeManager::RecoverIfNeeded(HWND window) {
// Restore display mode after HDR toggle.
if (pre_dm.dmDisplayFrequency != 0) {
pre_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(device_name.c_str(), &pre_dm, nullptr,
CDS_FULLSCREEN, nullptr);
ChangeDisplaySettingsExW(device_name.c_str(), &pre_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
}
}
@@ -556,10 +535,8 @@ bool DisplayModeManager::RecoverIfNeeded(HWND window) {
// --- Refresh rate matching ---
DWORD DisplayModeManager::FindBestRefreshRate(double video_fps,
const std::vector<DisplayMode>& modes,
DWORD current_width,
DWORD current_height) {
DWORD DisplayModeManager::FindBestRefreshRate(
double video_fps, const std::vector<DisplayMode>& modes, DWORD current_width, DWORD current_height) {
if (video_fps <= 0) return 0;
// Collect unique refresh rates available at the current resolution.
@@ -592,8 +569,7 @@ DWORD DisplayModeManager::FindBestRefreshRate(double video_fps,
// Prefer lowest multiplier (exact match > 2x > 3x > ...).
// Among equal multipliers, prefer higher rate (shouldn't happen, but safe).
if (best_rate == 0 || multiplier < best_multiplier ||
(multiplier == best_multiplier && rate > best_rate)) {
if (best_rate == 0 || multiplier < best_multiplier || (multiplier == best_multiplier && rate > best_rate)) {
best_rate = rate;
best_multiplier = multiplier;
}
+10 -7
View File
@@ -26,10 +26,14 @@ struct DisplayConfigId {
// Pure Win32 utility — no mpv or Flutter dependency.
//
// References:
// ChangeDisplaySettingsExW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw
// EnumDisplaySettingsW: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw
// DisplayConfigGetDeviceInfo: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfiggetdeviceinfo
// DisplayConfigSetDeviceInfo: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfigsetdeviceinfo
// ChangeDisplaySettingsExW:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw
// EnumDisplaySettingsW:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw
// DisplayConfigGetDeviceInfo:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfiggetdeviceinfo
// DisplayConfigSetDeviceInfo:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfigsetdeviceinfo
// Kodi impl: xbmc/platform/win32/DisplayUtilsWin32.cpp, xbmc/platform/win32/WIN32Util.cpp
class DisplayModeManager {
public:
@@ -98,9 +102,8 @@ class DisplayModeManager {
// Find the best matching refresh rate for a given video fps from available modes.
// Returns 0 if no suitable match found.
static DWORD FindBestRefreshRate(double video_fps,
const std::vector<DisplayMode>& modes,
DWORD current_width, DWORD current_height);
static DWORD FindBestRefreshRate(
double video_fps, const std::vector<DisplayMode>& modes, DWORD current_width, DWORD current_height);
private:
// Get the GDI device name for the monitor containing the window.
+11 -18
View File
@@ -22,16 +22,13 @@ HWND MpvContainer::Create() {
// Use WS_POPUP for a borderless window without title bar.
// Use WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP to prevent shadow and DWM effects.
handle_ = ::CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_NOREDIRECTIONBITMAP,
kClassName, kWindowName, WS_POPUP,
0, 0, 100, 100, nullptr, nullptr,
GetModuleHandle(nullptr), nullptr);
WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_NOREDIRECTIONBITMAP, kClassName, kWindowName, WS_POPUP, 0, 0, 100,
100, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
// Disable DWM animations on the container.
auto disable_window_transitions = TRUE;
DwmSetWindowAttribute(handle_, DWMWA_TRANSITIONS_FORCEDISABLED,
&disable_window_transitions,
sizeof(disable_window_transitions));
DwmSetWindowAttribute(
handle_, DWMWA_TRANSITIONS_FORCEDISABLED, &disable_window_transitions, sizeof(disable_window_transitions));
return handle_;
}
@@ -43,11 +40,10 @@ HWND MpvContainer::Get(HWND flutter_window) {
RECT window_rect;
::GetWindowRect(flutter_window, &window_rect);
::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));
::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));
::ShowWindow(handle_, SW_SHOWNOACTIVATE);
::SetFocus(flutter_window);
@@ -55,10 +51,8 @@ HWND MpvContainer::Get(HWND flutter_window) {
return handle_;
}
LRESULT CALLBACK MpvContainer::WindowProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
LRESULT CALLBACK
MpvContainer::WindowProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY: {
::PostQuitMessage(0);
@@ -87,7 +81,6 @@ LRESULT CALLBACK MpvContainer::WindowProc(HWND const window,
return ::DefWindowProc(window, message, wparam, lparam);
}
std::unique_ptr<MpvContainer> MpvContainer::instance_ =
std::make_unique<MpvContainer>();
std::unique_ptr<MpvContainer> MpvContainer::instance_ = std::make_unique<MpvContainer>();
} // namespace mpv
+1 -2
View File
@@ -26,8 +26,7 @@ class MpvContainer {
HWND handle() const { return handle_; }
private:
static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM wparam,
LPARAM lparam) noexcept;
static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
HWND handle_ = nullptr;
+28 -40
View File
@@ -9,12 +9,9 @@ namespace mpv {
MpvCore* MpvCore::GetInstance() { return instance_.get(); }
void MpvCore::SetInstance(std::unique_ptr<MpvCore> instance) {
instance_ = std::move(instance);
}
void MpvCore::SetInstance(std::unique_ptr<MpvCore> instance) { instance_ = std::move(instance); }
MpvCore::MpvCore(HWND flutter_window)
: flutter_window_(flutter_window) {}
MpvCore::MpvCore(HWND flutter_window) : flutter_window_(flutter_window) {}
MpvCore::~MpvCore() {
// Close all mpv views.
@@ -29,8 +26,7 @@ void MpvCore::EnsureInitialized() {
container_ = MpvContainer::GetInstance()->Get(flutter_window_);
}
void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect,
double device_pixel_ratio) {
void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect, double device_pixel_ratio) {
::SetParent(mpv_hwnd, container_);
::ShowWindow(mpv_hwnd, SW_SHOW);
@@ -43,21 +39,19 @@ void MpvCore::CreateMpvView(HWND mpv_hwnd, RECT rect,
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);
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);
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);
::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) {
@@ -76,24 +70,22 @@ void MpvCore::SetVisible(bool visible) {
}
}
std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam) {
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);
::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 ||
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();
@@ -112,17 +104,16 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
// 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);
::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);
::RedrawWindow(flutter_window_, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
}
}
break;
@@ -130,14 +121,12 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
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);
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) {
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;
}
@@ -158,8 +147,7 @@ std::optional<HRESULT> MpvCore::WindowProc(HWND hwnd, UINT message,
return std::nullopt;
}
RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
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_));
@@ -177,8 +165,8 @@ RECT MpvCore::GetGlobalRect(int32_t left, int32_t top, int32_t right,
}
void MpvCore::EnableComposition() {
::SetWindowPos(flutter_window_, nullptr, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
::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;
+1 -2
View File
@@ -39,8 +39,7 @@ class MpvCore {
void SetVisible(bool visible);
// Window procedure handler for Flutter window messages.
std::optional<HRESULT> WindowProc(HWND hwnd, UINT message, WPARAM wparam,
LPARAM lparam);
std::optional<HRESULT> WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
private:
RECT GetGlobalRect(int32_t left, int32_t top, int32_t right, int32_t bottom);
+18 -31
View File
@@ -23,9 +23,8 @@ bool MpvPlayer::Initialize(HWND container, HWND flutter_window) {
}
// Create a child window for mpv to render into.
hwnd_ = ::CreateWindowW(L"STATIC", L"", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100,
container, nullptr, GetModuleHandle(nullptr),
nullptr);
hwnd_ = ::CreateWindowW(
L"STATIC", L"", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100, container, nullptr, GetModuleHandle(nullptr), nullptr);
if (!hwnd_) {
mpv_destroy(mpv_);
mpv_ = nullptr;
@@ -118,8 +117,7 @@ void MpvPlayer::Command(const std::vector<std::string>& args) {
mpv_command(mpv_, c_args.data());
}
void MpvPlayer::CommandAsync(const std::vector<std::string>& args,
CommandCallback callback) {
void MpvPlayer::CommandAsync(const std::vector<std::string>& args, CommandCallback callback) {
if (!mpv_) {
if (callback) callback(0);
return;
@@ -178,9 +176,7 @@ std::string MpvPlayer::GetProperty(const std::string& name) {
return result;
}
void MpvPlayer::ObserveProperty(const std::string& name,
const std::string& format,
int id) {
void MpvPlayer::ObserveProperty(const std::string& name, const std::string& format, int id) {
if (!mpv_) return;
// Check if already observing.
@@ -213,9 +209,10 @@ void MpvPlayer::SetRect(RECT rect, double device_pixel_ratio) {
device_pixel_ratio_ = device_pixel_ratio;
if (hwnd_ && container_ && flutter_window_) {
// The rect from Dart is in Flutter client area coordinates (0,0 is top-left of Flutter content).
// The container window is positioned to match the Flutter window's full bounds (including title bar).
// We need to offset the mpv window within the container to align with Flutter's client area.
// The rect from Dart is in Flutter client area coordinates (0,0 is top-left of Flutter
// content). The container window is positioned to match the Flutter window's full bounds
// (including title bar). We need to offset the mpv window within the container to align with
// Flutter's client area.
// Get the Flutter window's window rect (screen coordinates, includes title bar)
RECT window_rect;
@@ -310,17 +307,13 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
case MPV_EVENT_LOG_MESSAGE: {
auto* msg = static_cast<mpv_event_log_message*>(event->data);
char log_msg[512];
snprintf(log_msg, sizeof(log_msg), "MPV [%s] %s: %s",
msg->level, msg->prefix, msg->text);
snprintf(log_msg, sizeof(log_msg), "MPV [%s] %s: %s", msg->level, msg->prefix, msg->text);
OutputDebugStringA(log_msg);
flutter::EncodableMap data;
data[flutter::EncodableValue("prefix")] =
flutter::EncodableValue(SanitizeUtf8(msg->prefix));
data[flutter::EncodableValue("level")] =
flutter::EncodableValue(SanitizeUtf8(msg->level));
data[flutter::EncodableValue("text")] =
flutter::EncodableValue(SanitizeUtf8(msg->text));
data[flutter::EncodableValue("prefix")] = flutter::EncodableValue(SanitizeUtf8(msg->prefix));
data[flutter::EncodableValue("level")] = flutter::EncodableValue(SanitizeUtf8(msg->level));
data[flutter::EncodableValue("text")] = flutter::EncodableValue(SanitizeUtf8(msg->text));
SendEvent("log-message", data);
break;
}
@@ -353,8 +346,7 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
}
// Handle sig-peak for HDR detection
if (strcmp(prop->name, "video-params/sig-peak") == 0 &&
prop->format == MPV_FORMAT_DOUBLE && prop->data) {
if (strcmp(prop->name, "video-params/sig-peak") == 0 && prop->format == MPV_FORMAT_DOUBLE && prop->data) {
double sigPeak = *static_cast<double*>(prop->data);
last_sig_peak_ = sigPeak;
UpdateHDRMode(sigPeak);
@@ -364,8 +356,7 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
// to null output (e.g. after sleep/wake or device unplug), re-set
// audio-device to switch back to the real output.
// Mirrors mpv's TOOLS/lua/ao-null-reload.lua for embedded libmpv.
if (strcmp(prop->name, "audio-device-list") == 0 &&
GetProperty("current-ao") == "null") {
if (strcmp(prop->name, "audio-device-list") == 0 && GetProperty("current-ao") == "null") {
auto device = GetProperty("audio-device");
if (!device.empty()) {
mpv_set_property_string(mpv_, "audio-device", device.c_str());
@@ -378,13 +369,10 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
case MPV_EVENT_END_FILE: {
auto* end = static_cast<mpv_event_end_file*>(event->data);
flutter::EncodableMap data;
data[flutter::EncodableValue("reason")] =
flutter::EncodableValue(static_cast<int>(end->reason));
data[flutter::EncodableValue("reason")] = flutter::EncodableValue(static_cast<int>(end->reason));
if (end->reason == MPV_END_FILE_REASON_ERROR) {
data[flutter::EncodableValue("error")] =
flutter::EncodableValue(static_cast<int>(end->error));
data[flutter::EncodableValue("message")] =
flutter::EncodableValue(SanitizeUtf8(mpv_error_string(end->error)));
data[flutter::EncodableValue("error")] = flutter::EncodableValue(static_cast<int>(end->error));
data[flutter::EncodableValue("message")] = flutter::EncodableValue(SanitizeUtf8(mpv_error_string(end->error)));
}
SendEvent("end-file", data);
break;
@@ -443,8 +431,7 @@ void MpvPlayer::SendPropertyChange(const char* name, mpv_node* data) {
}
}
void MpvPlayer::SendEvent(const std::string& name,
const flutter::EncodableMap& data) {
void MpvPlayer::SendEvent(const std::string& name, const flutter::EncodableMap& data) {
flutter::EncodableMap event;
event[flutter::EncodableValue("type")] = flutter::EncodableValue("event");
event[flutter::EncodableValue("name")] = flutter::EncodableValue(name);
+6 -10
View File
@@ -2,6 +2,7 @@
#define MPV_PLAYER_H_
#include <Windows.h>
#include <flutter/encodable_value.h>
#include <mpv/client.h>
#include <atomic>
@@ -13,16 +14,13 @@
#include <thread>
#include <vector>
#include <flutter/encodable_value.h>
namespace mpv {
// Wrapper for libmpv that handles initialization, commands, properties,
// and event dispatching.
class MpvPlayer {
public:
using EventCallback =
std::function<void(const flutter::EncodableValue&)>;
using EventCallback = std::function<void(const flutter::EncodableValue&)>;
MpvPlayer();
~MpvPlayer();
@@ -53,8 +51,7 @@ class MpvPlayer {
std::string GetProperty(const std::string& name);
// Observes an mpv property for changes.
void ObserveProperty(const std::string& name, const std::string& format,
int id);
void ObserveProperty(const std::string& name, const std::string& format, int id);
// Returns the mpv video window handle.
HWND GetHwnd() const { return hwnd_; }
@@ -77,8 +74,7 @@ class MpvPlayer {
void EventLoop();
void HandleMpvEvent(mpv_event* event);
void SendPropertyChange(const char* name, mpv_node* data);
void SendEvent(const std::string& name,
const flutter::EncodableMap& data = {});
void SendEvent(const std::string& name, const flutter::EncodableMap& data = {});
mpv_handle* mpv_ = nullptr;
HWND hwnd_ = nullptr;
@@ -101,8 +97,8 @@ class MpvPlayer {
std::mutex pending_commands_mutex_;
// HDR state
bool hdr_enabled_ = true; // User preference
double last_sig_peak_ = 0.0; // Last known sig-peak for HDR content detection
bool hdr_enabled_ = true; // User preference
double last_sig_peak_ = 0.0; // Last known sig-peak for HDR content detection
// HDR methods
void SetHDREnabled(bool enabled);
+43 -73
View File
@@ -11,52 +11,40 @@ static flutter::EncodableMap DisplayModeToMap(const mpv::DisplayMode& mode) {
return m;
}
void MpvPlayerPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
void MpvPlayerPluginRegisterWithRegistrar(FlutterDesktopPluginRegistrarRef registrar) {
mpv::MpvPlayerPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
flutter::PluginRegistrarManager::GetInstance()->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}
namespace mpv {
void MpvPlayerPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows* registrar) {
void MpvPlayerPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {
auto plugin = std::make_unique<MpvPlayerPlugin>(registrar);
registrar->AddPlugin(std::move(plugin));
}
MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar)
: registrar_(registrar) {
MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar) : registrar_(registrar) {
// Create method channel.
method_channel_ =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "com.plezy/mpv_player",
&flutter::StandardMethodCodec::GetInstance());
method_channel_ = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "com.plezy/mpv_player", &flutter::StandardMethodCodec::GetInstance());
method_channel_->SetMethodCallHandler(
[this](const auto& call, auto result) {
HandleMethodCall(call, std::move(result));
});
[this](const auto& call, auto result) { HandleMethodCall(call, std::move(result)); });
// Create event channel.
event_channel_ =
std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
registrar->messenger(), "com.plezy/mpv_player/events",
&flutter::StandardMethodCodec::GetInstance());
event_channel_ = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
registrar->messenger(), "com.plezy/mpv_player/events", &flutter::StandardMethodCodec::GetInstance());
auto handler = std::make_unique<
flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
[this](const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&&
events) -> std::unique_ptr<flutter::StreamHandlerError<
flutter::EncodableValue>> {
auto handler = std::make_unique<flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
[this](
const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events)
-> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
event_sink_ = std::move(events);
return nullptr;
},
[this](const flutter::EncodableValue* arguments)
-> std::unique_ptr<
flutter::StreamHandlerError<flutter::EncodableValue>> {
-> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
event_sink_ = nullptr;
return nullptr;
});
@@ -72,13 +60,9 @@ MpvPlayerPlugin::~MpvPlayerPlugin() {
}
}
HWND MpvPlayerPlugin::GetChildWindow() {
return registrar_->GetView()->GetNativeWindow();
}
HWND MpvPlayerPlugin::GetChildWindow() { return registrar_->GetView()->GetNativeWindow(); }
HWND MpvPlayerPlugin::GetWindow() {
return ::GetAncestor(GetChildWindow(), GA_ROOT);
}
HWND MpvPlayerPlugin::GetWindow() { return ::GetAncestor(GetChildWindow(), GA_ROOT); }
void MpvPlayerPlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
@@ -94,11 +78,10 @@ void MpvPlayerPlugin::HandleMethodCall(
HWND flutter_window = GetWindow();
MpvCore::SetInstance(
std::make_unique<MpvCore>(flutter_window));
MpvCore::SetInstance(std::make_unique<MpvCore>(flutter_window));
proc_id_ = registrar_->RegisterTopLevelWindowProcDelegate(
[](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
proc_id_ =
registrar_->RegisterTopLevelWindowProcDelegate([](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
auto* core = MpvCore::GetInstance();
if (core) {
return core->WindowProc(hwnd, message, wparam, lparam);
@@ -121,9 +104,7 @@ void MpvPlayerPlugin::HandleMethodCall(
if (success) {
// Set up event callback.
player_->SetEventCallback([this](const flutter::EncodableValue& event) {
SendEvent(event);
});
player_->SetEventCallback([this](const flutter::EncodableValue& event) { SendEvent(event); });
// Register the mpv window with core for z-order management.
RECT rect = {0, 0, 100, 100};
@@ -161,8 +142,7 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& map = std::get<flutter::EncodableMap>(*args);
auto it = map.find(flutter::EncodableValue("args"));
if (it == map.end() ||
!std::holds_alternative<flutter::EncodableList>(it->second)) {
if (it == map.end() || !std::holds_alternative<flutter::EncodableList>(it->second)) {
result->Error("INVALID_ARGS", "Missing 'args' list");
return;
}
@@ -177,12 +157,13 @@ void MpvPlayerPlugin::HandleMethodCall(
// Use async command to prevent UI blocking during network operations
// Move result into shared_ptr for safe capture in callback
auto result_ptr = std::make_shared<std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>>(std::move(result));
auto result_ptr =
std::make_shared<std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>>(std::move(result));
std::string cmd_name = command_args.empty() ? "unknown" : command_args[0];
player_->CommandAsync(command_args, [result_ptr, cmd_name](int error) {
if (error < 0) {
(*result_ptr)->Error("COMMAND_FAILED",
"MPV command failed: " + cmd_name + " (error " + std::to_string(error) + ")");
(*result_ptr)
->Error("COMMAND_FAILED", "MPV command failed: " + cmd_name + " (error " + std::to_string(error) + ")");
} else {
(*result_ptr)->Success();
}
@@ -204,19 +185,16 @@ void MpvPlayerPlugin::HandleMethodCall(
auto name_it = map.find(flutter::EncodableValue("name"));
auto value_it = map.find(flutter::EncodableValue("value"));
if (name_it == map.end() ||
!std::holds_alternative<std::string>(name_it->second)) {
if (name_it == map.end() || !std::holds_alternative<std::string>(name_it->second)) {
result->Error("INVALID_ARGS", "Missing 'name'");
return;
}
if (value_it == map.end() ||
!std::holds_alternative<std::string>(value_it->second)) {
if (value_it == map.end() || !std::holds_alternative<std::string>(value_it->second)) {
result->Error("INVALID_ARGS", "Missing 'value'");
return;
}
player_->SetProperty(std::get<std::string>(name_it->second),
std::get<std::string>(value_it->second));
player_->SetProperty(std::get<std::string>(name_it->second), std::get<std::string>(value_it->second));
result->Success();
} else if (method == "setLogLevel") {
if (!player_ || !player_->IsInitialized()) {
@@ -233,8 +211,7 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& map = std::get<flutter::EncodableMap>(*args);
auto level_it = map.find(flutter::EncodableValue("level"));
if (level_it == map.end() ||
!std::holds_alternative<std::string>(level_it->second)) {
if (level_it == map.end() || !std::holds_alternative<std::string>(level_it->second)) {
result->Error("INVALID_ARGS", "Missing 'level'");
return;
}
@@ -256,14 +233,12 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& map = std::get<flutter::EncodableMap>(*args);
auto name_it = map.find(flutter::EncodableValue("name"));
if (name_it == map.end() ||
!std::holds_alternative<std::string>(name_it->second)) {
if (name_it == map.end() || !std::holds_alternative<std::string>(name_it->second)) {
result->Error("INVALID_ARGS", "Missing 'name'");
return;
}
std::string value =
player_->GetProperty(std::get<std::string>(name_it->second));
std::string value = player_->GetProperty(std::get<std::string>(name_it->second));
if (value.empty()) {
result->Success();
} else {
@@ -286,25 +261,22 @@ void MpvPlayerPlugin::HandleMethodCall(
auto format_it = map.find(flutter::EncodableValue("format"));
auto id_it = map.find(flutter::EncodableValue("id"));
if (name_it == map.end() ||
!std::holds_alternative<std::string>(name_it->second)) {
if (name_it == map.end() || !std::holds_alternative<std::string>(name_it->second)) {
result->Error("INVALID_ARGS", "Missing 'name'");
return;
}
if (format_it == map.end() ||
!std::holds_alternative<std::string>(format_it->second)) {
if (format_it == map.end() || !std::holds_alternative<std::string>(format_it->second)) {
result->Error("INVALID_ARGS", "Missing 'format'");
return;
}
if (id_it == map.end() ||
!std::holds_alternative<int32_t>(id_it->second)) {
if (id_it == map.end() || !std::holds_alternative<int32_t>(id_it->second)) {
result->Error("INVALID_ARGS", "Missing 'id'");
return;
}
player_->ObserveProperty(std::get<std::string>(name_it->second),
std::get<std::string>(format_it->second),
std::get<int32_t>(id_it->second));
player_->ObserveProperty(
std::get<std::string>(name_it->second), std::get<std::string>(format_it->second),
std::get<int32_t>(id_it->second));
result->Success();
} else if (method == "setVisible") {
const auto* args = method_call.arguments();
@@ -316,8 +288,7 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& map = std::get<flutter::EncodableMap>(*args);
auto visible_it = map.find(flutter::EncodableValue("visible"));
if (visible_it == map.end() ||
!std::holds_alternative<bool>(visible_it->second)) {
if (visible_it == map.end() || !std::holds_alternative<bool>(visible_it->second)) {
result->Error("INVALID_ARGS", "Missing 'visible'");
return;
}
@@ -378,7 +349,7 @@ void MpvPlayerPlugin::HandleMethodCall(
bool initialized = player_ && player_->IsInitialized();
result->Success(flutter::EncodableValue(initialized));
// --- Display mode matching ---
// --- Display mode matching ---
} else if (method == "getDisplayModes") {
HWND hwnd = GetWindow();
auto modes = display_mode_manager_.EnumerateDisplayModes(hwnd);
@@ -400,13 +371,12 @@ void MpvPlayerPlugin::HandleMethodCall(
const auto& map = std::get<flutter::EncodableMap>(*args);
auto get_int = [&map](const char* key) -> int {
auto it = map.find(flutter::EncodableValue(key));
if (it != map.end() && std::holds_alternative<int32_t>(it->second))
return std::get<int32_t>(it->second);
if (it != map.end() && std::holds_alternative<int32_t>(it->second)) return std::get<int32_t>(it->second);
return 0;
};
HWND hwnd = GetWindow();
bool success = display_mode_manager_.SetDisplayMode(
hwnd, get_int("width"), get_int("height"), get_int("refreshRate"));
bool success =
display_mode_manager_.SetDisplayMode(hwnd, get_int("width"), get_int("height"), get_int("refreshRate"));
result->Success(flutter::EncodableValue(success));
} else if (method == "restoreDisplayMode") {
HWND hwnd = GetWindow();
+3 -6
View File
@@ -16,8 +16,7 @@
#include "mpv_player.h"
// C-style registration function for the plugin.
void MpvPlayerPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
void MpvPlayerPluginRegisterWithRegistrar(FlutterDesktopPluginRegistrarRef registrar);
namespace mpv {
@@ -39,10 +38,8 @@ class MpvPlayerPlugin : public flutter::Plugin {
HWND GetChildWindow();
flutter::PluginRegistrarWindows* registrar_;
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>>
method_channel_;
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>>
event_channel_;
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> method_channel_;
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>> event_channel_;
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink_;
std::unique_ptr<MpvPlayer> player_;
+5 -10
View File
@@ -56,8 +56,7 @@ typedef struct _ACCENT_POLICY {
DWORD AnimationId;
} ACCENT_POLICY;
typedef BOOL(WINAPI* _SetWindowCompositionAttribute)(
HWND, WINDOWCOMPOSITIONATTRIBDATA*);
typedef BOOL(WINAPI* _SetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
static _SetWindowCompositionAttribute g_set_window_composition_attribute = NULL;
static bool g_set_window_composition_attribute_initialized = false;
@@ -71,8 +70,7 @@ static RTL_OSVERSIONINFOW GetWindowsVersion() {
static RTL_OSVERSIONINFOW cached = []() {
HMODULE hmodule = ::GetModuleHandleW(L"ntdll.dll");
if (hmodule) {
RtlGetVersionPtr rtl_get_version_ptr =
(RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
RtlGetVersionPtr rtl_get_version_ptr = (RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
if (rtl_get_version_ptr != nullptr) {
RTL_OSVERSIONINFOW rovi = {0};
rovi.dwOSVersionInfoSize = sizeof(rovi);
@@ -87,23 +85,20 @@ static RTL_OSVERSIONINFOW GetWindowsVersion() {
return cached;
}
void SetWindowComposition(HWND window, int32_t accent_state,
int32_t gradient_color) {
void SetWindowComposition(HWND window, int32_t accent_state, int32_t gradient_color) {
if (GetWindowsVersion().dwBuildNumber >= 18362) {
if (!g_set_window_composition_attribute_initialized) {
auto user32 = ::GetModuleHandleA("user32.dll");
if (user32) {
g_set_window_composition_attribute =
reinterpret_cast<_SetWindowCompositionAttribute>(
::GetProcAddress(user32, "SetWindowCompositionAttribute"));
reinterpret_cast<_SetWindowCompositionAttribute>(::GetProcAddress(user32, "SetWindowCompositionAttribute"));
if (g_set_window_composition_attribute) {
g_set_window_composition_attribute_initialized = true;
}
}
}
if (g_set_window_composition_attribute) {
ACCENT_POLICY accent = {static_cast<ACCENT_STATE>(accent_state), 2,
static_cast<DWORD>(gradient_color), 0};
ACCENT_POLICY accent = {static_cast<ACCENT_STATE>(accent_state), 2, static_cast<DWORD>(gradient_color), 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
+1 -2
View File
@@ -11,8 +11,7 @@ namespace mpv {
// Sets window composition attribute for transparency.
// accent_state = 6 enables per-pixel transparency.
// accent_state = 0 makes window opaque.
void SetWindowComposition(HWND window, int32_t accent_state,
int32_t gradient_color);
void SetWindowComposition(HWND window, int32_t accent_state, int32_t gradient_color);
} // namespace mpv
+18 -18
View File
@@ -1,19 +1,19 @@
#ifndef PCH_H
#define PCH_H
#include <Windows.h>
#include <dwmapi.h>
#include <optional>
#include <memory>
#include <io.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <cmath>
#include <algorithm>
#include <map>
#ifndef PCH_H
#define PCH_H
#include <Windows.h>
#include <dwmapi.h>
#include <io.h>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#endif
+5 -5
View File
@@ -2,15 +2,15 @@
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
+3 -6
View File
@@ -9,7 +9,7 @@
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
FILE* unused;
freopen_s(&unused, "CONOUT$", "w", stdout);
freopen_s(&unused, "CONOUT$", "w", stderr);
std::ios::sync_with_stdio();
@@ -41,9 +41,7 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
int raw_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
int raw_length = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr);
if (raw_length <= 1) {
return std::string();
}
@@ -52,8 +50,7 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) {
std::string utf8_string;
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
input_length, utf8_string.data(), target_length, nullptr, nullptr);
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, input_length, utf8_string.data(), target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
+27 -55
View File
@@ -23,7 +23,7 @@ constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
// The number of Win32Window objects that currently exist.
@@ -33,9 +33,7 @@ using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
int Scale(int source, double scale_factor) { return static_cast<int>(source * scale_factor); }
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
@@ -45,8 +43,7 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
reinterpret_cast<EnableNonClientDpiScaling*>(GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
}
@@ -95,8 +92,7 @@ const wchar_t* WindowClassRegistrar::GetWindowClass() {
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hIcon = LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
@@ -111,34 +107,27 @@ void WindowClassRegistrar::UnregisterWindowClass() {
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::Win32Window() { ++g_active_window_count; }
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::Create(const std::wstring& title,
const Point& origin,
const Size& size) {
bool Win32Window::Create(const std::wstring& title, const Point& origin, const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const wchar_t* window_class = WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
const POINT target_point = {static_cast<LONG>(origin.x), static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
window_class, title.c_str(), WS_OVERLAPPEDWINDOW, Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr),
this);
if (!window) {
return false;
@@ -149,19 +138,14 @@ bool Win32Window::Create(const std::wstring& title,
return OnCreate();
}
bool Win32Window::Show() {
return ShowWindow(window_handle_, SW_SHOWNORMAL);
}
bool Win32Window::Show() { return ShowWindow(window_handle_, SW_SHOWNORMAL); }
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
LRESULT CALLBACK
Win32Window::WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
@@ -174,10 +158,7 @@ LRESULT CALLBACK Win32Window::WndProc(HWND const window,
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
@@ -192,8 +173,8 @@ Win32Window::MessageHandler(HWND hwnd,
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(
hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
@@ -201,8 +182,7 @@ Win32Window::MessageHandler(HWND hwnd,
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
}
return 0;
}
@@ -234,8 +214,7 @@ void Win32Window::Destroy() {
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
return reinterpret_cast<Win32Window*>(GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
@@ -243,8 +222,7 @@ void Win32Window::SetChildContent(HWND content) {
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
MoveWindow(content, frame.left, frame.top, frame.right - frame.left, frame.bottom - frame.top, true);
SetFocus(child_content_);
}
@@ -255,13 +233,9 @@ RECT Win32Window::GetClientArea() {
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
HWND Win32Window::GetHandle() { return window_handle_; }
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; }
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
@@ -275,14 +249,12 @@ void Win32Window::OnDestroy() {
void Win32Window::UpdateTheme(HWND const window) {
DWORD light_mode;
DWORD light_mode_size = sizeof(light_mode);
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue,
RRF_RT_REG_DWORD, nullptr, &light_mode,
&light_mode_size);
LSTATUS result = RegGetValue(
HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr,
&light_mode, &light_mode_size);
if (result == ERROR_SUCCESS) {
BOOL enable_dark_mode = light_mode == 0;
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
&enable_dark_mode, sizeof(enable_dark_mode));
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, &enable_dark_mode, sizeof(enable_dark_mode));
}
}
+4 -10
View File
@@ -21,8 +21,7 @@ class Win32Window {
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
Size(unsigned int width, unsigned int height) : width(width), height(height) {}
};
Win32Window();
@@ -59,10 +58,7 @@ class Win32Window {
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
virtual LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
@@ -79,10 +75,8 @@ class Win32Window {
// non-client DPI scaling so that the non-client area automatically
// responds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
static LRESULT CALLBACK
WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;