1 // Copyright 2014 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 "ash/host/ash_window_tree_host_x11.h" 6 7 #include <X11/extensions/Xfixes.h> 8 #include <X11/extensions/XInput2.h> 9 #include <X11/Xatom.h> 10 #include <X11/Xlib.h> 11 12 #include <string> 13 #include <vector> 14 15 #include "ash/host/ash_window_tree_host_init_params.h" 16 #include "ash/host/root_window_transformer.h" 17 #include "base/basictypes.h" 18 #include "base/sys_info.h" 19 #include "ui/aura/client/screen_position_client.h" 20 #include "ui/aura/env.h" 21 #include "ui/aura/window.h" 22 #include "ui/aura/window_event_dispatcher.h" 23 #include "ui/base/x/x11_util.h" 24 #include "ui/events/device_data_manager.h" 25 #include "ui/events/event.h" 26 #include "ui/events/event_utils.h" 27 #include "ui/events/platform/platform_event_source.h" 28 #include "ui/events/x/device_list_cache_x.h" 29 #include "ui/events/x/touch_factory_x11.h" 30 #include "ui/gfx/rect.h" 31 #include "ui/gfx/screen.h" 32 33 namespace ash { 34 35 AshWindowTreeHostX11::AshWindowTreeHostX11(const gfx::Rect& initial_bounds) 36 : WindowTreeHostX11(initial_bounds), 37 transformer_helper_(this), 38 display_ids_(std::make_pair(gfx::Display::kInvalidDisplayID, 39 gfx::Display::kInvalidDisplayID)) { 40 aura::Env::GetInstance()->AddObserver(this); 41 } 42 43 AshWindowTreeHostX11::~AshWindowTreeHostX11() { 44 aura::Env::GetInstance()->RemoveObserver(this); 45 UnConfineCursor(); 46 } 47 48 void AshWindowTreeHostX11::ToggleFullScreen() { NOTIMPLEMENTED(); } 49 50 bool AshWindowTreeHostX11::ConfineCursorToRootWindow() { 51 #if XFIXES_MAJOR >= 5 52 DCHECK(!pointer_barriers_.get()); 53 if (pointer_barriers_) 54 return false; 55 pointer_barriers_.reset(new XID[4]); 56 gfx::Rect barrier(bounds()); 57 barrier.Inset(transformer_helper_.GetHostInsets()); 58 // Horizontal, top barriers. 59 pointer_barriers_[0] = XFixesCreatePointerBarrier(xdisplay(), 60 x_root_window(), 61 barrier.x(), 62 barrier.y(), 63 barrier.right(), 64 barrier.y(), 65 BarrierPositiveY, 66 0, 67 XIAllDevices); 68 // Horizontal, bottom barriers. 69 pointer_barriers_[1] = XFixesCreatePointerBarrier(xdisplay(), 70 x_root_window(), 71 barrier.x(), 72 barrier.bottom(), 73 barrier.right(), 74 barrier.bottom(), 75 BarrierNegativeY, 76 0, 77 XIAllDevices); 78 // Vertical, left barriers. 79 pointer_barriers_[2] = XFixesCreatePointerBarrier(xdisplay(), 80 x_root_window(), 81 barrier.x(), 82 barrier.y(), 83 barrier.x(), 84 barrier.bottom(), 85 BarrierPositiveX, 86 0, 87 XIAllDevices); 88 // Vertical, right barriers. 89 pointer_barriers_[3] = XFixesCreatePointerBarrier(xdisplay(), 90 x_root_window(), 91 barrier.right(), 92 barrier.y(), 93 barrier.right(), 94 barrier.bottom(), 95 BarrierNegativeX, 96 0, 97 XIAllDevices); 98 #endif 99 return true; 100 } 101 102 void AshWindowTreeHostX11::UnConfineCursor() { 103 #if XFIXES_MAJOR >= 5 104 if (pointer_barriers_) { 105 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[0]); 106 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[1]); 107 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[2]); 108 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[3]); 109 pointer_barriers_.reset(); 110 } 111 #endif 112 } 113 114 void AshWindowTreeHostX11::SetRootWindowTransformer( 115 scoped_ptr<RootWindowTransformer> transformer) { 116 transformer_helper_.SetRootWindowTransformer(transformer.Pass()); 117 if (pointer_barriers_) { 118 UnConfineCursor(); 119 ConfineCursorToRootWindow(); 120 } 121 } 122 123 gfx::Insets AshWindowTreeHostX11::GetHostInsets() const { 124 return transformer_helper_.GetHostInsets(); 125 } 126 127 aura::WindowTreeHost* AshWindowTreeHostX11::AsWindowTreeHost() { return this; } 128 129 void AshWindowTreeHostX11::UpdateDisplayID(int64 id1, int64 id2) { 130 display_ids_.first = id1; 131 display_ids_.second = id2; 132 } 133 134 void AshWindowTreeHostX11::PrepareForShutdown() { 135 if (ui::PlatformEventSource::GetInstance()) 136 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 137 } 138 139 void AshWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) { 140 WindowTreeHostX11::SetBounds(bounds); 141 if (pointer_barriers_) { 142 UnConfineCursor(); 143 ConfineCursorToRootWindow(); 144 } 145 } 146 147 gfx::Transform AshWindowTreeHostX11::GetRootTransform() const { 148 return transformer_helper_.GetTransform(); 149 } 150 151 void AshWindowTreeHostX11::SetRootTransform(const gfx::Transform& transform) { 152 transformer_helper_.SetTransform(transform); 153 } 154 155 gfx::Transform AshWindowTreeHostX11::GetInverseRootTransform() const { 156 return transformer_helper_.GetInverseTransform(); 157 } 158 159 void AshWindowTreeHostX11::UpdateRootWindowSize(const gfx::Size& host_size) { 160 transformer_helper_.UpdateWindowSize(host_size); 161 } 162 163 void AshWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) { 164 #if defined(OS_CHROMEOS) 165 SetCrOSTapPaused(!show); 166 #endif 167 } 168 169 void AshWindowTreeHostX11::OnWindowInitialized(aura::Window* window) {} 170 171 void AshWindowTreeHostX11::OnHostInitialized(aura::WindowTreeHost* host) { 172 if (host != AsWindowTreeHost()) 173 return; 174 175 #if defined(OS_CHROMEOS) 176 // We have to enable Tap-to-click by default because the cursor is set to 177 // visible in Shell::InitRootWindowController. 178 SetCrOSTapPaused(false); 179 #endif 180 } 181 182 void AshWindowTreeHostX11::OnConfigureNotify() { 183 // Always update barrier and mouse location because |bounds_| might 184 // have already been updated in |SetBounds|. 185 if (pointer_barriers_) { 186 UnConfineCursor(); 187 ConfineCursorToRootWindow(); 188 } 189 } 190 191 bool AshWindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) { 192 if(!WindowTreeHostX11::CanDispatchEvent(event)) 193 return false; 194 XEvent* xev = event; 195 ui::EventType type = ui::EventTypeFromNative(xev); 196 // For touch event, check if the root window is residing on the according 197 // touch display. 198 switch (type) { 199 case ui::ET_TOUCH_MOVED: 200 case ui::ET_TOUCH_PRESSED: 201 case ui::ET_TOUCH_CANCELLED: 202 case ui::ET_TOUCH_RELEASED: { 203 #if defined(OS_CHROMEOS) 204 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data); 205 int64 touch_display_id = 206 ui::DeviceDataManager::GetInstance()->GetDisplayForTouchDevice( 207 xiev->deviceid); 208 // If we don't have record of display id for this touch device, check 209 // that if the event is within the bound of the root window. Note 210 // that in multi-monitor case, the event position is in framebuffer 211 // space so the bounds check will not work so well. 212 if (touch_display_id == gfx::Display::kInvalidDisplayID) { 213 if (base::SysInfo::IsRunningOnChromeOS() && 214 !bounds().Contains(ui::EventLocationFromNative(xev))) 215 return false; 216 } else if (touch_display_id != display_ids_.first && 217 touch_display_id != display_ids_.second) { 218 return false; 219 } 220 #endif // defined(OS_CHROMEOS) 221 return true; 222 } 223 default: 224 return true; 225 } 226 } 227 void AshWindowTreeHostX11::TranslateAndDispatchLocatedEvent( 228 ui::LocatedEvent* event) { 229 if (!event->IsTouchEvent()) { 230 aura::Window* root_window = window(); 231 aura::client::ScreenPositionClient* screen_position_client = 232 aura::client::GetScreenPositionClient(root_window); 233 gfx::Rect local(bounds().size()); 234 local.Inset(transformer_helper_.GetHostInsets()); 235 236 if (screen_position_client && !local.Contains(event->location())) { 237 gfx::Point location(event->location()); 238 // In order to get the correct point in screen coordinates 239 // during passive grab, we first need to find on which host window 240 // the mouse is on, and find out the screen coordinates on that 241 // host window, then convert it back to this host window's coordinate. 242 screen_position_client->ConvertHostPointToScreen(root_window, 243 &location); 244 screen_position_client->ConvertPointFromScreen(root_window, &location); 245 ConvertPointToHost(&location); 246 event->set_location(location); 247 event->set_root_location(location); 248 } 249 } 250 SendEventToProcessor(event); 251 } 252 253 #if defined(OS_CHROMEOS) 254 void AshWindowTreeHostX11::SetCrOSTapPaused(bool state) { 255 if (!ui::IsXInput2Available()) 256 return; 257 // Temporarily pause tap-to-click when the cursor is hidden. 258 Atom prop = atom_cache()->GetAtom("Tap Paused"); 259 unsigned char value = state; 260 XIDeviceList dev_list = 261 ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay()); 262 263 // Only slave pointer devices could possibly have tap-paused property. 264 for (int i = 0; i < dev_list.count; i++) { 265 if (dev_list[i].use == XISlavePointer) { 266 Atom old_type; 267 int old_format; 268 unsigned long old_nvalues, bytes; 269 unsigned char* data; 270 int result = XIGetProperty(xdisplay(), 271 dev_list[i].deviceid, 272 prop, 273 0, 274 0, 275 False, 276 AnyPropertyType, 277 &old_type, 278 &old_format, 279 &old_nvalues, 280 &bytes, 281 &data); 282 if (result != Success) 283 continue; 284 XFree(data); 285 XIChangeProperty(xdisplay(), 286 dev_list[i].deviceid, 287 prop, 288 XA_INTEGER, 289 8, 290 PropModeReplace, 291 &value, 292 1); 293 } 294 } 295 } 296 #endif 297 298 AshWindowTreeHost* AshWindowTreeHost::Create( 299 const AshWindowTreeHostInitParams& init_params) { 300 return new AshWindowTreeHostX11(init_params.initial_bounds); 301 } 302 303 } // namespace ash 304