| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/base/cocoa/cursor_accessibility_scale_factor.h" |
| |
| #include <Foundation/Foundation.h> |
| #include <dispatch/dispatch.h> |
| |
| #include <algorithm> |
| #include <optional> |
| |
| #include "base/apple/foundation_util.h" |
| #include "base/command_line.h" |
| #include "ui/base/cursor/cursor.h" |
| |
| @interface CursorAccessibilityScaleFactorNotifier () |
| @property(readonly, nonatomic) float scaleFactor; |
| @end |
| |
| @implementation CursorAccessibilityScaleFactorNotifier { |
| NSMutableDictionary<id<NSObject>, void (^)(void)>* _observers; |
| NSUserDefaults* __strong _defaults; |
| float _scaleFactor; |
| } |
| |
| @synthesize scaleFactor = _scaleFactor; |
| |
| + (CursorAccessibilityScaleFactorNotifier*)sharedNotifier { |
| static dispatch_once_t once; |
| static CursorAccessibilityScaleFactorNotifier* instance; |
| dispatch_once(&once, ^{ |
| instance = [[CursorAccessibilityScaleFactorNotifier alloc] init]; |
| }); |
| return instance; |
| } |
| |
| - (instancetype)init { |
| if (self = [super init]) { |
| CHECK(base::CommandLine::ForCurrentProcess() |
| ->GetSwitchValueASCII("type") |
| .empty()); |
| |
| _observers = [[NSMutableDictionary alloc] init]; |
| _defaults = |
| [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.universalaccess"]; |
| [_defaults addObserver:self |
| forKeyPath:@"mouseDriverCursorSize" |
| options:NSKeyValueObservingOptionNew | |
| NSKeyValueObservingOptionInitial |
| context:nullptr]; |
| } |
| return self; |
| } |
| |
| - (void)observeValueForKeyPath:(NSString*)keyPath |
| ofObject:(id)object |
| change:(NSDictionary*)change |
| context:(void*)context { |
| // If this key is present, it will be a number from [1.0, 4.0], but it might |
| // not be present if it is never set, or in tests. In that case, this |
| // observation method will be called with NSNull, so the following code must |
| // handle that case. |
| NSNumber* defaultsValue = |
| base::apple::ObjCCast<NSNumber>(change[NSKeyValueChangeNewKey]); |
| _scaleFactor = std::clamp(defaultsValue.floatValue, 1.0f, 4.0f); |
| for (void (^observer)() in _observers.allValues) { |
| observer(); |
| } |
| } |
| |
| - (id<NSObject>)addObserver:(void (^)())observer { |
| static int sequence; |
| |
| // An opaque token is required to represent the observation, something that is |
| // quick and easy and compares differently to other values. A wrapped |
| // incrementing number will do. |
| id<NSObject, NSCopying> token = @(++sequence); |
| _observers[token] = observer; |
| |
| return token; |
| } |
| |
| - (void)removeObserver:(id<NSObject>)token { |
| [_observers removeObjectForKey:token]; |
| } |
| |
| namespace ui { |
| |
| float GetCursorAccessibilityScaleFactor() { |
| return CursorAccessibilityScaleFactorNotifier.sharedNotifier.scaleFactor; |
| } |
| |
| } // namespace ui |
| |
| @end |