blob: ef4a1605a468c84f7accc89ed762eded0bbb6fe5 [file] [log] [blame]
Chloe Pellinga0e11359d2020-08-27 07:26:571// 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
22namespace exo {
23
24constexpr auto kLongPressEscapeDuration = base::TimeDelta::FromSeconds(2);
25constexpr 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
29UILockController::UILockController(Seat* seat) : seat_(seat) {
30 WMHelper::GetInstance()->AddPreTargetHandler(this);
31 seat_->AddObserver(this);
32}
33
34UILockController::~UILockController() {
35 seat_->RemoveObserver(this);
36 WMHelper::GetInstance()->RemovePreTargetHandler(this);
37}
38
39void 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
53void UILockController::OnSurfaceFocused(Surface* gained_focus) {
54 if (gained_focus != focused_surface_to_unlock_)
55 StopTimer();
56}
57
58namespace {
59bool 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
84void 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
99void 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
116void 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