compound_event_filter.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright (c) 2012 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 "ui/views/corewm/compound_event_filter.h"
6
7#include "base/containers/hash_tables.h"
8#include "base/logging.h"
9#include "ui/aura/client/activation_client.h"
10#include "ui/aura/client/cursor_client.h"
11#include "ui/aura/client/drag_drop_client.h"
12#include "ui/aura/env.h"
13#include "ui/aura/root_window.h"
14#include "ui/aura/window_delegate.h"
15#include "ui/aura/window_tracker.h"
16#include "ui/base/hit_test.h"
17#include "ui/events/event.h"
18
19namespace views {
20namespace corewm {
21
22namespace {
23
24bool ShouldHideCursorOnKeyEvent(const ui::KeyEvent& event) {
25#if defined(OS_CHROMEOS)
26  // All alt and control key commands are ignored.
27  if (event.IsAltDown() || event.IsControlDown())
28    return false;
29
30  static bool inited = false;
31  static base::hash_set<int32> ignored_keys;
32  if (!inited) {
33    // Modifiers.
34    ignored_keys.insert(ui::VKEY_SHIFT);
35    ignored_keys.insert(ui::VKEY_CONTROL);
36    ignored_keys.insert(ui::VKEY_MENU);
37
38    // Search key == VKEY_LWIN.
39    ignored_keys.insert(ui::VKEY_LWIN);
40
41    // Function keys.
42    for (int key = ui::VKEY_F1; key <= ui::VKEY_F24; ++key)
43      ignored_keys.insert(key);
44
45    // Media keys.
46    for (int key = ui::VKEY_BROWSER_BACK; key <= ui::VKEY_MEDIA_LAUNCH_APP2;
47         ++key) {
48      ignored_keys.insert(key);
49    }
50
51#if defined(OS_POSIX)
52    ignored_keys.insert(ui::VKEY_WLAN);
53    ignored_keys.insert(ui::VKEY_POWER);
54    ignored_keys.insert(ui::VKEY_BRIGHTNESS_DOWN);
55    ignored_keys.insert(ui::VKEY_BRIGHTNESS_UP);
56    ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_DOWN);
57    ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_UP);
58#endif
59
60    inited = true;
61  }
62
63  if (ignored_keys.count(event.key_code()) > 0)
64    return false;
65
66  return true;
67#else  // !defined(OS_CHROMEOS)
68  return false;
69#endif  // defined(OS_CHROMEOS)
70}
71
72}  // namespace
73
74////////////////////////////////////////////////////////////////////////////////
75// CompoundEventFilter, public:
76
77CompoundEventFilter::CompoundEventFilter() : cursor_hidden_by_filter_(false) {
78}
79
80CompoundEventFilter::~CompoundEventFilter() {
81  // Additional filters are not owned by CompoundEventFilter and they
82  // should all be removed when running here. |handlers_| has
83  // check_empty == true and will DCHECK failure if it is not empty.
84}
85
86// static
87gfx::NativeCursor CompoundEventFilter::CursorForWindowComponent(
88    int window_component) {
89  switch (window_component) {
90    case HTBOTTOM:
91      return ui::kCursorSouthResize;
92    case HTBOTTOMLEFT:
93      return ui::kCursorSouthWestResize;
94    case HTBOTTOMRIGHT:
95      return ui::kCursorSouthEastResize;
96    case HTLEFT:
97      return ui::kCursorWestResize;
98    case HTRIGHT:
99      return ui::kCursorEastResize;
100    case HTTOP:
101      return ui::kCursorNorthResize;
102    case HTTOPLEFT:
103      return ui::kCursorNorthWestResize;
104    case HTTOPRIGHT:
105      return ui::kCursorNorthEastResize;
106    default:
107      return ui::kCursorNull;
108  }
109}
110
111void CompoundEventFilter::AddHandler(ui::EventHandler* handler) {
112  handlers_.AddObserver(handler);
113}
114
115void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) {
116  handlers_.RemoveObserver(handler);
117}
118
119////////////////////////////////////////////////////////////////////////////////
120// CompoundEventFilter, private:
121
122void CompoundEventFilter::UpdateCursor(aura::Window* target,
123                                       ui::MouseEvent* event) {
124  // If drag and drop is in progress, let the drag drop client set the cursor
125  // instead of setting the cursor here.
126  aura::RootWindow* root_window = target->GetRootWindow();
127  aura::client::DragDropClient* drag_drop_client =
128      aura::client::GetDragDropClient(root_window);
129  if (drag_drop_client && drag_drop_client->IsDragDropInProgress())
130    return;
131
132  aura::client::CursorClient* cursor_client =
133      aura::client::GetCursorClient(root_window);
134  if (cursor_client) {
135    gfx::NativeCursor cursor = target->GetCursor(event->location());
136    if (event->flags() & ui::EF_IS_NON_CLIENT) {
137      int window_component =
138          target->delegate()->GetNonClientComponent(event->location());
139      cursor = CursorForWindowComponent(window_component);
140    }
141
142    cursor_client->SetCursor(cursor);
143  }
144}
145
146void CompoundEventFilter::FilterKeyEvent(ui::KeyEvent* event) {
147  if (handlers_.might_have_observers()) {
148    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
149    ui::EventHandler* handler;
150    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
151      handler->OnKeyEvent(event);
152  }
153}
154
155void CompoundEventFilter::FilterMouseEvent(ui::MouseEvent* event) {
156  if (handlers_.might_have_observers()) {
157    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
158    ui::EventHandler* handler;
159    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
160      handler->OnMouseEvent(event);
161  }
162}
163
164void CompoundEventFilter::FilterTouchEvent(ui::TouchEvent* event) {
165  if (handlers_.might_have_observers()) {
166    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
167    ui::EventHandler* handler;
168    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
169      handler->OnTouchEvent(event);
170  }
171}
172
173void CompoundEventFilter::SetCursorVisibilityOnEvent(aura::Window* target,
174                                                     ui::Event* event,
175                                                     bool show) {
176  if (event->flags() & ui::EF_IS_SYNTHESIZED)
177    return;
178
179  aura::client::CursorClient* client =
180      aura::client::GetCursorClient(target->GetRootWindow());
181  if (!client)
182    return;
183
184  if (show && cursor_hidden_by_filter_) {
185    cursor_hidden_by_filter_ = false;
186    client->ShowCursor();
187  } else if (!show && !cursor_hidden_by_filter_) {
188    cursor_hidden_by_filter_ = true;
189    client->HideCursor();
190  } else if (show && !client->IsCursorVisible() && !client->IsCursorLocked()) {
191    // TODO(tdanderson): Remove this temporary logging once the issues related
192    // to a disappearing mouse cursor on the Pixel login screen / Pixel
193    // wakeup have been resolved. See crbug.com/275826.
194    LOG(ERROR) << "Event of type " << event->type() << " did not show cursor."
195               << " Mouse enabled state is " << client->IsMouseEventsEnabled();
196  }
197}
198
199void CompoundEventFilter::SetMouseEventsEnableStateOnEvent(aura::Window* target,
200                                                           ui::Event* event,
201                                                           bool enable) {
202  if (event->flags() & ui::EF_IS_SYNTHESIZED)
203    return;
204  aura::client::CursorClient* client =
205      aura::client::GetCursorClient(target->GetRootWindow());
206  if (!client)
207    return;
208
209  if (enable)
210    client->EnableMouseEvents();
211  else
212    client->DisableMouseEvents();
213}
214
215////////////////////////////////////////////////////////////////////////////////
216// CompoundEventFilter, ui::EventHandler implementation:
217
218void CompoundEventFilter::OnKeyEvent(ui::KeyEvent* event) {
219  if (ShouldHideCursorOnKeyEvent(*event)) {
220    SetCursorVisibilityOnEvent(
221        static_cast<aura::Window*>(event->target()), event, false);
222  }
223
224  FilterKeyEvent(event);
225}
226
227void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) {
228  aura::Window* window = static_cast<aura::Window*>(event->target());
229  aura::WindowTracker window_tracker;
230  window_tracker.Add(window);
231
232  // We must always update the cursor, otherwise the cursor can get stuck if an
233  // event filter registered with us consumes the event.
234  // It should also update the cursor for clicking and wheels for ChromeOS boot.
235  // When ChromeOS is booted, it hides the mouse cursor but immediate mouse
236  // operation will show the cursor.
237  // We also update the cursor for mouse enter in case a mouse cursor is sent to
238  // outside of the root window and moved back for some reasons (e.g. running on
239  // on Desktop for testing, or a bug in pointer barrier).
240  if (event->type() == ui::ET_MOUSE_ENTERED ||
241      event->type() == ui::ET_MOUSE_MOVED ||
242      event->type() == ui::ET_MOUSE_PRESSED ||
243      event->type() == ui::ET_MOUSEWHEEL) {
244    SetMouseEventsEnableStateOnEvent(window, event, true);
245    SetCursorVisibilityOnEvent(window, event, true);
246    UpdateCursor(window, event);
247  }
248
249  FilterMouseEvent(event);
250}
251
252void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) {
253}
254
255void CompoundEventFilter::OnTouchEvent(ui::TouchEvent* event) {
256  FilterTouchEvent(event);
257  if (!event->handled() && event->type() == ui::ET_TOUCH_PRESSED &&
258      !aura::Env::GetInstance()->IsMouseButtonDown()) {
259    SetMouseEventsEnableStateOnEvent(
260        static_cast<aura::Window*>(event->target()), event, false);
261  }
262}
263
264void CompoundEventFilter::OnGestureEvent(ui::GestureEvent* event) {
265  if (handlers_.might_have_observers()) {
266    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
267    ui::EventHandler* handler;
268    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
269      handler->OnGestureEvent(event);
270  }
271}
272
273}  // namespace corewm
274}  // namespace views
275