Home | History | Annotate | Download | only in host
      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/event.h"
     25 #include "ui/events/event_utils.h"
     26 #include "ui/events/platform/platform_event_source.h"
     27 #include "ui/events/x/device_data_manager.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