blob: 1f3358e968b1b7d6e8d860a28de680c99391d5ad [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/host/ash_window_tree_host_x11.h"
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput2.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <string>
#include <vector>
#include "ash/host/ash_window_tree_host_init_params.h"
#include "ash/host/root_window_transformer.h"
#include "base/basictypes.h"
#include "base/sys_info.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/device_data_manager.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/x/device_list_cache_x.h"
#include "ui/events/x/touch_factory_x11.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
namespace ash {
AshWindowTreeHostX11::AshWindowTreeHostX11(const gfx::Rect& initial_bounds)
: WindowTreeHostX11(initial_bounds),
transformer_helper_(this),
display_ids_(std::make_pair(gfx::Display::kInvalidDisplayID,
gfx::Display::kInvalidDisplayID)) {
aura::Env::GetInstance()->AddObserver(this);
}
AshWindowTreeHostX11::~AshWindowTreeHostX11() {
aura::Env::GetInstance()->RemoveObserver(this);
UnConfineCursor();
}
void AshWindowTreeHostX11::ToggleFullScreen() { NOTIMPLEMENTED(); }
bool AshWindowTreeHostX11::ConfineCursorToRootWindow() {
#if XFIXES_MAJOR >= 5
DCHECK(!pointer_barriers_.get());
if (pointer_barriers_)
return false;
pointer_barriers_.reset(new XID[4]);
gfx::Rect barrier(bounds());
barrier.Inset(transformer_helper_.GetHostInsets());
// Horizontal, top barriers.
pointer_barriers_[0] = XFixesCreatePointerBarrier(xdisplay(),
x_root_window(),
barrier.x(),
barrier.y(),
barrier.right(),
barrier.y(),
BarrierPositiveY,
0,
XIAllDevices);
// Horizontal, bottom barriers.
pointer_barriers_[1] = XFixesCreatePointerBarrier(xdisplay(),
x_root_window(),
barrier.x(),
barrier.bottom(),
barrier.right(),
barrier.bottom(),
BarrierNegativeY,
0,
XIAllDevices);
// Vertical, left barriers.
pointer_barriers_[2] = XFixesCreatePointerBarrier(xdisplay(),
x_root_window(),
barrier.x(),
barrier.y(),
barrier.x(),
barrier.bottom(),
BarrierPositiveX,
0,
XIAllDevices);
// Vertical, right barriers.
pointer_barriers_[3] = XFixesCreatePointerBarrier(xdisplay(),
x_root_window(),
barrier.right(),
barrier.y(),
barrier.right(),
barrier.bottom(),
BarrierNegativeX,
0,
XIAllDevices);
#endif
return true;
}
void AshWindowTreeHostX11::UnConfineCursor() {
#if XFIXES_MAJOR >= 5
if (pointer_barriers_) {
XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[0]);
XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[1]);
XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[2]);
XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[3]);
pointer_barriers_.reset();
}
#endif
}
void AshWindowTreeHostX11::SetRootWindowTransformer(
scoped_ptr<RootWindowTransformer> transformer) {
transformer_helper_.SetRootWindowTransformer(transformer.Pass());
if (pointer_barriers_) {
UnConfineCursor();
ConfineCursorToRootWindow();
}
}
gfx::Insets AshWindowTreeHostX11::GetHostInsets() const {
return transformer_helper_.GetHostInsets();
}
aura::WindowTreeHost* AshWindowTreeHostX11::AsWindowTreeHost() { return this; }
void AshWindowTreeHostX11::UpdateDisplayID(int64 id1, int64 id2) {
display_ids_.first = id1;
display_ids_.second = id2;
}
void AshWindowTreeHostX11::PrepareForShutdown() {
if (ui::PlatformEventSource::GetInstance())
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
}
void AshWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
WindowTreeHostX11::SetBounds(bounds);
if (pointer_barriers_) {
UnConfineCursor();
ConfineCursorToRootWindow();
}
}
gfx::Transform AshWindowTreeHostX11::GetRootTransform() const {
return transformer_helper_.GetTransform();
}
void AshWindowTreeHostX11::SetRootTransform(const gfx::Transform& transform) {
transformer_helper_.SetTransform(transform);
}
gfx::Transform AshWindowTreeHostX11::GetInverseRootTransform() const {
return transformer_helper_.GetInverseTransform();
}
void AshWindowTreeHostX11::UpdateRootWindowSize(const gfx::Size& host_size) {
transformer_helper_.UpdateWindowSize(host_size);
}
void AshWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
#if defined(OS_CHROMEOS)
SetCrOSTapPaused(!show);
#endif
}
void AshWindowTreeHostX11::OnWindowInitialized(aura::Window* window) {}
void AshWindowTreeHostX11::OnHostInitialized(aura::WindowTreeHost* host) {
if (host != AsWindowTreeHost())
return;
#if defined(OS_CHROMEOS)
// We have to enable Tap-to-click by default because the cursor is set to
// visible in Shell::InitRootWindowController.
SetCrOSTapPaused(false);
#endif
}
void AshWindowTreeHostX11::OnConfigureNotify() {
// Always update barrier and mouse location because |bounds_| might
// have already been updated in |SetBounds|.
if (pointer_barriers_) {
UnConfineCursor();
ConfineCursorToRootWindow();
}
}
bool AshWindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) {
if(!WindowTreeHostX11::CanDispatchEvent(event))
return false;
XEvent* xev = event;
ui::EventType type = ui::EventTypeFromNative(xev);
// For touch event, check if the root window is residing on the according
// touch display.
switch (type) {
case ui::ET_TOUCH_MOVED:
case ui::ET_TOUCH_PRESSED:
case ui::ET_TOUCH_CANCELLED:
case ui::ET_TOUCH_RELEASED: {
#if defined(OS_CHROMEOS)
XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data);
int64 touch_display_id =
ui::DeviceDataManager::GetInstance()->GetDisplayForTouchDevice(
xiev->deviceid);
// If we don't have record of display id for this touch device, check
// that if the event is within the bound of the root window. Note
// that in multi-monitor case, the event position is in framebuffer
// space so the bounds check will not work so well.
if (touch_display_id == gfx::Display::kInvalidDisplayID) {
if (base::SysInfo::IsRunningOnChromeOS() &&
!bounds().Contains(ui::EventLocationFromNative(xev)))
return false;
} else if (touch_display_id != display_ids_.first &&
touch_display_id != display_ids_.second) {
return false;
}
#endif // defined(OS_CHROMEOS)
return true;
}
default:
return true;
}
}
void AshWindowTreeHostX11::TranslateAndDispatchLocatedEvent(
ui::LocatedEvent* event) {
if (!event->IsTouchEvent()) {
aura::Window* root_window = window();
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(root_window);
gfx::Rect local(bounds().size());
local.Inset(transformer_helper_.GetHostInsets());
if (screen_position_client && !local.Contains(event->location())) {
gfx::Point location(event->location());
// In order to get the correct point in screen coordinates
// during passive grab, we first need to find on which host window
// the mouse is on, and find out the screen coordinates on that
// host window, then convert it back to this host window's coordinate.
screen_position_client->ConvertHostPointToScreen(root_window,
&location);
screen_position_client->ConvertPointFromScreen(root_window, &location);
ConvertPointToHost(&location);
event->set_location(location);
event->set_root_location(location);
}
}
SendEventToProcessor(event);
}
#if defined(OS_CHROMEOS)
void AshWindowTreeHostX11::SetCrOSTapPaused(bool state) {
if (!ui::IsXInput2Available())
return;
// Temporarily pause tap-to-click when the cursor is hidden.
Atom prop = atom_cache()->GetAtom("Tap Paused");
unsigned char value = state;
XIDeviceList dev_list =
ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay());
// Only slave pointer devices could possibly have tap-paused property.
for (int i = 0; i < dev_list.count; i++) {
if (dev_list[i].use == XISlavePointer) {
Atom old_type;
int old_format;
unsigned long old_nvalues, bytes;
unsigned char* data;
int result = XIGetProperty(xdisplay(),
dev_list[i].deviceid,
prop,
0,
0,
False,
AnyPropertyType,
&old_type,
&old_format,
&old_nvalues,
&bytes,
&data);
if (result != Success)
continue;
XFree(data);
XIChangeProperty(xdisplay(),
dev_list[i].deviceid,
prop,
XA_INTEGER,
8,
PropModeReplace,
&value,
1);
}
}
}
#endif
AshWindowTreeHost* AshWindowTreeHost::Create(
const AshWindowTreeHostInitParams& init_params) {
return new AshWindowTreeHostX11(init_params.initial_bounds);
}
} // namespace ash