Chloe Pelling | a0e11359d | 2020-08-27 07:26:57 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "components/exo/ui_lock_controller.h" |
| 6 | |
| 7 | #include "ash/public/cpp/app_types.h" |
| 8 | #include "ash/public/cpp/window_properties.h" |
| 9 | #include "ash/wm/window_state.h" |
| 10 | #include "base/time/time.h" |
| 11 | #include "base/timer/timer.h" |
| 12 | #include "components/exo/seat.h" |
| 13 | #include "components/exo/shell_surface_util.h" |
| 14 | #include "components/exo/surface.h" |
| 15 | #include "components/exo/wm_helper.h" |
| 16 | #include "ui/aura/client/aura_constants.h" |
| 17 | #include "ui/aura/window.h" |
| 18 | #include "ui/events/event_constants.h" |
| 19 | #include "ui/events/keycodes/dom/dom_code.h" |
| 20 | #include "ui/views/widget/widget.h" |
| 21 | |
| 22 | namespace exo { |
| 23 | |
| 24 | constexpr auto kLongPressEscapeDuration = base::TimeDelta::FromSeconds(2); |
| 25 | constexpr auto kExcludedFlags = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | |
| 26 | ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN | |
| 27 | ui::EF_ALTGR_DOWN | ui::EF_IS_REPEAT; |
| 28 | |
| 29 | UILockController::UILockController(Seat* seat) : seat_(seat) { |
| 30 | WMHelper::GetInstance()->AddPreTargetHandler(this); |
| 31 | seat_->AddObserver(this); |
| 32 | } |
| 33 | |
| 34 | UILockController::~UILockController() { |
| 35 | seat_->RemoveObserver(this); |
| 36 | WMHelper::GetInstance()->RemovePreTargetHandler(this); |
| 37 | } |
| 38 | |
| 39 | void UILockController::OnKeyEvent(ui::KeyEvent* event) { |
| 40 | // If the event target is not an exo::Surface, let another handler process the |
| 41 | // event. |
| 42 | if (!GetShellMainSurface(static_cast<aura::Window*>(event->target())) && |
| 43 | !Surface::AsSurface(static_cast<aura::Window*>(event->target()))) { |
| 44 | return; |
| 45 | } |
| 46 | |
| 47 | if (event->code() == ui::DomCode::ESCAPE && |
| 48 | (event->flags() & kExcludedFlags) == 0) { |
| 49 | OnEscapeKey(event->type() == ui::ET_KEY_PRESSED); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | void UILockController::OnSurfaceFocused(Surface* gained_focus) { |
| 54 | if (gained_focus != focused_surface_to_unlock_) |
| 55 | StopTimer(); |
| 56 | } |
| 57 | |
| 58 | namespace { |
| 59 | bool FocusedWindowIsNonImmersiveFullscreen(Seat* seat) { |
| 60 | auto* surface = seat->GetFocusedSurface(); |
| 61 | if (!surface) |
| 62 | return false; |
| 63 | |
| 64 | auto* widget = |
| 65 | views::Widget::GetTopLevelWidgetForNativeView(surface->window()); |
| 66 | if (!widget) |
| 67 | return false; |
| 68 | |
| 69 | aura::Window* window = widget->GetNativeWindow(); |
| 70 | if (!window || window->GetProperty(ash::kImmersiveImpliedByFullscreen)) |
| 71 | return false; |
| 72 | |
| 73 | // TODO(b/165865831): Add the Borealis AppType if/when we add one. |
| 74 | if (window->GetProperty(aura::client::kAppType) != |
| 75 | static_cast<int>(ash::AppType::CROSTINI_APP)) { |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | auto* window_state = ash::WindowState::Get(window); |
| 80 | return window_state && window_state->IsFullscreen(); |
| 81 | } |
| 82 | } // namespace |
| 83 | |
| 84 | void UILockController::OnEscapeKey(bool pressed) { |
| 85 | if (pressed) { |
| 86 | if (FocusedWindowIsNonImmersiveFullscreen(seat_) && |
| 87 | !exit_fullscreen_timer_.IsRunning()) { |
| 88 | focused_surface_to_unlock_ = seat_->GetFocusedSurface(); |
| 89 | exit_fullscreen_timer_.Start( |
| 90 | FROM_HERE, kLongPressEscapeDuration, |
| 91 | base::BindOnce(&UILockController::OnEscapeHeld, |
| 92 | base::Unretained(this))); |
| 93 | } |
| 94 | } else { |
| 95 | StopTimer(); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | void UILockController::OnEscapeHeld() { |
| 100 | auto* surface = seat_->GetFocusedSurface(); |
| 101 | if (!surface || surface != focused_surface_to_unlock_) { |
| 102 | focused_surface_to_unlock_ = nullptr; |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | focused_surface_to_unlock_ = nullptr; |
| 107 | |
| 108 | auto* widget = |
| 109 | views::Widget::GetTopLevelWidgetForNativeView(surface->window()); |
| 110 | auto* window_state = |
| 111 | ash::WindowState::Get(widget ? widget->GetNativeWindow() : nullptr); |
| 112 | if (window_state) |
| 113 | window_state->Minimize(); |
| 114 | } |
| 115 | |
| 116 | void UILockController::StopTimer() { |
| 117 | if (exit_fullscreen_timer_.IsRunning()) { |
| 118 | exit_fullscreen_timer_.Stop(); |
| 119 | focused_surface_to_unlock_ = nullptr; |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | } // namespace exo |