chore: add native formatting checks
This commit is contained in:
@@ -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_ = {};
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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, ®istry_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(), ®istry_dm, nullptr,
|
||||
CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
|
||||
ChangeDisplaySettingsExW(device_name.c_str(), ®istry_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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user