// 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 #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/drag_drop_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_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/event.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 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(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), ALLOW_THIS_IN_INITIALIZER_LIST( host_(RootWindowHost::Create(this, 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(); } void RootWindow::ShowRootWindow() { host_->Show(); } RootWindowHostDelegate* RootWindow::AsRootWindowHostDelegate() { return this; } 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) { // If a drag is in progress, the DragDropClient should override the cursor. client::DragDropClient* dnd_client = client::GetDragDropClient(this); if (dnd_client && dnd_client->IsDragDropInProgress()) cursor = dnd_client->GetDragCursor(); 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::DispatchGestureEvent(ui::GestureEvent* event) { DispatchHeldMouseMove(); Window* target = client::GetCaptureWindow(this); if (!target) { target = ConsumerToWindow( gesture_recognizer_->GetTargetForGestureEvent(event)); if (!target) return false; } if (target) { ui::GestureEvent translated_event( *event, static_cast(this), target); ui::GestureStatus status = ProcessGestureEvent(target, &translated_event); return status != ui::GESTURE_STATUS_UNKNOWN; } return false; } 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 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 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* 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. ui::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 ui::MouseEvent& event, Window* target) { if (target == mouse_moved_handler_) return; // Send an exited event. if (mouse_moved_handler_ && mouse_moved_handler_->delegate()) { ui::MouseEvent translated_event(event, static_cast(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()) { ui::MouseEvent translated_event(event, static_cast(this), mouse_moved_handler_, ui::ET_MOUSE_ENTERED, event.flags()); ProcessMouseEvent(mouse_moved_handler_, &translated_event); } } bool RootWindow::ProcessMouseEvent(Window* target, ui::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, ui::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, ui::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, ui::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++) { ui::GestureEvent* gesture = 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(event); } bool RootWindow::DispatchCancelTouchEvent(ui::TouchEvent* event) { return OnHostTouchEvent(event); } void RootWindow::OnLayerAnimationEnded( ui::LayerAnimationSequence* animation) { OnHostResized(host_->GetBounds().size()); } void RootWindow::OnLayerAnimationScheduled( ui::LayerAnimationSequence* animation) { } void RootWindow::OnLayerAnimationAborted( ui::LayerAnimationSequence* animation) { } //////////////////////////////////////////////////////////////////////////////// // RootWindow, RootWindowHostDelegate implementation: bool RootWindow::OnHostKeyEvent(ui::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::OnHostMouseEvent(ui::MouseEvent* event) { if (event->type() == ui::ET_MOUSE_DRAGGED || (event->flags() & ui::EF_IS_SYNTHESIZED)) { if (mouse_move_hold_count_) { Window* null_window = static_cast(NULL); held_mouse_move_.reset( new ui::MouseEvent(*event, null_window, null_window)); 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::OnHostScrollEvent(ui::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::ConvertPointToTarget(this, target, &location_in_window); if (IsNonClientLocation(target, location_in_window)) flags |= ui::EF_IS_NON_CLIENT; ui::ScrollEvent translated_event( *event, static_cast(this), target, event->type(), flags); return ProcessMouseEvent(target, &translated_event); } return false; } bool RootWindow::OnHostTouchEvent(ui::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->location())); } } 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; } ui::TouchEvent translated_event( *event, static_cast(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 gestures; gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( *event, status, target)); return ProcessGestures(gestures.get()) ? true : handled; } void RootWindow::OnHostLostCapture() { Window* capture_window = client::GetCaptureWindow(this); if (capture_window && capture_window->GetRootWindow() == this) capture_window->ReleaseCapture(); } void RootWindow::OnHostPaint() { Draw(); } void RootWindow::OnHostResized(const gfx::Size& size) { 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); 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)); layer()->transform().TransformRect(&bounds); SetBounds(bounds); FOR_EACH_OBSERVER(RootWindowObserver, observers_, OnRootWindowResized(this, old)); } float RootWindow::GetDeviceScaleFactor() { return compositor()->device_scale_factor(); } RootWindow* RootWindow::AsRootWindow() { return this; } //////////////////////////////////////////////////////////////////////////////// // RootWindow, private: bool RootWindow::DispatchMouseEventImpl(ui::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(ui::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::ConvertPointToTarget(this, target, &location_in_window); if (IsNonClientLocation(target, location_in_window)) flags |= ui::EF_IS_NON_CLIENT; ui::MouseEvent translated_event( *event, static_cast(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(GetLastMouseLocationInRoot()); 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. ui::MouseEvent event(ui::ET_MOUSE_MOVED, orig_mouse_location, orig_mouse_location, ui::EF_IS_SYNTHESIZED); OnHostMouseEvent(&event); #endif } void RootWindow::UnlockCompositor() { DCHECK(compositor_lock_); compositor_lock_ = NULL; if (draw_on_compositor_unlock_) { draw_on_compositor_unlock_ = false; ScheduleDraw(); } } } // namespace aura