blob: 4cc218be635bd9253c25b9e5f1271a50cfe6b3f4 [file] [log] [blame]
// Copyright (c) 2012 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 "ui/aura/root_window.h"
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "ui/aura/aura_switches.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/event_client.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/event.h"
#include "ui/aura/event_filter.h"
#include "ui/aura/focus_manager.h"
#include "ui/aura/display_manager.h"
#include "ui/aura/root_window_host.h"
#include "ui/aura/root_window_observer.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/gestures/gesture_recognizer.h"
#include "ui/base/gestures/gesture_types.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/dip_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/gfx/display.h"
#include "ui/gfx/point3.h"
#include "ui/gfx/screen.h"
using std::vector;
namespace aura {
namespace {
const int kCompositorLockTimeoutMs = 67;
// Returns true if |target| has a non-client (frame) component at |location|,
// in window coordinates.
bool IsNonClientLocation(Window* target, const gfx::Point& location) {
if (!target->delegate())
return false;
int hit_test_code = target->delegate()->GetNonClientComponent(location);
return hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE;
}
typedef std::vector<EventFilter*> EventFilters;
void GetEventFiltersToNotify(Window* target, EventFilters* filters) {
while (target) {
if (target->event_filter())
filters->push_back(target->event_filter());
target = target->parent();
}
if (Env::GetInstance()->event_filter())
filters->push_back(Env::GetInstance()->event_filter());
}
float GetDeviceScaleFactorFromDisplay(const Window* window) {
DisplayManager* display_manager = Env::GetInstance()->display_manager();
return display_manager->GetDisplayNearestWindow(window).device_scale_factor();
}
Window* ConsumerToWindow(ui::GestureConsumer* consumer) {
return consumer && !consumer->ignores_events() ?
static_cast<Window*>(consumer) : NULL;
}
void SetLastMouseLocation(const Window* root_window,
const gfx::Point& location) {
Env::GetInstance()->SetLastMouseLocation(*root_window, location);
}
} // namespace
CompositorLock::CompositorLock(RootWindow* root_window)
: root_window_(root_window) {
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CompositorLock::CancelLock, AsWeakPtr()),
base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs));
}
CompositorLock::~CompositorLock() {
CancelLock();
}
void CompositorLock::CancelLock() {
if (!root_window_)
return;
root_window_->UnlockCompositor();
root_window_ = NULL;
}
bool RootWindow::hide_host_cursor_ = false;
////////////////////////////////////////////////////////////////////////////////
// RootWindow, public:
RootWindow::RootWindow(const gfx::Rect& initial_bounds)
: Window(NULL),
host_(RootWindowHost::Create(initial_bounds)),
ALLOW_THIS_IN_INITIALIZER_LIST(schedule_paint_factory_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(event_factory_(this)),
mouse_button_flags_(0),
touch_ids_down_(0),
last_cursor_(ui::kCursorNull),
mouse_pressed_handler_(NULL),
mouse_moved_handler_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(
gesture_recognizer_(ui::GestureRecognizer::Create(this))),
synthesize_mouse_move_(false),
waiting_on_compositing_end_(false),
draw_on_compositing_end_(false),
defer_draw_scheduling_(false),
mouse_move_hold_count_(0),
ALLOW_THIS_IN_INITIALIZER_LIST(held_mouse_event_factory_(this)),
compositor_lock_(NULL),
draw_on_compositor_unlock_(false) {
SetName("RootWindow");
compositor_.reset(new ui::Compositor(this, host_->GetAcceleratedWidget()));
DCHECK(compositor_.get());
compositor_->AddObserver(this);
}
RootWindow::~RootWindow() {
if (compositor_lock_) {
// No need to schedule a draw, we're going away.
draw_on_compositor_unlock_ = false;
compositor_lock_->CancelLock();
DCHECK(!compositor_lock_);
}
compositor_->RemoveObserver(this);
// Make sure to destroy the compositor before terminating so that state is
// cleared and we don't hit asserts.
compositor_.reset();
// Tear down in reverse. Frees any references held by the host.
host_.reset(NULL);
// An observer may have been added by an animation on the RootWindow.
layer()->GetAnimator()->RemoveObserver(this);
}
// static
RootWindow* RootWindow::GetForAcceleratedWidget(
gfx::AcceleratedWidget widget) {
RootWindowHost* host = RootWindowHost::GetForAcceleratedWidget(widget);
return host ? host->GetRootWindow() : NULL;
}
void RootWindow::Init() {
compositor()->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(this),
host_->GetBounds().size());
Window::Init(ui::LAYER_NOT_DRAWN);
gfx::Point point;
if (host_->QueryMouseLocation(&point))
SetLastMouseLocation(this, ui::ConvertPointToDIP(layer(), point));
compositor()->SetRootLayer(layer());
SetBounds(
ui::ConvertRectToDIP(layer(), gfx::Rect(host_->GetBounds().size())));
Show();
host_->SetRootWindow(this);
}
void RootWindow::ShowRootWindow() {
host_->Show();
}
void RootWindow::SetHostSize(const gfx::Size& size_in_pixel) {
DispatchHeldMouseMove();
gfx::Rect bounds = host_->GetBounds();
bounds.set_size(size_in_pixel);
host_->SetBounds(bounds);
// Requery the location to constrain it within the new root window size.
gfx::Point point;
if (host_->QueryMouseLocation(&point))
SetLastMouseLocation(this, ui::ConvertPointToDIP(layer(), point));
synthesize_mouse_move_ = false;
}
gfx::Size RootWindow::GetHostSize() const {
return host_->GetBounds().size();
}
void RootWindow::SetHostBounds(const gfx::Rect& bounds_in_pixel) {
DispatchHeldMouseMove();
host_->SetBounds(bounds_in_pixel);
// Requery the location to constrain it within the new root window size.
gfx::Point point;
if (host_->QueryMouseLocation(&point))
SetLastMouseLocation(this, ui::ConvertPointToDIP(layer(), point));
synthesize_mouse_move_ = false;
}
gfx::Point RootWindow::GetHostOrigin() const {
return host_->GetBounds().origin();
}
void RootWindow::SetCursor(gfx::NativeCursor cursor) {
last_cursor_ = cursor;
// A lot of code seems to depend on NULL cursors actually showing an arrow,
// so just pass everything along to the host.
host_->SetCursor(cursor);
}
void RootWindow::ShowCursor(bool show) {
cursor_shown_ = show;
host_->ShowCursor(show);
}
void RootWindow::MoveCursorTo(const gfx::Point& location_in_dip) {
gfx::Point location = location_in_dip;
layer()->transform().TransformPoint(location);
host_->MoveCursorTo(ui::ConvertPointToPixel(layer(), location));
SetLastMouseLocation(this, location_in_dip);
}
bool RootWindow::ConfineCursorToWindow() {
// We would like to be able to confine the cursor to that window. However,
// currently, we do not have such functionality in X. So we just confine
// to the root window. This is ok because this option is currently only
// being used in fullscreen mode, so root_window bounds = window bounds.
return host_->ConfineCursorToRootWindow();
}
void RootWindow::Draw() {
defer_draw_scheduling_ = false;
if (waiting_on_compositing_end_) {
draw_on_compositing_end_ = true;
return;
}
if (compositor_lock_) {
draw_on_compositor_unlock_ = true;
return;
}
waiting_on_compositing_end_ = true;
TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::Draw",
compositor_->last_started_frame() + 1);
compositor_->Draw(false);
}
void RootWindow::ScheduleFullDraw() {
compositor_->ScheduleFullDraw();
}
bool RootWindow::DispatchMouseEvent(MouseEvent* event) {
if (event->type() == ui::ET_MOUSE_DRAGGED ||
(event->flags() & ui::EF_IS_SYNTHESIZED)) {
if (mouse_move_hold_count_) {
held_mouse_move_.reset(new MouseEvent(*event, NULL, NULL));
return true;
} else {
// We may have a held event for a period between the time
// mouse_move_hold_count_ fell to 0 and the DispatchHeldMouseMove
// executes. Since we're going to dispatch the new event directly below,
// we can reset the old one.
held_mouse_move_.reset();
}
}
DispatchHeldMouseMove();
return DispatchMouseEventImpl(event);
}
bool RootWindow::DispatchKeyEvent(KeyEvent* event) {
DispatchHeldMouseMove();
if (event->key_code() == ui::VKEY_UNKNOWN)
return false;
client::EventClient* client = client::GetEventClient(GetRootWindow());
Window* focused_window = focus_manager_->GetFocusedWindow();
if (client && !client->CanProcessEventsWithinSubtree(focused_window)) {
GetFocusManager()->SetFocusedWindow(NULL, NULL);
return false;
}
return ProcessKeyEvent(focused_window, event);
}
bool RootWindow::DispatchScrollEvent(ScrollEvent* event) {
DispatchHeldMouseMove();
float scale = ui::GetDeviceScaleFactor(layer());
ui::Transform transform = layer()->transform();
transform.ConcatScale(scale, scale);
event->UpdateForRootTransform(transform);
SetLastMouseLocation(this, event->location());
synthesize_mouse_move_ = false;
Window* target = mouse_pressed_handler_ ?
mouse_pressed_handler_ : client::GetCaptureWindow(this);
if (!target)
target = GetEventHandlerForPoint(event->location());
if (target && target->delegate()) {
int flags = event->flags();
gfx::Point location_in_window = event->location();
Window::ConvertPointToWindow(this, target, &location_in_window);
if (IsNonClientLocation(target, location_in_window))
flags |= ui::EF_IS_NON_CLIENT;
ScrollEvent translated_event(*event, this, target, event->type(), flags);
return ProcessMouseEvent(target, &translated_event);
}
return false;
}
bool RootWindow::DispatchTouchEvent(TouchEvent* event) {
DispatchHeldMouseMove();
switch (event->type()) {
case ui::ET_TOUCH_PRESSED:
touch_ids_down_ |= (1 << event->touch_id());
Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
break;
// Don't handle ET_TOUCH_CANCELLED since we always get a ET_TOUCH_RELEASED.
case ui::ET_TOUCH_RELEASED:
touch_ids_down_ = (touch_ids_down_ | (1 << event->touch_id())) ^
(1 << event->touch_id());
Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
break;
default:
break;
}
float scale = ui::GetDeviceScaleFactor(layer());
ui::Transform transform = layer()->transform();
transform.ConcatScale(scale, scale);
event->UpdateForRootTransform(transform);
bool handled = false;
ui::TouchStatus status = ui::TOUCH_STATUS_UNKNOWN;
Window* target = client::GetCaptureWindow(this);
if (!target) {
target = ConsumerToWindow(
gesture_recognizer_->GetTouchLockedTarget(event));
if (!target) {
target = ConsumerToWindow(
gesture_recognizer_->GetTargetForLocation(event->GetLocation()));
}
}
if (!target && !bounds().Contains(event->location())) {
// If the initial touch is outside the root window, target the root.
target = this;
} else {
// We only come here when the first contact was within the root window.
if (!target) {
target = GetEventHandlerForPoint(event->location());
if (!target)
return false;
}
TouchEvent translated_event(*event, this, target);
status = ProcessTouchEvent(target, &translated_event);
handled = status != ui::TOUCH_STATUS_UNKNOWN;
if (status == ui::TOUCH_STATUS_QUEUED ||
status == ui::TOUCH_STATUS_QUEUED_END)
gesture_recognizer_->QueueTouchEventForGesture(target, *event);
}
// Get the list of GestureEvents from GestureRecognizer.
scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
*event, status, target));
return ProcessGestures(gestures.get()) ? true : handled;
}
bool RootWindow::DispatchGestureEvent(GestureEvent* event) {
DispatchHeldMouseMove();
Window* target = client::GetCaptureWindow(this);
if (!target) {
target = ConsumerToWindow(
gesture_recognizer_->GetTargetForGestureEvent(event));
if (!target)
return false;
}
if (target) {
GestureEvent translated_event(*event, this, target);
ui::GestureStatus status = ProcessGestureEvent(target, &translated_event);
return status != ui::GESTURE_STATUS_UNKNOWN;
}
return false;
}
void RootWindow::OnHostResized(const gfx::Size& size_in_pixel) {
DispatchHeldMouseMove();
// The compositor should have the same size as the native root window host.
// Get the latest scale from display because it might have been changed.
compositor_->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(this),
size_in_pixel);
gfx::Size old(bounds().size());
// The layer, and all the observers should be notified of the
// transformed size of the root window.
gfx::Rect bounds(ui::ConvertSizeToDIP(layer(), size_in_pixel));
layer()->transform().TransformRect(&bounds);
SetBounds(bounds);
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnRootWindowResized(this, old));
}
void RootWindow::OnWindowDestroying(Window* window) {
OnWindowHidden(window, true);
if (window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowBoundsChanged(Window* window,
bool contained_mouse_point) {
if (contained_mouse_point ||
(window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowVisibilityChanged(Window* window, bool is_visible) {
if (!is_visible)
OnWindowHidden(window, false);
if (window->ContainsPointInRoot(GetLastMouseLocationInRoot()))
PostMouseMoveEventAfterWindowChange();
}
void RootWindow::OnWindowTransformed(Window* window, bool contained_mouse) {
if (contained_mouse ||
(window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnKeyboardMappingChanged() {
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnKeyboardMappingChanged(this));
}
void RootWindow::OnRootWindowHostClosed() {
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnRootWindowHostClosed(this));
}
void RootWindow::AddRootWindowObserver(RootWindowObserver* observer) {
observers_.AddObserver(observer);
}
void RootWindow::RemoveRootWindowObserver(RootWindowObserver* observer) {
observers_.RemoveObserver(observer);
}
void RootWindow::PostNativeEvent(const base::NativeEvent& native_event) {
#if !defined(OS_MACOSX)
host_->PostNativeEvent(native_event);
#endif
}
void RootWindow::ConvertPointToNativeScreen(gfx::Point* point) const {
gfx::Point location = host_->GetLocationOnNativeScreen();
point->Offset(location.x(), location.y());
}
void RootWindow::AdvanceQueuedTouchEvent(Window* window, bool processed) {
scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
gestures.reset(gesture_recognizer_->AdvanceTouchQueue(window, processed));
ProcessGestures(gestures.get());
}
void RootWindow::SetGestureRecognizerForTesting(ui::GestureRecognizer* gr) {
gesture_recognizer_.reset(gr);
}
gfx::AcceleratedWidget RootWindow::GetAcceleratedWidget() {
return host_->GetAcceleratedWidget();
}
void RootWindow::ToggleFullScreen() {
host_->ToggleFullScreen();
}
void RootWindow::HoldMouseMoves() {
if (!mouse_move_hold_count_)
held_mouse_event_factory_.InvalidateWeakPtrs();
++mouse_move_hold_count_;
}
void RootWindow::ReleaseMouseMoves() {
--mouse_move_hold_count_;
DCHECK_GE(mouse_move_hold_count_, 0);
if (!mouse_move_hold_count_ && held_mouse_move_.get()) {
// We don't want to call DispatchHeldMouseMove directly, because this might
// be called from a deep stack while another event, in which case
// dispatching another one may not be safe/expected.
// Instead we post a task, that we may cancel if HoldMouseMoves is called
// again before it executes.
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&RootWindow::DispatchHeldMouseMove,
held_mouse_event_factory_.GetWeakPtr()));
}
}
scoped_refptr<CompositorLock> RootWindow::GetCompositorLock() {
if (!compositor_lock_)
compositor_lock_ = new CompositorLock(this);
return compositor_lock_;
}
void RootWindow::SetFocusWhenShown(bool focused) {
host_->SetFocusWhenShown(focused);
}
bool RootWindow::GrabSnapshot(const gfx::Rect& snapshot_bounds,
std::vector<unsigned char>* png_representation) {
DCHECK(bounds().Contains(snapshot_bounds));
gfx::Rect snapshot_pixels = ui::ConvertRectToPixel(layer(), snapshot_bounds);
return host_->GrabSnapshot(snapshot_pixels, png_representation);
}
gfx::Point RootWindow::GetLastMouseLocationInRoot() const {
gfx::Point location = Env::GetInstance()->last_mouse_location();
client::ScreenPositionClient* client = client::GetScreenPositionClient(this);
if (client)
client->ConvertPointFromScreen(this, &location);
return location;
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, Window overrides:
RootWindow* RootWindow::GetRootWindow() {
return this;
}
const RootWindow* RootWindow::GetRootWindow() const {
return this;
}
void RootWindow::SetTransform(const ui::Transform& transform) {
Window::SetTransform(transform);
// If the layer is not animating, then we need to update the host size
// immediately.
if (!layer()->GetAnimator()->is_animating())
OnHostResized(host_->GetBounds().size());
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::CompositorDelegate implementation:
void RootWindow::ScheduleDraw() {
if (compositor_lock_) {
draw_on_compositor_unlock_ = true;
} else if (!defer_draw_scheduling_) {
defer_draw_scheduling_ = true;
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&RootWindow::Draw, schedule_paint_factory_.GetWeakPtr()));
}
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::CompositorObserver implementation:
void RootWindow::OnCompositingDidCommit(ui::Compositor*) {
}
void RootWindow::OnCompositingWillStart(ui::Compositor*) {
}
void RootWindow::OnCompositingStarted(ui::Compositor*) {
}
void RootWindow::OnCompositingEnded(ui::Compositor*) {
TRACE_EVENT_ASYNC_END0("ui", "RootWindow::Draw",
compositor_->last_ended_frame());
waiting_on_compositing_end_ = false;
if (draw_on_compositing_end_) {
draw_on_compositing_end_ = false;
// Call ScheduleDraw() instead of Draw() in order to allow other
// ui::CompositorObservers to be notified before starting another
// draw cycle.
ScheduleDraw();
}
}
void RootWindow::OnCompositingAborted(ui::Compositor*) {
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::LayerDelegate implementation:
void RootWindow::OnDeviceScaleFactorChanged(
float device_scale_factor) {
if (cursor_shown_)
ShowCursor(false);
host_->OnDeviceScaleFactorChanged(device_scale_factor);
Window::OnDeviceScaleFactorChanged(device_scale_factor);
if (cursor_shown_)
ShowCursor(true);
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, overridden from aura::Window:
bool RootWindow::CanFocus() const {
return IsVisible();
}
bool RootWindow::CanReceiveEvents() const {
return IsVisible();
}
FocusManager* RootWindow::GetFocusManager() {
return focus_manager_;
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, overridden from aura::client::CaptureDelegate:
void RootWindow::UpdateCapture(Window* old_capture,
Window* new_capture) {
if (old_capture && old_capture->GetRootWindow() == this &&
old_capture->delegate()) {
// Send a capture changed event with bogus location data.
MouseEvent event(
ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(), gfx::Point(), 0);
ProcessMouseEvent(old_capture, &event);
old_capture->delegate()->OnCaptureLost();
}
// Reset the mouse_moved_handler_ if the mouse_moved_handler_ belongs
// to another root window when losing the capture.
if (mouse_moved_handler_ && old_capture &&
old_capture->Contains(mouse_moved_handler_) &&
old_capture->GetRootWindow() != this) {
mouse_moved_handler_ = NULL;
}
if (new_capture) {
// Make all subsequent mouse events and touch go to the capture window. We
// shouldn't need to send an event here as OnCaptureLost should take care of
// that.
if (mouse_moved_handler_ || Env::GetInstance()->is_mouse_button_down())
mouse_moved_handler_ = new_capture;
} else {
// Make sure mouse_moved_handler gets updated.
SynthesizeMouseMoveEvent();
}
mouse_pressed_handler_ = NULL;
}
void RootWindow::SetNativeCapture() {
host_->SetCapture();
}
void RootWindow::ReleaseNativeCapture() {
host_->ReleaseCapture();
}
gfx::Point RootWindow::QueryMouseLocationForTest() const {
gfx::Point point;
host_->QueryMouseLocation(&point);
return point;
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, private:
void RootWindow::HandleMouseMoved(const MouseEvent& event, Window* target) {
if (target == mouse_moved_handler_)
return;
// Send an exited event.
if (mouse_moved_handler_ && mouse_moved_handler_->delegate()) {
MouseEvent translated_event(event, this, mouse_moved_handler_,
ui::ET_MOUSE_EXITED, event.flags());
ProcessMouseEvent(mouse_moved_handler_, &translated_event);
}
mouse_moved_handler_ = target;
// Send an entered event.
if (mouse_moved_handler_ && mouse_moved_handler_->delegate()) {
MouseEvent translated_event(event, this, mouse_moved_handler_,
ui::ET_MOUSE_ENTERED, event.flags());
ProcessMouseEvent(mouse_moved_handler_, &translated_event);
}
}
bool RootWindow::ProcessMouseEvent(Window* target, MouseEvent* event) {
if (!target->IsVisible())
return false;
EventFilters filters;
GetEventFiltersToNotify(target->parent(), &filters);
for (EventFilters::const_reverse_iterator it = filters.rbegin(),
rend = filters.rend();
it != rend; ++it) {
if ((*it)->PreHandleMouseEvent(target, event))
return true;
}
if (!target->delegate())
return false;
return target->delegate()->OnMouseEvent(event);
}
bool RootWindow::ProcessKeyEvent(Window* target, KeyEvent* event) {
EventFilters filters;
if (!target) {
// When no window is focused, send the key event to |this| so event filters
// for the window could check if the key is a global shortcut like Alt+Tab.
target = this;
GetEventFiltersToNotify(this, &filters);
} else {
if (!target->IsVisible())
return false;
GetEventFiltersToNotify(target->parent(), &filters);
}
for (EventFilters::const_reverse_iterator it = filters.rbegin(),
rend = filters.rend();
it != rend; ++it) {
if ((*it)->PreHandleKeyEvent(target, event))
return true;
}
if (!target->delegate())
return false;
return target->delegate()->OnKeyEvent(event);
}
ui::TouchStatus RootWindow::ProcessTouchEvent(Window* target,
TouchEvent* event) {
if (!target->IsVisible())
return ui::TOUCH_STATUS_UNKNOWN;
EventFilters filters;
if (target == this)
GetEventFiltersToNotify(target, &filters);
else
GetEventFiltersToNotify(target->parent(), &filters);
for (EventFilters::const_reverse_iterator it = filters.rbegin(),
rend = filters.rend();
it != rend; ++it) {
ui::TouchStatus status = (*it)->PreHandleTouchEvent(target, event);
if (status != ui::TOUCH_STATUS_UNKNOWN)
return status;
}
if (target->delegate())
return target->delegate()->OnTouchEvent(event);
return ui::TOUCH_STATUS_UNKNOWN;
}
ui::GestureStatus RootWindow::ProcessGestureEvent(Window* target,
GestureEvent* event) {
if (!target->IsVisible())
return ui::GESTURE_STATUS_UNKNOWN;
EventFilters filters;
if (target == this)
GetEventFiltersToNotify(target, &filters);
else
GetEventFiltersToNotify(target->parent(), &filters);
ui::GestureStatus status = ui::GESTURE_STATUS_UNKNOWN;
for (EventFilters::const_reverse_iterator it = filters.rbegin(),
rend = filters.rend();
it != rend; ++it) {
status = (*it)->PreHandleGestureEvent(target, event);
if (status != ui::GESTURE_STATUS_UNKNOWN)
return status;
}
if (target->delegate())
status = target->delegate()->OnGestureEvent(event);
return status;
}
bool RootWindow::ProcessGestures(ui::GestureRecognizer::Gestures* gestures) {
if (!gestures)
return false;
bool handled = false;
for (unsigned int i = 0; i < gestures->size(); i++) {
GestureEvent* gesture =
static_cast<GestureEvent*>(gestures->get().at(i));
if (DispatchGestureEvent(gesture) != ui::GESTURE_STATUS_UNKNOWN)
handled = true;
}
return handled;
}
void RootWindow::OnWindowRemovedFromRootWindow(Window* detached) {
DCHECK(aura::client::GetCaptureWindow(this) != this);
OnWindowHidden(detached, false);
if (detached->IsVisible() &&
detached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowHidden(Window* invisible, bool destroyed) {
// Update the focused window state if the invisible window contains
// focused_window.
Window* focused_window = focus_manager_->GetFocusedWindow();
if (invisible->Contains(focused_window)) {
Window* focus_to = invisible->transient_parent();
if (focus_to) {
// Has to be removed from the transient parent before focusing, otherwise
// |window| will be focused again.
if (destroyed)
focus_to->RemoveTransientChild(invisible);
} else {
// If the invisible view has no visible transient window, focus to the
// topmost visible parent window.
focus_to = invisible->parent();
}
if (focus_to &&
(!focus_to->IsVisible() ||
!focus_to->CanFocus() ||
(client::GetActivationClient(this) &&
!client::GetActivationClient(this)->OnWillFocusWindow(focus_to,
NULL)))) {
focus_to = NULL;
}
GetFocusManager()->SetFocusedWindow(focus_to, NULL);
}
Window* capture_window = aura::client::GetCaptureWindow(this);
// If the ancestor of the capture window is hidden,
// release the capture.
if (invisible->Contains(capture_window) && invisible != this)
capture_window->ReleaseCapture();
// If the ancestor of any event handler windows are invisible, release the
// pointer to those windows.
if (invisible->Contains(mouse_pressed_handler_))
mouse_pressed_handler_ = NULL;
if (invisible->Contains(mouse_moved_handler_))
mouse_moved_handler_ = NULL;
gesture_recognizer_->FlushTouchQueue(invisible);
}
void RootWindow::OnWindowAddedToRootWindow(Window* attached) {
if (attached->IsVisible() &&
attached->ContainsPointInRoot(GetLastMouseLocationInRoot()))
PostMouseMoveEventAfterWindowChange();
}
bool RootWindow::DispatchLongPressGestureEvent(ui::GestureEvent* event) {
return DispatchGestureEvent(static_cast<GestureEvent*>(event));
}
bool RootWindow::DispatchCancelTouchEvent(ui::TouchEvent* event) {
return DispatchTouchEvent(static_cast<TouchEvent*>(event));
}
ui::GestureEvent* RootWindow::CreateGestureEvent(
const ui::GestureEventDetails& details,
const gfx::Point& location,
int flags,
base::Time time,
unsigned int touch_id_bitfield) {
return new GestureEvent(details.type(), location.x(), location.y(),
flags, time, details,
touch_id_bitfield);
}
ui::TouchEvent* RootWindow::CreateTouchEvent(ui::EventType type,
const gfx::Point& location,
int touch_id,
base::TimeDelta time_stamp) {
return new TouchEvent(type, location, touch_id, time_stamp);
}
void RootWindow::OnLayerAnimationEnded(
ui::LayerAnimationSequence* animation) {
OnHostResized(host_->GetBounds().size());
}
void RootWindow::OnLayerAnimationScheduled(
ui::LayerAnimationSequence* animation) {
}
void RootWindow::OnLayerAnimationAborted(
ui::LayerAnimationSequence* animation) {
}
bool RootWindow::DispatchMouseEventImpl(MouseEvent* event) {
float scale = ui::GetDeviceScaleFactor(layer());
ui::Transform transform = layer()->transform();
transform.ConcatScale(scale, scale);
event->UpdateForRootTransform(transform);
Window* target = mouse_pressed_handler_ ?
mouse_pressed_handler_ : client::GetCaptureWindow(this);
if (!target)
target = GetEventHandlerForPoint(event->location());
return DispatchMouseEventToTarget(event, target);
}
bool RootWindow::DispatchMouseEventToTarget(MouseEvent* event,
Window* target) {
static const int kMouseButtonFlagMask =
ui::EF_LEFT_MOUSE_BUTTON |
ui::EF_MIDDLE_MOUSE_BUTTON |
ui::EF_RIGHT_MOUSE_BUTTON;
SetLastMouseLocation(this, event->location());
synthesize_mouse_move_ = false;
switch (event->type()) {
case ui::ET_MOUSE_MOVED:
HandleMouseMoved(*event, target);
break;
case ui::ET_MOUSE_PRESSED:
if (!mouse_pressed_handler_)
mouse_pressed_handler_ = target;
mouse_button_flags_ = event->flags() & kMouseButtonFlagMask;
Env::GetInstance()->set_mouse_button_flags(mouse_button_flags_);
break;
case ui::ET_MOUSE_RELEASED:
mouse_pressed_handler_ = NULL;
mouse_button_flags_ = event->flags() & kMouseButtonFlagMask;
Env::GetInstance()->set_mouse_button_flags(mouse_button_flags_);
break;
default:
break;
}
if (target && target->delegate()) {
int flags = event->flags();
gfx::Point location_in_window = event->location();
Window::ConvertPointToWindow(this, target, &location_in_window);
if (IsNonClientLocation(target, location_in_window))
flags |= ui::EF_IS_NON_CLIENT;
MouseEvent translated_event(*event, this, target, event->type(), flags);
return ProcessMouseEvent(target, &translated_event);
}
return false;
}
void RootWindow::DispatchHeldMouseMove() {
if (held_mouse_move_.get()) {
// If a mouse move has been synthesized, the target location is suspect,
// so drop the held event.
if (!synthesize_mouse_move_)
DispatchMouseEventImpl(held_mouse_move_.get());
held_mouse_move_.reset();
}
}
void RootWindow::PostMouseMoveEventAfterWindowChange() {
if (synthesize_mouse_move_)
return;
synthesize_mouse_move_ = true;
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&RootWindow::SynthesizeMouseMoveEvent,
event_factory_.GetWeakPtr()));
}
void RootWindow::SynthesizeMouseMoveEvent() {
if (!synthesize_mouse_move_)
return;
synthesize_mouse_move_ = false;
#if !defined(OS_WIN)
// Temporarily disabled for windows. See crbug.com/112222.
gfx::Point3f point(Env::GetInstance()->last_mouse_location());
ui::Transform transform = layer()->transform();
float scale = ui::GetDeviceScaleFactor(layer());
transform.ConcatScale(scale, scale);
transform.TransformPoint(point);
gfx::Point orig_mouse_location = point.AsPoint();
// TODO(derat|oshima): Don't use mouse_button_flags_ as it's
// currently broken. See/ crbug.com/107931.
MouseEvent event(ui::ET_MOUSE_MOVED,
orig_mouse_location,
orig_mouse_location,
ui::EF_IS_SYNTHESIZED);
DispatchMouseEvent(&event);
#endif
}
void RootWindow::UnlockCompositor() {
DCHECK(compositor_lock_);
compositor_lock_ = NULL;
if (draw_on_compositor_unlock_) {
draw_on_compositor_unlock_ = false;
ScheduleDraw();
}
}
} // namespace aura