[Windows] Refactor high contrast mode detection (flutter/engine#47314)

This refactors how high contrast is implemented on Windows:

1. Added a test to verify accessibility features are updated when a view is created. This prevents staleness issues as the Windows embedder isn't notified of accessibility changes while in headless mode.
1. Moved high contrast mode detection to `WindowsProcTable` from `FlutterWindow` to remove engine to view to window plumbing.
1. `FlutterWindow` and `FlutterWindowsEngine` now share their `WindowsProcTable` (which is used for mocking and polyfilling win32 APIs) to reduce redundant dynamic loading.

This pull request contains no functional changes.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Loïc Sharma 2023-10-26 13:22:15 -07:00 committed by GitHub
parent 912c891a1d
commit f06b89215e
19 changed files with 143 additions and 113 deletions

View File

@ -119,7 +119,7 @@ static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
FlutterWindow::FlutterWindow(
int width,
int height,
std::unique_ptr<WindowsProcTable> windows_proc_table,
std::shared_ptr<WindowsProcTable> windows_proc_table,
std::unique_ptr<TextInputManager> text_input_manager)
: binding_handler_delegate_(nullptr),
touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
@ -356,12 +356,7 @@ PointerLocation FlutterWindow::GetPrimaryPointerLocation() {
}
void FlutterWindow::OnThemeChange() {
binding_handler_delegate_->UpdateHighContrastEnabled(
GetHighContrastEnabled());
}
void FlutterWindow::SendInitialAccessibilityFeatures() {
OnThemeChange();
binding_handler_delegate_->OnHighContrastChanged();
}
ui::AXFragmentRootDelegateWin* FlutterWindow::GetAxFragmentRootDelegate() {
@ -950,19 +945,6 @@ float FlutterWindow::GetScrollOffsetMultiplier() {
return scroll_offset_multiplier_;
}
bool FlutterWindow::GetHighContrastEnabled() {
HIGHCONTRAST high_contrast = {.cbSize = sizeof(HIGHCONTRAST)};
// API call is only supported on Windows 8+
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST),
&high_contrast, 0)) {
return high_contrast.dwFlags & HCF_HIGHCONTRASTON;
} else {
FML_LOG(INFO) << "Failed to get status of high contrast feature,"
<< "support only for Windows 8 + ";
return false;
}
}
LRESULT FlutterWindow::Win32DefWindowProc(HWND hWnd,
UINT Msg,
WPARAM wParam,

View File

@ -38,7 +38,7 @@ class FlutterWindow : public KeyboardManager::WindowDelegate,
// Create flutter Window for use as child window
FlutterWindow(int width,
int height,
std::unique_ptr<WindowsProcTable> windows_proc_table = nullptr,
std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr,
std::unique_ptr<TextInputManager> text_input_manager = nullptr);
virtual ~FlutterWindow();
@ -189,9 +189,6 @@ class FlutterWindow : public KeyboardManager::WindowDelegate,
// Called when a theme change message is issued.
virtual void OnThemeChange();
// |WindowBindingHandler|
virtual void SendInitialAccessibilityFeatures() override;
// |WindowBindingHandler|
virtual AlertPlatformNodeDelegate* GetAlertDelegate() override;
@ -281,9 +278,6 @@ class FlutterWindow : public KeyboardManager::WindowDelegate,
// Returns the current pixel per scroll tick value.
virtual float GetScrollOffsetMultiplier();
// Check if the high contrast feature is enabled on the OS
virtual bool GetHighContrastEnabled();
// Delegate to a alert_node_ used to set the announcement text.
std::unique_ptr<AlertPlatformNodeDelegate> alert_delegate_;
@ -381,7 +375,7 @@ class FlutterWindow : public KeyboardManager::WindowDelegate,
// Abstracts Windows APIs that may not be available on all supported versions
// of Windows.
std::unique_ptr<WindowsProcTable> windows_proc_table_;
std::shared_ptr<WindowsProcTable> windows_proc_table_;
// Manages IME state.
std::unique_ptr<TextInputManager> text_input_manager_;

View File

@ -64,7 +64,6 @@ class MockFlutterWindow : public FlutterWindow {
(override));
MOCK_METHOD(void, OnSetCursor, (), (override));
MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
MOCK_METHOD(bool, GetHighContrastEnabled, (), (override));
MOCK_METHOD(float, GetDpiScale, (), (override));
MOCK_METHOD(bool, IsVisible, (), (override));
MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
@ -290,8 +289,7 @@ TEST(FlutterWindowTest, OnThemeChange) {
MockWindowBindingHandlerDelegate delegate;
win32window.SetView(&delegate);
ON_CALL(win32window, GetHighContrastEnabled()).WillByDefault(Return(true));
EXPECT_CALL(delegate, UpdateHighContrastEnabled(true)).Times(1);
EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
}
@ -305,17 +303,6 @@ TEST(FlutterWindowTest, AccessibilityNodeWithoutView) {
EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
}
TEST(FlutterWindowTest, InitialAccessibilityFeatures) {
MockFlutterWindow win32window;
MockWindowBindingHandlerDelegate delegate;
win32window.SetView(&delegate);
ON_CALL(win32window, GetHighContrastEnabled()).WillByDefault(Return(true));
EXPECT_CALL(delegate, UpdateHighContrastEnabled(true)).Times(1);
win32window.SendInitialAccessibilityFeatures();
}
// Ensure that announcing the alert propagates the message to the alert node.
// Different screen readers use different properties for alerts.
TEST(FlutterWindowTest, AlertNode) {

View File

@ -76,11 +76,12 @@ FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(
int width,
int height,
FlutterDesktopEngineRef engine_ref) {
flutter::FlutterWindowsEngine* engine_ptr = EngineFromHandle(engine_ref);
std::unique_ptr<flutter::WindowBindingHandler> window_wrapper =
std::make_unique<flutter::FlutterWindow>(width, height);
std::make_unique<flutter::FlutterWindow>(
width, height, engine_ptr->windows_proc_table());
auto engine = std::unique_ptr<flutter::FlutterWindowsEngine>(
EngineFromHandle(engine_ref));
auto engine = std::unique_ptr<flutter::FlutterWindowsEngine>(engine_ptr);
auto view =
std::make_unique<flutter::FlutterWindowsView>(std::move(window_wrapper));
auto controller = std::make_unique<flutter::FlutterWindowsViewController>(
@ -96,7 +97,11 @@ FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(
// Must happen after engine is running.
controller->view()->SendInitialBounds();
controller->view()->SendInitialAccessibilityFeatures();
// The Windows embedder listens to accessibility updates using the
// view's HWND. The embedder's accessibility features may be stale if
// the app was in headless mode.
controller->engine()->UpdateAccessibilityFeatures();
return HandleForViewController(controller.release());
}

View File

@ -157,10 +157,17 @@ FlutterLocale CovertToFlutterLocale(const LanguageInfo& info) {
} // namespace
FlutterWindowsEngine::FlutterWindowsEngine(const FlutterProjectBundle& project)
FlutterWindowsEngine::FlutterWindowsEngine(
const FlutterProjectBundle& project,
std::shared_ptr<WindowsProcTable> windows_proc_table)
: project_(std::make_unique<FlutterProjectBundle>(project)),
windows_proc_table_(std::move(windows_proc_table)),
aot_data_(nullptr, nullptr),
lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) {
if (windows_proc_table_ == nullptr) {
windows_proc_table_ = std::make_shared<WindowsProcTable>();
}
embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
FlutterEngineGetProcAddresses(&embedder_api_);
@ -569,8 +576,7 @@ void FlutterWindowsEngine::HandlePlatformMessage(
auto message = ConvertToDesktopMessage(*engine_message);
message_dispatcher_->HandleMessage(
message, [this] {}, [this] {});
message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
}
void FlutterWindowsEngine::ReloadSystemFonts() {
@ -605,7 +611,7 @@ void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) {
void FlutterWindowsEngine::SendSystemLocales() {
std::vector<LanguageInfo> languages =
GetPreferredLanguageInfo(windows_proc_table_);
GetPreferredLanguageInfo(*windows_proc_table_);
std::vector<FlutterLocale> flutter_locales;
flutter_locales.reserve(languages.size());
for (const auto& info : languages) {
@ -737,34 +743,27 @@ std::string FlutterWindowsEngine::GetExecutableName() const {
return "Flutter";
}
void FlutterWindowsEngine::UpdateAccessibilityFeatures(
FlutterAccessibilityFeature flags) {
embedder_api_.UpdateAccessibilityFeatures(engine_, flags);
void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
UpdateHighContrastMode();
}
void FlutterWindowsEngine::UpdateHighContrastEnabled(bool enabled) {
high_contrast_enabled_ = enabled;
int flags = EnabledAccessibilityFeatures();
if (enabled) {
flags |=
FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
} else {
flags &=
~FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
}
UpdateAccessibilityFeatures(static_cast<FlutterAccessibilityFeature>(flags));
settings_plugin_->UpdateHighContrastMode(enabled);
void FlutterWindowsEngine::UpdateHighContrastMode() {
high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
SendAccessibilityFeatures();
settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
}
int FlutterWindowsEngine::EnabledAccessibilityFeatures() const {
void FlutterWindowsEngine::SendAccessibilityFeatures() {
int flags = 0;
if (high_contrast_enabled()) {
if (high_contrast_enabled_) {
flags |=
FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
}
// As more accessibility features are enabled for Windows,
// the corresponding checks and flags should be added here.
return flags;
embedder_api_.UpdateAccessibilityFeatures(
engine_, static_cast<FlutterAccessibilityFeature>(flags));
}
void FlutterWindowsEngine::HandleAccessibilityMessage(

View File

@ -78,7 +78,9 @@ static void WindowsPlatformThreadPrioritySetter(
class FlutterWindowsEngine {
public:
// Creates a new Flutter engine object configured to run |project|.
explicit FlutterWindowsEngine(const FlutterProjectBundle& project);
FlutterWindowsEngine(
const FlutterProjectBundle& project,
std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr);
virtual ~FlutterWindowsEngine();
@ -215,11 +217,11 @@ class FlutterWindowsEngine {
// Returns true if the semantics tree is enabled.
bool semantics_enabled() const { return semantics_enabled_; }
// Update the high contrast feature state.
void UpdateHighContrastEnabled(bool enabled);
// Refresh accessibility features and send them to the engine.
void UpdateAccessibilityFeatures();
// Returns the flags for all currently enabled accessibility features
int EnabledAccessibilityFeatures() const;
// Refresh high contrast accessibility mode and notify the engine.
void UpdateHighContrastMode();
// Returns true if the high contrast feature is enabled.
bool high_contrast_enabled() const { return high_contrast_enabled_; }
@ -240,9 +242,6 @@ class FlutterWindowsEngine {
// Returns the executable name for this process or "Flutter" if unknown.
std::string GetExecutableName() const;
// Updates accessibility, e.g. switch to high contrast mode
void UpdateAccessibilityFeatures(FlutterAccessibilityFeature flags);
// Called when the application quits in response to a quit request.
void OnQuit(std::optional<HWND> hwnd,
std::optional<WPARAM> wparam,
@ -274,6 +273,10 @@ class FlutterWindowsEngine {
return lifecycle_manager_.get();
}
std::shared_ptr<WindowsProcTable> windows_proc_table() {
return windows_proc_table_;
}
protected:
// Creates the keyboard key handler.
//
@ -320,6 +323,9 @@ class FlutterWindowsEngine {
// Calling this method again resets the keyboard state.
void InitializeKeyboard();
// Send the currently enabled accessibility features to the engine.
void SendAccessibilityFeatures();
void HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger,
const FlutterDesktopMessage* message);
@ -414,7 +420,7 @@ class FlutterWindowsEngine {
// Handler for top level window messages.
std::unique_ptr<WindowsLifecycleManager> lifecycle_manager_;
WindowsProcTable windows_proc_table_;
std::shared_ptr<WindowsProcTable> windows_proc_table_;
FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindowsEngine);
};

View File

@ -12,6 +12,7 @@
#include "flutter/shell/platform/windows/testing/engine_modifier.h"
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
#include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
#include "flutter/shell/platform/windows/testing/test_keyboard.h"
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
@ -25,6 +26,8 @@
namespace flutter {
namespace testing {
using ::testing::Return;
class FlutterWindowsEngineTest : public WindowsTest {};
TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
@ -558,29 +561,41 @@ TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
// Ensure that after setting or resetting the high contrast feature,
// the corresponding status flag can be retrieved from the engine.
TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
.WillOnce(Return(true))
.WillOnce(Return(false));
FlutterWindowsEngineBuilder builder{GetContext()};
builder.SetWindowsProcTable(windows_proc_table);
std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
EngineModifier modifier(engine.get());
bool called = false;
std::optional<FlutterAccessibilityFeature> engine_flags;
modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
UpdateAccessibilityFeatures, ([&called](auto engine, auto flags) {
called = true;
UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
engine_flags = flags;
return kSuccess;
}));
engine->UpdateHighContrastEnabled(true);
EXPECT_TRUE(
engine->EnabledAccessibilityFeatures() &
FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
EXPECT_TRUE(engine->high_contrast_enabled());
EXPECT_TRUE(called);
// 1: High contrast is enabled.
engine->UpdateHighContrastMode();
engine->UpdateHighContrastEnabled(false);
EXPECT_FALSE(
engine->EnabledAccessibilityFeatures() &
EXPECT_TRUE(engine->high_contrast_enabled());
EXPECT_TRUE(engine_flags.has_value());
EXPECT_TRUE(
engine_flags.value() &
FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
// 2: High contrast is disabled.
engine_flags.reset();
engine->UpdateHighContrastMode();
EXPECT_FALSE(engine->high_contrast_enabled());
EXPECT_TRUE(engine_flags.has_value());
EXPECT_FALSE(
engine_flags.value() &
FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
}
TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {

View File

@ -10,6 +10,8 @@
#include "flutter/fml/synchronization/count_down_latch.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/windows/testing/engine_modifier.h"
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
#include "flutter/shell/platform/windows/testing/windows_test_context.h"
@ -99,6 +101,27 @@ TEST_F(WindowsTest, LaunchHeadlessEngine) {
ASSERT_TRUE(FlutterDesktopEngineRun(engine.get(), nullptr));
}
// Verify that accessibility features are initialized when a view is created.
TEST_F(WindowsTest, LaunchRefreshesAccessibility) {
auto& context = GetContext();
WindowsConfigBuilder builder(context);
EnginePtr engine{builder.InitializeEngine()};
EngineModifier modifier{
reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
auto called = false;
modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
UpdateAccessibilityFeatures, ([&called](auto engine, auto flags) {
called = true;
return kSuccess;
}));
ViewControllerPtr controller{
FlutterDesktopViewControllerCreate(0, 0, engine.release())};
ASSERT_TRUE(called);
}
// Verify that engine fails to launch when a conflicting entrypoint in
// FlutterDesktopEngineProperties.dart_entrypoint and the
// FlutterDesktopEngineRun parameter.

View File

@ -613,12 +613,8 @@ void FlutterWindowsView::DestroyRenderSurface() {
}
}
void FlutterWindowsView::SendInitialAccessibilityFeatures() {
binding_handler_->SendInitialAccessibilityFeatures();
}
void FlutterWindowsView::UpdateHighContrastEnabled(bool enabled) {
engine_->UpdateHighContrastEnabled(enabled);
void FlutterWindowsView::OnHighContrastChanged() {
engine_->UpdateHighContrastMode();
}
WindowsRenderTarget* FlutterWindowsView::GetRenderTarget() const {

View File

@ -82,14 +82,11 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
// Send initial bounds to embedder. Must occur after engine has initialized.
void SendInitialBounds();
// Send the initial accessibility features to the window
void SendInitialAccessibilityFeatures();
// Set the text of the alert, and create it if it does not yet exist.
void AnnounceAlert(const std::wstring& text);
// |WindowBindingHandlerDelegate|
void UpdateHighContrastEnabled(bool enabled) override;
void OnHighContrastChanged() override;
// Returns the frame buffer id for the engine to render to.
uint32_t GetFrameBufferId(size_t width, size_t height);

View File

@ -5,6 +5,7 @@
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/windows/windows_proc_table.h"
namespace flutter {
namespace testing {
@ -14,8 +15,9 @@ class TestFlutterWindowsEngine : public FlutterWindowsEngine {
TestFlutterWindowsEngine(
const FlutterProjectBundle& project,
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state,
KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan)
: FlutterWindowsEngine(project),
KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan,
std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr)
: FlutterWindowsEngine(project, std::move(windows_proc_table)),
get_key_state_(std::move(get_key_state)),
map_vk_to_scan_(std::move(map_vk_to_scan)) {}
@ -74,6 +76,11 @@ void FlutterWindowsEngineBuilder::SetCreateKeyboardHandlerCallbacks(
map_vk_to_scan_ = std::move(map_vk_to_scan);
}
void FlutterWindowsEngineBuilder::SetWindowsProcTable(
std::shared_ptr<WindowsProcTable> windows_proc_table) {
windows_proc_table_ = std::move(windows_proc_table);
}
std::unique_ptr<FlutterWindowsEngine> FlutterWindowsEngineBuilder::Build() {
std::vector<const char*> dart_args;
dart_args.reserve(dart_entrypoint_arguments_.size());
@ -93,8 +100,8 @@ std::unique_ptr<FlutterWindowsEngine> FlutterWindowsEngineBuilder::Build() {
FlutterProjectBundle project(properties_);
project.SetSwitches(switches_);
return std::make_unique<TestFlutterWindowsEngine>(project, get_key_state_,
map_vk_to_scan_);
return std::make_unique<TestFlutterWindowsEngine>(
project, get_key_state_, map_vk_to_scan_, std::move(windows_proc_table_));
}
} // namespace testing

View File

@ -31,6 +31,9 @@ class FlutterWindowsEngineBuilder {
void SetSwitches(std::vector<std::string> switches);
void SetWindowsProcTable(
std::shared_ptr<WindowsProcTable> windows_proc_table);
std::unique_ptr<FlutterWindowsEngine> Build();
private:
@ -41,6 +44,7 @@ class FlutterWindowsEngineBuilder {
std::vector<std::string> switches_;
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state_;
KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan_;
std::shared_ptr<WindowsProcTable> windows_proc_table_;
FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindowsEngineBuilder);
};

View File

@ -38,7 +38,6 @@ class MockWindowBindingHandler : public WindowBindingHandler {
(const void* allocation, size_t row_bytes, size_t height),
(override));
MOCK_METHOD(PointerLocation, GetPrimaryPointerLocation, (), (override));
MOCK_METHOD(void, SendInitialAccessibilityFeatures, (), (override));
MOCK_METHOD(AlertPlatformNodeDelegate*, GetAlertDelegate, (), (override));
MOCK_METHOD(ui::AXPlatformNodeWin*, GetAlert, (), (override));
MOCK_METHOD(bool, NeedsVSync, (), (override));

View File

@ -68,7 +68,7 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate {
(double, double, double, double, int, FlutterPointerDeviceKind, int32_t),
(override));
MOCK_METHOD(void, OnScrollInertiaCancel, (int32_t), (override));
MOCK_METHOD(void, UpdateHighContrastEnabled, (bool enabled), (override));
MOCK_METHOD(void, OnHighContrastChanged, (), (override));
MOCK_METHOD(ui::AXFragmentRootDelegateWin*,
GetAxFragmentRootDelegate,

View File

@ -28,6 +28,8 @@ class MockWindowsProcTable : public WindowsProcTable {
(DWORD, PULONG, PZZWSTR, PULONG),
(const, override));
MOCK_METHOD(bool, GetHighContrastEnabled, (), (override));
private:
FML_DISALLOW_COPY_AND_ASSIGN(MockWindowsProcTable);
};

View File

@ -98,9 +98,6 @@ class WindowBindingHandler {
// coordinates.
virtual PointerLocation GetPrimaryPointerLocation() = 0;
// Called to set the initial state of accessibility features
virtual void SendInitialAccessibilityFeatures() = 0;
// Retrieve the delegate for the alert.
virtual AlertPlatformNodeDelegate* GetAlertDelegate() = 0;

View File

@ -136,8 +136,8 @@ class WindowBindingHandlerDelegate {
// Returns the root view accessibility node, or nullptr if none.
virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0;
// Update the status of the high contrast feature
virtual void UpdateHighContrastEnabled(bool enabled) = 0;
// Update the status of the high contrast feature.
virtual void OnHighContrastChanged() = 0;
// Obtain a pointer to the fragment root delegate.
// This is required by UIA in order to obtain the fragment root that

View File

@ -32,4 +32,14 @@ LRESULT WindowsProcTable::GetThreadPreferredUILanguages(DWORD flags,
return ::GetThreadPreferredUILanguages(flags, count, languages, length);
}
bool WindowsProcTable::GetHighContrastEnabled() {
HIGHCONTRAST high_contrast = {.cbSize = sizeof(HIGHCONTRAST)};
if (!::SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST),
&high_contrast, 0)) {
return false;
}
return high_contrast.dwFlags & HCF_HIGHCONTRASTON;
}
} // namespace flutter

View File

@ -22,7 +22,7 @@ class WindowsProcTable {
// Retrieves the pointer type for a specified pointer.
//
// Used to react differently to touch or pen inputs. Returns false on failure.
// Available in Windows 8 and newer, otherwise returns false.
// Available on Windows 8 and newer, otherwise returns false.
virtual BOOL GetPointerType(UINT32 pointer_id,
POINTER_INPUT_TYPE* pointer_type);
@ -35,6 +35,13 @@ class WindowsProcTable {
PZZWSTR languages,
PULONG length) const;
// Get whether high contrast is enabled.
//
// Available on Windows 8 and newer, otherwise returns false.
// See
// https://learn.microsoft.com/windows/win32/winauto/high-contrast-parameter
virtual bool GetHighContrastEnabled();
private:
using GetPointerType_ = BOOL __stdcall(UINT32 pointerId,
POINTER_INPUT_TYPE* pointerType);