// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <UIKit/UIKit.h>
#include "base/apple/foundation_util.h"
#include "base/check.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "build/ios_buildflags.h"
#include "ui/display/display.h"
#include "ui/display/display_features.h"
#include "ui/display/screen_base.h"
#include "ui/gfx/native_widget_types.h"
namespace display {
namespace {
class ScreenNotification {
public:
virtual void ScreenChanged() = 0;
};
} // namespace
} // namespace display
@interface ScreenObserver : NSObject {
raw_ptr<display::ScreenNotification> _notifier;
}
- (void)mainScreenChanged;
@end
@implementation ScreenObserver
- (instancetype)initWithNotifier:(display::ScreenNotification*)notifier {
if ((self = [super init])) {
_notifier = notifier;
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(mainScreenChanged)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[defaultCenter addObserver:self
selector:@selector(mainScreenChanged)
name:UIWindowDidBecomeKeyNotification
object:nil];
}
return self;
}
- (void)mainScreenChanged {
if (!base::SingleThreadTaskRunner::HasCurrentDefault()) {
return;
}
// This notification comes before UIScreen can change its bounds so post a
// task so the update occurs after the UIScreen has been updated.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&display::ScreenNotification::ScreenChanged,
base::Unretained(_notifier)));
}
@end
namespace display {
namespace {
class ScreenIos : public ScreenBase, public ScreenNotification {
public:
ScreenIos() {
observer_ = [[ScreenObserver alloc] initWithNotifier:this];
ScreenChanged();
}
ScreenIos(const ScreenIos&) = delete;
ScreenIos& operator=(const ScreenIos&) = delete;
void ScreenChanged() override {
UIScreen* screen = GetAllActiveScreens().firstObject;
if (!screen) {
return;
}
Display display(0, gfx::Rect(screen.bounds));
CGFloat scale = [screen scale];
if (Display::HasForceDeviceScaleFactor()) {
scale = Display::GetForcedDeviceScaleFactor();
}
display.set_device_scale_factor(scale);
ProcessDisplayChanged(display, true /* is_primary */);
}
gfx::Point GetCursorScreenPoint() override {
NOTIMPLEMENTED();
return gfx::Point(0, 0);
}
bool IsWindowUnderCursor(gfx::NativeWindow window) override {
NOTIMPLEMENTED();
return false;
}
gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override {
NOTIMPLEMENTED();
return gfx::NativeWindow();
}
int GetNumDisplays() const override {
return std::max(static_cast<int>([GetAllActiveScreens() count]), 1);
}
private:
// Return all screens associated with scenes of the application.
NSArray<UIScreen*>* GetAllActiveScreens() const {
#if BUILDFLAG(IS_IOS_APP_EXTENSION)
return [NSArray<UIScreen*> array];
#else
NSMutableSet<UIScreen*>* screens = [NSMutableSet set];
for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) {
UIWindowScene* windowScene =
base::apple::ObjCCastStrict<UIWindowScene>(scene);
UIScreen* screen = windowScene.keyWindow.screen;
if (screen) {
[screens addObject:screen];
}
}
return [screens allObjects];
#endif
}
ScreenObserver* __strong observer_;
};
} // namespace
// static
gfx::NativeWindow Screen::GetWindowForView(gfx::NativeView view) {
return gfx::NativeWindow(view.Get().window);
}
Screen* CreateNativeScreen() {
return new ScreenIos;
}
} // namespace display