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/wm/core/compound_event_filter.h"
6
7 #include "base/containers/hash_tables.h"
8 #include "base/logging.h"
9 #include "ui/aura/client/cursor_client.h"
10 #include "ui/aura/env.h"
11 #include "ui/aura/window.h"
12 #include "ui/aura/window_delegate.h"
13 #include "ui/aura/window_event_dispatcher.h"
14 #include "ui/aura/window_tracker.h"
15 #include "ui/base/hit_test.h"
16 #include "ui/events/event.h"
17 #include "ui/wm/public/activation_client.h"
18 #include "ui/wm/public/drag_drop_client.h"
19
20 #if defined(OS_CHROMEOS) && defined(USE_X11)
21 #include "ui/events/x/touch_factory_x11.h"
22 #endif
23
24 namespace wm {
25
26 namespace {
27
28 // Returns true if the cursor should be hidden on touch events.
29 // TODO(tdanderson|rsadam): Move this function into CursorClient.
ShouldHideCursorOnTouch(const ui::TouchEvent & event)30 bool ShouldHideCursorOnTouch(const ui::TouchEvent& event) {
31 #if defined(OS_WIN)
32 return true;
33 #elif defined(OS_CHROMEOS)
34 #if defined(USE_X11)
35 int device_id = event.source_device_id();
36 if (device_id >= 0 &&
37 !ui::TouchFactory::GetInstance()->IsMultiTouchDevice(device_id)) {
38 // If the touch event is coming from a mouse-device (i.e. not a real
39 // touch-device), then do not hide the cursor.
40 return false;
41 }
42 #endif // defined(USE_X11)
43 return true;
44 #else
45 // Linux Aura does not hide the cursor on touch by default.
46 // TODO(tdanderson): Change this if having consistency across
47 // all platforms which use Aura is desired.
48 return false;
49 #endif
50 }
51
52 } // namespace
53
54 ////////////////////////////////////////////////////////////////////////////////
55 // CompoundEventFilter, public:
56
CompoundEventFilter()57 CompoundEventFilter::CompoundEventFilter() {
58 }
59
~CompoundEventFilter()60 CompoundEventFilter::~CompoundEventFilter() {
61 // Additional filters are not owned by CompoundEventFilter and they
62 // should all be removed when running here. |handlers_| has
63 // check_empty == true and will DCHECK failure if it is not empty.
64 }
65
66 // static
CursorForWindowComponent(int window_component)67 gfx::NativeCursor CompoundEventFilter::CursorForWindowComponent(
68 int window_component) {
69 switch (window_component) {
70 case HTBOTTOM:
71 return ui::kCursorSouthResize;
72 case HTBOTTOMLEFT:
73 return ui::kCursorSouthWestResize;
74 case HTBOTTOMRIGHT:
75 return ui::kCursorSouthEastResize;
76 case HTLEFT:
77 return ui::kCursorWestResize;
78 case HTRIGHT:
79 return ui::kCursorEastResize;
80 case HTTOP:
81 return ui::kCursorNorthResize;
82 case HTTOPLEFT:
83 return ui::kCursorNorthWestResize;
84 case HTTOPRIGHT:
85 return ui::kCursorNorthEastResize;
86 default:
87 return ui::kCursorNull;
88 }
89 }
90
AddHandler(ui::EventHandler * handler)91 void CompoundEventFilter::AddHandler(ui::EventHandler* handler) {
92 handlers_.AddObserver(handler);
93 }
94
RemoveHandler(ui::EventHandler * handler)95 void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) {
96 handlers_.RemoveObserver(handler);
97 }
98
99 ////////////////////////////////////////////////////////////////////////////////
100 // CompoundEventFilter, private:
101
UpdateCursor(aura::Window * target,ui::MouseEvent * event)102 void CompoundEventFilter::UpdateCursor(aura::Window* target,
103 ui::MouseEvent* event) {
104 // If drag and drop is in progress, let the drag drop client set the cursor
105 // instead of setting the cursor here.
106 aura::Window* root_window = target->GetRootWindow();
107 aura::client::DragDropClient* drag_drop_client =
108 aura::client::GetDragDropClient(root_window);
109 if (drag_drop_client && drag_drop_client->IsDragDropInProgress())
110 return;
111
112 aura::client::CursorClient* cursor_client =
113 aura::client::GetCursorClient(root_window);
114 if (cursor_client) {
115 gfx::NativeCursor cursor = target->GetCursor(event->location());
116 if ((event->flags() & ui::EF_IS_NON_CLIENT)) {
117 if (target->delegate()) {
118 int window_component =
119 target->delegate()->GetNonClientComponent(event->location());
120 cursor = CursorForWindowComponent(window_component);
121 } else {
122 // Allow the OS to handle non client cursors if we don't have a
123 // a delegate to handle the non client hittest.
124 return;
125 }
126 }
127 cursor_client->SetCursor(cursor);
128 }
129 }
130
FilterKeyEvent(ui::KeyEvent * event)131 void CompoundEventFilter::FilterKeyEvent(ui::KeyEvent* event) {
132 if (handlers_.might_have_observers()) {
133 ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
134 ui::EventHandler* handler;
135 while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
136 handler->OnKeyEvent(event);
137 }
138 }
139
FilterMouseEvent(ui::MouseEvent * event)140 void CompoundEventFilter::FilterMouseEvent(ui::MouseEvent* event) {
141 if (handlers_.might_have_observers()) {
142 ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
143 ui::EventHandler* handler;
144 while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
145 handler->OnMouseEvent(event);
146 }
147 }
148
FilterTouchEvent(ui::TouchEvent * event)149 void CompoundEventFilter::FilterTouchEvent(ui::TouchEvent* event) {
150 if (handlers_.might_have_observers()) {
151 ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
152 ui::EventHandler* handler;
153 while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
154 handler->OnTouchEvent(event);
155 }
156 }
157
SetCursorVisibilityOnEvent(aura::Window * target,ui::Event * event,bool show)158 void CompoundEventFilter::SetCursorVisibilityOnEvent(aura::Window* target,
159 ui::Event* event,
160 bool show) {
161 if (event->flags() & ui::EF_IS_SYNTHESIZED)
162 return;
163
164 aura::client::CursorClient* client =
165 aura::client::GetCursorClient(target->GetRootWindow());
166 if (!client)
167 return;
168
169 if (show)
170 client->ShowCursor();
171 else
172 client->HideCursor();
173 }
174
SetMouseEventsEnableStateOnEvent(aura::Window * target,ui::Event * event,bool enable)175 void CompoundEventFilter::SetMouseEventsEnableStateOnEvent(aura::Window* target,
176 ui::Event* event,
177 bool enable) {
178 if (event->flags() & ui::EF_IS_SYNTHESIZED)
179 return;
180 aura::client::CursorClient* client =
181 aura::client::GetCursorClient(target->GetRootWindow());
182 if (!client)
183 return;
184
185 if (enable)
186 client->EnableMouseEvents();
187 else
188 client->DisableMouseEvents();
189 }
190
191 ////////////////////////////////////////////////////////////////////////////////
192 // CompoundEventFilter, ui::EventHandler implementation:
193
OnKeyEvent(ui::KeyEvent * event)194 void CompoundEventFilter::OnKeyEvent(ui::KeyEvent* event) {
195 aura::Window* target = static_cast<aura::Window*>(event->target());
196 aura::client::CursorClient* client =
197 aura::client::GetCursorClient(target->GetRootWindow());
198 if (client && client->ShouldHideCursorOnKeyEvent(*event))
199 SetCursorVisibilityOnEvent(target, event, false);
200
201 FilterKeyEvent(event);
202 }
203
OnMouseEvent(ui::MouseEvent * event)204 void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) {
205 aura::Window* window = static_cast<aura::Window*>(event->target());
206 aura::WindowTracker window_tracker;
207 window_tracker.Add(window);
208
209 // We must always update the cursor, otherwise the cursor can get stuck if an
210 // event filter registered with us consumes the event.
211 // It should also update the cursor for clicking and wheels for ChromeOS boot.
212 // When ChromeOS is booted, it hides the mouse cursor but immediate mouse
213 // operation will show the cursor.
214 // We also update the cursor for mouse enter in case a mouse cursor is sent to
215 // outside of the root window and moved back for some reasons (e.g. running on
216 // on Desktop for testing, or a bug in pointer barrier).
217 if (!(event->flags() & ui::EF_FROM_TOUCH) &&
218 (event->type() == ui::ET_MOUSE_ENTERED ||
219 event->type() == ui::ET_MOUSE_MOVED ||
220 event->type() == ui::ET_MOUSE_PRESSED ||
221 event->type() == ui::ET_MOUSEWHEEL)) {
222 SetMouseEventsEnableStateOnEvent(window, event, true);
223 SetCursorVisibilityOnEvent(window, event, true);
224 UpdateCursor(window, event);
225 }
226
227 FilterMouseEvent(event);
228 }
229
OnScrollEvent(ui::ScrollEvent * event)230 void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) {
231 }
232
OnTouchEvent(ui::TouchEvent * event)233 void CompoundEventFilter::OnTouchEvent(ui::TouchEvent* event) {
234 FilterTouchEvent(event);
235 if (!event->handled() && event->type() == ui::ET_TOUCH_PRESSED &&
236 ShouldHideCursorOnTouch(*event) &&
237 !aura::Env::GetInstance()->IsMouseButtonDown()) {
238 SetMouseEventsEnableStateOnEvent(
239 static_cast<aura::Window*>(event->target()), event, false);
240 }
241 }
242
OnGestureEvent(ui::GestureEvent * event)243 void CompoundEventFilter::OnGestureEvent(ui::GestureEvent* event) {
244 if (handlers_.might_have_observers()) {
245 ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
246 ui::EventHandler* handler;
247 while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
248 handler->OnGestureEvent(event);
249 }
250
251 #if defined(OS_WIN)
252 // A Win8 edge swipe event is a special event that does not have location
253 // information associated with it, and is not preceeded by an ET_TOUCH_PRESSED
254 // event. So we treat it specially here.
255 if (!event->handled() && event->type() == ui::ET_GESTURE_WIN8_EDGE_SWIPE &&
256 !aura::Env::GetInstance()->IsMouseButtonDown()) {
257 SetMouseEventsEnableStateOnEvent(
258 static_cast<aura::Window*>(event->target()), event, false);
259 }
260 #endif
261 }
262
263 } // namespace wm
264