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