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 24namespace wm { 25 26namespace { 27 28// Returns true if the cursor should be hidden on touch events. 29// TODO(tdanderson|rsadam): Move this function into CursorClient. 30bool 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 57CompoundEventFilter::CompoundEventFilter() { 58} 59 60CompoundEventFilter::~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 67gfx::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 91void CompoundEventFilter::AddHandler(ui::EventHandler* handler) { 92 handlers_.AddObserver(handler); 93} 94 95void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) { 96 handlers_.RemoveObserver(handler); 97} 98 99//////////////////////////////////////////////////////////////////////////////// 100// CompoundEventFilter, private: 101 102void 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 131void 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 140void 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 149void 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 158void 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 175void 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 194void 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 204void 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 230void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) { 231} 232 233void 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 243void 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