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 33namespace ash { 34 35AshWindowTreeHostX11::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 43AshWindowTreeHostX11::~AshWindowTreeHostX11() { 44 aura::Env::GetInstance()->RemoveObserver(this); 45 UnConfineCursor(); 46} 47 48void AshWindowTreeHostX11::ToggleFullScreen() { NOTIMPLEMENTED(); } 49 50bool 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 102void 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 114void AshWindowTreeHostX11::SetRootWindowTransformer( 115 scoped_ptr<RootWindowTransformer> transformer) { 116 transformer_helper_.SetRootWindowTransformer(transformer.Pass()); 117 if (pointer_barriers_) { 118 UnConfineCursor(); 119 ConfineCursorToRootWindow(); 120 } 121} 122 123gfx::Insets AshWindowTreeHostX11::GetHostInsets() const { 124 return transformer_helper_.GetHostInsets(); 125} 126 127aura::WindowTreeHost* AshWindowTreeHostX11::AsWindowTreeHost() { return this; } 128 129void AshWindowTreeHostX11::UpdateDisplayID(int64 id1, int64 id2) { 130 display_ids_.first = id1; 131 display_ids_.second = id2; 132} 133 134void AshWindowTreeHostX11::PrepareForShutdown() { 135 if (ui::PlatformEventSource::GetInstance()) 136 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 137} 138 139void AshWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) { 140 WindowTreeHostX11::SetBounds(bounds); 141 if (pointer_barriers_) { 142 UnConfineCursor(); 143 ConfineCursorToRootWindow(); 144 } 145} 146 147gfx::Transform AshWindowTreeHostX11::GetRootTransform() const { 148 return transformer_helper_.GetTransform(); 149} 150 151void AshWindowTreeHostX11::SetRootTransform(const gfx::Transform& transform) { 152 transformer_helper_.SetTransform(transform); 153} 154 155gfx::Transform AshWindowTreeHostX11::GetInverseRootTransform() const { 156 return transformer_helper_.GetInverseTransform(); 157} 158 159void AshWindowTreeHostX11::UpdateRootWindowSize(const gfx::Size& host_size) { 160 transformer_helper_.UpdateWindowSize(host_size); 161} 162 163void AshWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) { 164#if defined(OS_CHROMEOS) 165 SetCrOSTapPaused(!show); 166#endif 167} 168 169void AshWindowTreeHostX11::OnWindowInitialized(aura::Window* window) {} 170 171void 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 182void 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 191bool 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} 227void 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) 254void 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 298AshWindowTreeHost* AshWindowTreeHost::Create( 299 const AshWindowTreeHostInitParams& init_params) { 300 return new AshWindowTreeHostX11(init_params.initial_bounds); 301} 302 303} // namespace ash 304