1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/screen_orientation_controller.h"
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/constants/ash_switches.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_state_observer.h"
#include "ash/wm/window_util.h"
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/scoped_observation.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/display_util.h"
#include "chromeos/ui/base/window_properties.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/display/util/display_util.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
// The angle which the screen has to be rotated past before the display will
// rotate to match it (i.e. 45.0f is no stickiness).
const float kDisplayRotationStickyAngleDegrees = 60.0f;
// The minimum acceleration in m/s^2 in a direction required to trigger screen
// rotation. This prevents rapid toggling of rotation when the device is near
// flat and there is very little screen aligned force on it. The value is
// effectively the sine of the rise angle required times the acceleration due
// to gravity, with the current value requiring at least a 25 degree rise.
const float kMinimumAccelerationScreenRotation = 4.2f;
chromeos::OrientationType GetInternalDisplayNaturalOrientation() {
if (!display::HasInternalDisplay())
return chromeos::OrientationType::kLandscape;
return chromeos::GetDisplayNaturalOrientation(
Shell::Get()->display_manager()->GetDisplayForId(
display::Display::InternalDisplayId()));
}
// Returns the locked orientation that matches the application
// requested orientation, or the application orientation itself
// if it didn't match.
chromeos::OrientationType ResolveOrientationLock(
chromeos::OrientationType app_requested,
chromeos::OrientationType lock) {
if (app_requested == chromeos::OrientationType::kAny ||
(app_requested == chromeos::OrientationType::kLandscape &&
(lock == chromeos::OrientationType::kLandscapePrimary ||
lock == chromeos::OrientationType::kLandscapeSecondary)) ||
(app_requested == chromeos::OrientationType::kPortrait &&
(lock == chromeos::OrientationType::kPortraitPrimary ||
lock == chromeos::OrientationType::kPortraitSecondary))) {
return lock;
}
return app_requested;
}
} // namespace
chromeos::OrientationType GetCurrentScreenOrientation() {
// ScreenOrientationController might be nullptr during shutdown.
// TODO(xdai|sammiequon): See if we can reorder so that users of the function
// |SplitViewController::Get| get shutdown before screen orientation
// controller.
if (!Shell::Get()->screen_orientation_controller())
return chromeos::OrientationType::kAny;
return Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
}
bool IsCurrentScreenOrientationLandscape() {
return chromeos::IsLandscapeOrientation(GetCurrentScreenOrientation());
}
bool IsCurrentScreenOrientationPrimary() {
return chromeos::IsPrimaryOrientation(GetCurrentScreenOrientation());
}
std::ostream& operator<<(std::ostream& out,
const chromeos::OrientationType& lock) {
switch (lock) {
case chromeos::OrientationType::kAny:
out << "any";
break;
case chromeos::OrientationType::kNatural:
out << "natural";
break;
case chromeos::OrientationType::kCurrent:
out << "current";
break;
case chromeos::OrientationType::kPortrait:
out << "portrait";
break;
case chromeos::OrientationType::kLandscape:
out << "landscape";
break;
case chromeos::OrientationType::kPortraitPrimary:
out << "portrait-primary";
break;
case chromeos::OrientationType::kPortraitSecondary:
out << "portrait-secondary";
break;
case chromeos::OrientationType::kLandscapePrimary:
out << "landscape-primary";
break;
case chromeos::OrientationType::kLandscapeSecondary:
out << "landscape-secondary";
break;
}
return out;
}
// `WindowStateChangeNotifier` observes an active window state change, and
// call `ApplyLockForTopMostWindowOnInternalDisplay` when the state changes.
class ScreenOrientationController::WindowStateChangeNotifier
: public wm::ActivationChangeObserver,
public WindowStateObserver,
public aura::WindowObserver {
public:
explicit WindowStateChangeNotifier(ScreenOrientationController* controller)
: controller_(controller) {
activation_observation_.Observe(Shell::Get()->activation_client());
}
WindowStateChangeNotifier(const WindowStateChangeNotifier&) = delete;
WindowStateChangeNotifier& operator=(const WindowStateChangeNotifier&) =
delete;
~WindowStateChangeNotifier() override = default;
// wm::ActivationChangeObserver:
void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) override {
StartObservingWindowIfNeeded(gained_active);
}
// WindowStateObserver:
void OnPostWindowStateTypeChange(
WindowState* window_state,
chromeos::WindowStateType old_type) override {
controller_->ApplyLockForTopMostWindowOnInternalDisplay();
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
window_observation_.Reset();
window_state_observation_.Reset();
}
private:
void StartObservingWindowIfNeeded(aura::Window* window) {
if (window == window_observation_.GetSource()) {
return;
}
window_observation_.Reset();
window_state_observation_.Reset();
// Orphan window can not have WindowState.
if (!window || !window->parent()) {
return;
}
if (auto* const window_state = WindowState::Get(window)) {
window_observation_.Observe(window);
window_state_observation_.Observe(window_state);
}
}
const raw_ptr<ScreenOrientationController> controller_;
base::ScopedObservation<WindowState, WindowStateObserver>
window_state_observation_{this};
base::ScopedObservation<aura::Window, aura::WindowObserver>
window_observation_{this};
base::ScopedObservation<wm::ActivationClient, wm::ActivationChangeObserver>
activation_observation_{this};
};
ScreenOrientationController::ScreenOrientationController()
: natural_orientation_(GetInternalDisplayNaturalOrientation()),
ignore_display_configuration_updates_(false),
rotation_locked_(false),
rotation_locked_orientation_(chromeos::OrientationType::kAny),
user_rotation_(display::Display::ROTATE_0),
window_state_change_notifier_(
std::make_unique<WindowStateChangeNotifier>(this)) {
display_manager_observation_.Observe(Shell::Get()->display_manager());
Shell::Get()->tablet_mode_controller()->AddObserver(this);
AccelerometerReader::GetInstance()->AddObserver(this);
}
ScreenOrientationController::~ScreenOrientationController() {
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
AccelerometerReader::GetInstance()->RemoveObserver(this);
Shell::Get()->activation_client()->RemoveObserver(this);
for (auto& windows : lock_info_map_)
windows.first->RemoveObserver(this);
}
void ScreenOrientationController::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ScreenOrientationController::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ScreenOrientationController::LockOrientationForWindow(
aura::Window* requesting_window,
chromeos::OrientationType orientation_lock) {
if (!requesting_window->HasObserver(this))
requesting_window->AddObserver(this);
auto iter = lock_info_map_.find(requesting_window);
if (iter != lock_info_map_.end()) {
if (orientation_lock == chromeos::OrientationType::kCurrent) {
// If the app previously requested an orientation,
// disable the sensor when that orientation is locked.
iter->second.lock_completion_behavior =
LockCompletionBehavior::DisableSensor;
} else {
iter->second.orientation_lock = orientation_lock;
iter->second.lock_completion_behavior = LockCompletionBehavior::None;
}
} else {
lock_info_map_.emplace(
requesting_window,
LockInfo(orientation_lock, requesting_window->GetRootWindow()));
}
ApplyLockForTopMostWindowOnInternalDisplay();
}
void ScreenOrientationController::UnlockOrientationForWindow(
aura::Window* window) {
lock_info_map_.erase(window);
window->RemoveObserver(this);
ApplyLockForTopMostWindowOnInternalDisplay();
}
void ScreenOrientationController::UnlockAll() {
SetRotationLockedInternal(false);
if (user_rotation_ != GetInternalDisplayTargetRotation()) {
SetDisplayRotation(user_rotation_,
display::Display::RotationSource::ACCELEROMETER,
DisplayConfigurationController::ANIMATION_SYNC);
}
}
bool ScreenOrientationController::IsUserLockedOrientationPortrait() {
switch (user_locked_orientation_) {
case chromeos::OrientationType::kPortraitPrimary:
case chromeos::OrientationType::kPortraitSecondary:
case chromeos::OrientationType::kPortrait:
return true;
default:
return false;
}
}
chromeos::OrientationType
ScreenOrientationController::GetCurrentAppRequestedOrientationLock() const {
return current_app_requested_orientation_lock_.value_or(
chromeos::OrientationType::kAny);
}
void ScreenOrientationController::ToggleUserRotationLock() {
if (!display::HasInternalDisplay())
return;
if (user_rotation_locked()) {
SetLockToOrientation(chromeos::OrientationType::kAny);
} else {
display::Display::Rotation current_rotation =
Shell::Get()
->display_manager()
->GetDisplayInfo(display::Display::InternalDisplayId())
.GetActiveRotation();
SetLockToRotation(current_rotation);
}
}
void ScreenOrientationController::SetLockToRotation(
display::Display::Rotation rotation) {
if (!display::HasInternalDisplay())
return;
SetLockToOrientation(RotationToOrientation(natural_orientation_, rotation));
}
chromeos::OrientationType ScreenOrientationController::GetCurrentOrientation()
const {
return RotationToOrientation(natural_orientation_,
GetInternalDisplayTargetRotation());
}
bool ScreenOrientationController::IsAutoRotationAllowed() const {
return Shell::Get()
->tablet_mode_controller()
->is_in_tablet_physical_state() ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSupportsClamshellAutoRotation);
}
void ScreenOrientationController::OnWindowActivated(
::wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) {
ApplyLockForTopMostWindowOnInternalDisplay();
}
void ScreenOrientationController::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
// Window may move to an external display or back to internal (e.g. via
// shortcut). In this case, we need to undo/redo any orientation lock it
// applies on the internal display.
if (!display::HasInternalDisplay())
return;
aura::Window* window = params.receiver;
aura::Window* target = params.target;
// The target window of the hierarchy change event must be the receiving
// window itself, or one of its ancestors; we don't care about events
// happening to descendant windows, since that doesn't indicate a change of
// |window|'s root.
for (auto* curr = window; curr != target; curr = curr->parent()) {
if (!curr)
return;
}
auto iter = lock_info_map_.find(window);
if (iter != lock_info_map_.end() &&
iter->second.root_window != window->GetRootWindow()) {
iter->second.root_window = window->GetRootWindow();
ApplyLockForTopMostWindowOnInternalDisplay();
}
}
void ScreenOrientationController::OnWindowDestroying(aura::Window* window) {
UnlockOrientationForWindow(window);
}
// Currently contents::WebContents will only be able to lock rotation while
// fullscreen. In this state a user cannot click on the tab strip to change. If
// this becomes supported for non-fullscreen tabs then the following interferes
// with TabDragController. OnWindowVisibilityChanged is called between a mouse
// down and mouse up. The rotation this triggers leads to a coordinate space
// change in the middle of an event. Causes the tab to separate from the tab
// strip.
void ScreenOrientationController::OnWindowVisibilityChanged(
aura::Window* window,
bool visible) {
if (base::Contains(lock_info_map_, window))
ApplyLockForTopMostWindowOnInternalDisplay();
}
void ScreenOrientationController::OnAccelerometerUpdated(
const AccelerometerUpdate& update) {
if (!IsAutoRotationAllowed())
return;
if (rotation_locked_ && !CanRotateInLockedState())
return;
if (!update.has(ACCELEROMETER_SOURCE_SCREEN))
return;
// Ignore the reading if it appears unstable. The reading is considered
// unstable if it deviates too much from gravity
if (update.IsReadingStable(ACCELEROMETER_SOURCE_SCREEN))
HandleScreenRotation(update.get(ACCELEROMETER_SOURCE_SCREEN));
}
void ScreenOrientationController::OnDisplayTabletStateChanged(
display::TabletState state) {
switch (state) {
case display::TabletState::kEnteringTabletMode:
case display::TabletState::kExitingTabletMode:
break;
case display::TabletState::kInTabletMode:
// Observe window activation only while in UI tablet mode, since this the
// only mode in which we apply apps' requested orientation locks.
Shell::Get()->activation_client()->AddObserver(this);
if (display::HasInternalDisplay()) {
ApplyLockForTopMostWindowOnInternalDisplay();
}
break;
case display::TabletState::kInClamshellMode:
Shell::Get()->activation_client()->RemoveObserver(this);
if (!display::HasInternalDisplay()) {
break;
}
if (!IsAutoRotationAllowed()) {
// Rotation locks should have been cleared already in
// `OnTabletPhysicalStateChanged()`.
DCHECK(!rotation_locked());
DCHECK_EQ(rotation_locked_orientation_,
chromeos::OrientationType::kAny);
break;
}
// Auto-rotation is still allowed (since device is still in a physical
// tablet state). We no-longer apply app's requested orientation locks, so
// we'll call `ApplyLockForTopMostWindowOnInternalDisplay()` to apply the
// `user_locked_orientation_` if any.
ApplyLockForTopMostWindowOnInternalDisplay();
break;
}
}
void ScreenOrientationController::OnTabletPhysicalStateChanged() {
if (IsAutoRotationAllowed()) {
// Do not exit early, as the internal display can be determined after
// Maximize Mode has started. (chrome-os-partner:38796) Always start
// observing.
if (display::HasInternalDisplay()) {
user_rotation_ = GetInternalDisplayTargetRotation();
}
if (!rotation_locked_) {
LoadDisplayRotationProperties();
}
if (!display::HasInternalDisplay()) {
return;
}
ApplyLockForTopMostWindowOnInternalDisplay();
} else {
if (!display::HasInternalDisplay()) {
return;
}
UnlockAll();
}
for (auto& observer : observers_)
observer.OnUserRotationLockChanged();
}
void ScreenOrientationController::OnWillProcessDisplayChanges() {
suspend_orientation_lock_refreshes_ = true;
}
void ScreenOrientationController::OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) {
suspend_orientation_lock_refreshes_ = false;
if (is_orientation_lock_refresh_pending_) {
// Note: We must set |is_orientation_lock_refresh_pending_| to false first
// before calling `ApplyLockForTopMostWindowOnInternalDisplay()`, since
// changing the display's rotation triggers an
// `OnWillProcessDisplayChanges()` and `OnDidProcessDisplayChanges()` pair,
// and we don't want to end up here again.
is_orientation_lock_refresh_pending_ = false;
ApplyLockForTopMostWindowOnInternalDisplay();
}
}
void ScreenOrientationController::SetDisplayRotation(
display::Display::Rotation rotation,
display::Display::RotationSource source,
DisplayConfigurationController::RotationAnimation mode) {
if (!display::HasInternalDisplay())
return;
base::AutoReset<bool> auto_ignore_display_configuration_updates(
&ignore_display_configuration_updates_, true);
Shell::Get()->display_configuration_controller()->SetDisplayRotation(
display::Display::InternalDisplayId(), rotation, source, mode);
}
display::Display::Rotation
ScreenOrientationController::GetInternalDisplayTargetRotation() const {
return display::HasInternalDisplay()
? Shell::Get()
->display_configuration_controller()
->GetTargetRotation(display::Display::InternalDisplayId())
: display::Display::ROTATE_0;
}
void ScreenOrientationController::SetRotationLockedInternal(
bool rotation_locked) {
if (rotation_locked_ == rotation_locked)
return;
rotation_locked_ = rotation_locked;
if (!rotation_locked_)
rotation_locked_orientation_ = chromeos::OrientationType::kAny;
}
void ScreenOrientationController::SetLockToOrientation(
chromeos::OrientationType orientation) {
user_locked_orientation_ = orientation;
base::AutoReset<bool> auto_ignore_display_configuration_updates(
&ignore_display_configuration_updates_, true);
Shell::Get()->display_manager()->RegisterDisplayRotationProperties(
user_rotation_locked(),
chromeos::OrientationToRotation(natural_orientation_,
user_locked_orientation_));
ApplyLockForTopMostWindowOnInternalDisplay();
for (auto& observer : observers_)
observer.OnUserRotationLockChanged();
}
void ScreenOrientationController::LockRotation(
display::Display::Rotation rotation,
display::Display::RotationSource source) {
SetRotationLockedInternal(true);
SetDisplayRotation(rotation, source);
}
void ScreenOrientationController::LockRotationToOrientation(
chromeos::OrientationType lock_orientation) {
rotation_locked_orientation_ = lock_orientation;
switch (lock_orientation) {
case chromeos::OrientationType::kAny:
SetRotationLockedInternal(false);
break;
case chromeos::OrientationType::kLandscape:
case chromeos::OrientationType::kPortrait:
LockToRotationMatchingOrientation(lock_orientation);
break;
case chromeos::OrientationType::kLandscapePrimary:
case chromeos::OrientationType::kLandscapeSecondary:
case chromeos::OrientationType::kPortraitPrimary:
case chromeos::OrientationType::kPortraitSecondary:
LockRotation(chromeos::OrientationToRotation(natural_orientation_,
lock_orientation),
display::Display::RotationSource::ACTIVE);
break;
case chromeos::OrientationType::kNatural:
LockRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
break;
default:
NOTREACHED();
}
}
void ScreenOrientationController::LockToRotationMatchingOrientation(
chromeos::OrientationType lock_orientation) {
if (!display::HasInternalDisplay())
return;
display::Display::Rotation rotation =
Shell::Get()
->display_manager()
->GetDisplayInfo(display::Display::InternalDisplayId())
.GetActiveRotation();
if (natural_orientation_ == lock_orientation) {
if (rotation == display::Display::ROTATE_0 ||
rotation == display::Display::ROTATE_180) {
SetRotationLockedInternal(true);
} else {
LockRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
}
} else {
if (rotation == display::Display::ROTATE_90 ||
rotation == display::Display::ROTATE_270) {
SetRotationLockedInternal(true);
} else {
// Rotate to the default rotation of the requested orientation.
display::Display::Rotation default_rotation =
natural_orientation_ == chromeos::OrientationType::kLandscape
? display::Display::ROTATE_270 // portrait in landscape device.
: display::Display::ROTATE_90; // landscape in portrait device.
LockRotation(default_rotation, display::Display::RotationSource::ACTIVE);
}
}
}
void ScreenOrientationController::HandleScreenRotation(
const AccelerometerReading& lid) {
gfx::Vector3dF lid_flattened(lid.x, lid.y, 0.0f);
float lid_flattened_length = lid_flattened.Length();
// When the lid is close to being flat, don't change rotation as it is too
// sensitive to slight movements.
if (lid_flattened_length < kMinimumAccelerationScreenRotation)
return;
// The reference vector is the angle of gravity when the device is rotated
// clockwise by 45 degrees. Computing the angle between this vector and
// gravity we can easily determine the expected display rotation.
static constexpr gfx::Vector3dF rotation_reference(-1.0f, 1.0f, 0.0f);
// Set the down vector to match the expected direction of gravity given the
// internal display's target rotation. This is used to enforce a stickiness
// that the user must overcome to rotate the display and prevents frequent
// rotations when holding the device near 45 degrees.
display::Display::Rotation target_rotation =
GetInternalDisplayTargetRotation();
gfx::Vector3dF down(0.0f, 0.0f, 0.0f);
if (target_rotation == display::Display::ROTATE_0) {
down.set_y(1.0f);
} else if (target_rotation == display::Display::ROTATE_90) {
down.set_x(1.0f);
} else if (target_rotation == display::Display::ROTATE_180) {
down.set_y(-1.0f);
} else {
down.set_x(-1.0f);
}
// Don't rotate if the screen has not passed the threshold.
if (gfx::AngleBetweenVectorsInDegrees(down, lid_flattened) <
kDisplayRotationStickyAngleDegrees) {
return;
}
float angle = gfx::ClockwiseAngleBetweenVectorsInDegrees(
rotation_reference, lid_flattened, gfx::Vector3dF(0.0f, 0.0f, 1.0f));
display::Display::Rotation new_rotation = display::Display::ROTATE_270;
if (angle < 90.0f)
new_rotation = display::Display::ROTATE_0;
else if (angle < 180.0f)
new_rotation = display::Display::ROTATE_90;
else if (angle < 270.0f)
new_rotation = display::Display::ROTATE_180;
if (new_rotation != target_rotation &&
IsRotationAllowedInLockedState(new_rotation)) {
SetDisplayRotation(new_rotation,
display::Display::RotationSource::ACCELEROMETER);
}
}
void ScreenOrientationController::LoadDisplayRotationProperties() {
display::DisplayManager* display_manager = Shell::Get()->display_manager();
if (!display_manager->registered_internal_display_rotation_lock())
return;
user_locked_orientation_ = RotationToOrientation(
natural_orientation_,
display_manager->registered_internal_display_rotation());
}
void ScreenOrientationController::ApplyLockForTopMostWindowOnInternalDisplay() {
if (suspend_orientation_lock_refreshes_) {
is_orientation_lock_refresh_pending_ = true;
return;
}
current_app_requested_orientation_lock_ = std::nullopt;
if (!display::HasInternalDisplay())
return;
aura::Window* const internal_display_root =
Shell::GetRootWindowForDisplayId(display::Display::InternalDisplayId());
if (!internal_display_root) {
// We might have an internal display, but no root window for it, such as in
// the case of Unified Display. Also, some tests may not set an internal
// display.
// Since rotation lock is applied only on internal displays (see
// ScreenOrientationController::SetDisplayRotation()), there's no need to
// continue.
return;
}
if (!display::Screen::GetScreen()->InTabletMode()) {
if (IsAutoRotationAllowed()) {
// We ignore windows and app requested orientation locks while the UI is
// in clamshell mode when the device is physically in a tablet state.
// Instead we apply the orientation lock requested by the user.
LockRotationToOrientation(user_locked_orientation_);
}
return;
}
MruWindowTracker::WindowList mru_windows(
Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(
kActiveDesk));
for (aura::Window* window : mru_windows) {
if (window->GetRootWindow() != internal_display_root)
continue;
if (!window->TargetVisibility())
continue;
auto* const window_state = WindowState::Get(window);
// If the top visible window is snapped, we ignore any window-requested
// rotation. Here we shouldn't rely on `SplitViewController::state()` which
// can be updated in `WindowStateObserver::OnPostWindowStateTypeChange()`
// because `ApplyLockForTopMostWindowOnInternalDisplay()` is also triggered
// by the same callback.
if (window_state->IsSnapped()) {
break;
}
// We don't respect a window-requested rotation when the window is not
// maximized/fullscreen.
if (!window_state->IsMaximized() && !window_state->IsFullscreen()) {
continue;
}
if (ApplyLockForWindowIfPossible(window))
return;
}
LockRotationToOrientation(user_locked_orientation_);
}
bool ScreenOrientationController::ApplyLockForWindowIfPossible(
const aura::Window* window) {
for (auto& pair : lock_info_map_) {
const aura::Window* lock_window = pair.first;
LockInfo& lock_info = pair.second;
if (lock_window->TargetVisibility() && window->Contains(lock_window)) {
if (lock_info.orientation_lock == chromeos::OrientationType::kCurrent) {
// If the app requested "current" without previously
// specifying an orientation, use the target rotation.
lock_info.orientation_lock = RotationToOrientation(
natural_orientation_, GetInternalDisplayTargetRotation());
LockRotationToOrientation(lock_info.orientation_lock);
} else {
const auto orientation_lock = ResolveOrientationLock(
lock_info.orientation_lock, user_locked_orientation_);
LockRotationToOrientation(orientation_lock);
if (lock_info.lock_completion_behavior ==
LockCompletionBehavior::DisableSensor) {
lock_info.lock_completion_behavior = LockCompletionBehavior::None;
lock_info.orientation_lock = orientation_lock;
}
}
current_app_requested_orientation_lock_ =
std::make_optional<chromeos::OrientationType>(
lock_info.orientation_lock);
return true;
}
}
// The default orientation for all chrome browser/apps windows is
// ANY, so use the user_locked_orientation_;
if (window->GetProperty(chromeos::kAppTypeKey) !=
chromeos::AppType::NON_APP) {
LockRotationToOrientation(user_locked_orientation_);
return true;
}
return false;
}
bool ScreenOrientationController::IsRotationAllowedInLockedState(
display::Display::Rotation rotation) {
if (!rotation_locked_)
return true;
if (!CanRotateInLockedState())
return false;
if (natural_orientation_ == rotation_locked_orientation_) {
return rotation == display::Display::ROTATE_0 ||
rotation == display::Display::ROTATE_180;
}
return rotation == display::Display::ROTATE_90 ||
rotation == display::Display::ROTATE_270;
}
bool ScreenOrientationController::CanRotateInLockedState() {
return rotation_locked_orientation_ ==
chromeos::OrientationType::kLandscape ||
rotation_locked_orientation_ == chromeos::OrientationType::kPortrait;
}
void ScreenOrientationController::UpdateNaturalOrientationForTest() {
natural_orientation_ = GetInternalDisplayNaturalOrientation();
}
} // namespace ash
|