| // 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/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "ui/aura/client/activation_client.h" |
| #include "ui/aura/client/capture_client.h" |
| #include "ui/aura/client/cursor_client.h" |
| #include "ui/aura/client/event_client.h" |
| #include "ui/aura/client/focus_client.h" |
| #include "ui/aura/client/screen_position_client.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/root_window_host.h" |
| #include "ui/aura/root_window_observer.h" |
| #include "ui/aura/root_window_transformer.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_delegate.h" |
| #include "ui/aura/window_tracker.h" |
| #include "ui/base/events/event.h" |
| #include "ui/base/gestures/gesture_recognizer.h" |
| #include "ui/base/gestures/gesture_types.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/base/view_prop.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_f.h" |
| #include "ui/gfx/point_conversions.h" |
| #include "ui/gfx/screen.h" |
| #include "ui/gfx/size_conversions.h" |
| |
| using std::vector; |
| |
| namespace aura { |
| |
| namespace { |
| |
| const char kRootWindowForAcceleratedWidget[] = |
| "__AURA_ROOT_WINDOW_ACCELERATED_WIDGET__"; |
| |
| // 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; |
| } |
| |
| float GetDeviceScaleFactorFromDisplay(Window* window) { |
| return gfx::Screen::GetScreenFor(window)-> |
| GetDisplayNearestWindow(window).device_scale_factor(); |
| } |
| |
| Window* ConsumerToWindow(ui::GestureConsumer* consumer) { |
| return consumer && !consumer->ignores_events() ? |
| static_cast<Window*>(consumer) : NULL; |
| } |
| |
| void SetLastMouseLocation(const RootWindow* root_window, |
| const gfx::Point& location_in_root) { |
| client::ScreenPositionClient* client = |
| client::GetScreenPositionClient(root_window); |
| if (client) { |
| gfx::Point location_in_screen = location_in_root; |
| client->ConvertPointToScreen(root_window, &location_in_screen); |
| Env::GetInstance()->set_last_mouse_location(location_in_screen); |
| } else { |
| Env::GetInstance()->set_last_mouse_location(location_in_root); |
| } |
| } |
| |
| RootWindowHost* CreateHost(RootWindow* root_window, |
| const RootWindow::CreateParams& params) { |
| RootWindowHost* host = params.host ? |
| params.host : RootWindowHost::Create(params.initial_bounds); |
| host->SetDelegate(root_window); |
| return host; |
| } |
| |
| class SimpleRootWindowTransformer : public RootWindowTransformer { |
| public: |
| SimpleRootWindowTransformer(const RootWindow* root_window, |
| const gfx::Transform& transform) |
| : root_window_(root_window), |
| transform_(transform) { |
| } |
| |
| // RootWindowTransformer overrides: |
| virtual gfx::Transform GetTransform() const OVERRIDE { |
| return transform_; |
| } |
| |
| virtual gfx::Transform GetInverseTransform() const OVERRIDE { |
| gfx::Transform invert; |
| if (!transform_.GetInverse(&invert)) |
| return transform_; |
| return invert; |
| } |
| |
| virtual gfx::Rect GetRootWindowBounds( |
| const gfx::Size& host_size) const OVERRIDE { |
| gfx::Rect bounds(host_size); |
| gfx::RectF new_bounds(ui::ConvertRectToDIP(root_window_->layer(), bounds)); |
| transform_.TransformRect(&new_bounds); |
| return gfx::Rect(gfx::ToFlooredSize(new_bounds.size())); |
| } |
| |
| virtual gfx::Insets GetHostInsets() const OVERRIDE { |
| return gfx::Insets(); |
| } |
| |
| private: |
| virtual ~SimpleRootWindowTransformer() {} |
| |
| const RootWindow* root_window_; |
| const gfx::Transform transform_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SimpleRootWindowTransformer); |
| }; |
| |
| } // namespace |
| |
| RootWindow::CreateParams::CreateParams(const gfx::Rect& a_initial_bounds) |
| : initial_bounds(a_initial_bounds), |
| host(NULL) { |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, public: |
| |
| RootWindow::RootWindow(const CreateParams& params) |
| : Window(NULL), |
| host_(CreateHost(this, params)), |
| schedule_paint_factory_(this), |
| event_factory_(this), |
| touch_ids_down_(0), |
| last_cursor_(ui::kCursorNull), |
| mouse_pressed_handler_(NULL), |
| mouse_moved_handler_(NULL), |
| mouse_event_dispatch_target_(NULL), |
| event_dispatch_target_(NULL), |
| gesture_recognizer_(ui::GestureRecognizer::Create(this)), |
| synthesize_mouse_move_(false), |
| waiting_on_compositing_end_(false), |
| draw_on_compositing_end_(false), |
| defer_draw_scheduling_(false), |
| move_hold_count_(0), |
| held_event_factory_(this), |
| repostable_event_factory_(this) { |
| SetName("RootWindow"); |
| |
| compositor_.reset(new ui::Compositor(this, host_->GetAcceleratedWidget())); |
| DCHECK(compositor_.get()); |
| compositor_->AddObserver(this); |
| |
| prop_.reset(new ui::ViewProp(host_->GetAcceleratedWidget(), |
| kRootWindowForAcceleratedWidget, |
| this)); |
| } |
| |
| RootWindow::~RootWindow() { |
| 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) { |
| return reinterpret_cast<RootWindow*>( |
| ui::ViewProp::GetValue(widget, kRootWindowForAcceleratedWidget)); |
| } |
| |
| void RootWindow::Init() { |
| compositor()->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(this), |
| host_->GetBounds().size()); |
| Window::Init(ui::LAYER_NOT_DRAWN); |
| compositor()->SetRootLayer(layer()); |
| transformer_.reset(new SimpleRootWindowTransformer(this, gfx::Transform())); |
| UpdateRootWindowSize(GetHostSize()); |
| Env::GetInstance()->NotifyRootWindowInitialized(this); |
| Show(); |
| } |
| |
| void RootWindow::ShowRootWindow() { |
| host_->Show(); |
| } |
| |
| void RootWindow::HideRootWindow() { |
| host_->Hide(); |
| } |
| |
| void RootWindow::PrepareForShutdown() { |
| host_->PrepareForShutdown(); |
| // discard synthesize event request as well. |
| synthesize_mouse_move_ = false; |
| } |
| |
| void RootWindow::RepostEvent(const ui::LocatedEvent& event) { |
| // We allow for only one outstanding repostable event. This is used |
| // in exiting context menus. A dropped repost request is allowed. |
| if (event.type() == ui::ET_MOUSE_PRESSED) { |
| held_repostable_event_.reset( |
| new ui::MouseEvent( |
| static_cast<const ui::MouseEvent&>(event), |
| static_cast<aura::Window*>(event.target()), |
| static_cast<aura::Window*>(this))); |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&RootWindow::DispatchHeldEvents, |
| repostable_event_factory_.GetWeakPtr())); |
| } else { |
| DCHECK(event.type() == ui::ET_GESTURE_TAP_DOWN); |
| held_repostable_event_.reset(); |
| // TODO(sschmitz): add similar code for gesture events. |
| } |
| } |
| |
| RootWindowHostDelegate* RootWindow::AsRootWindowHostDelegate() { |
| return this; |
| } |
| |
| void RootWindow::SetHostSize(const gfx::Size& size_in_pixel) { |
| DispatchHeldEvents(); |
| 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) { |
| DCHECK(!bounds_in_pixel.IsEmpty()); |
| DispatchHeldEvents(); |
| host_->SetBounds(bounds_in_pixel); |
| 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::OnCursorVisibilityChanged(bool show) { |
| host_->OnCursorVisibilityChanged(show); |
| } |
| |
| void RootWindow::OnMouseEventsEnableStateChanged(bool enabled) { |
| // Send entered / exited so that visual state can be updated to match |
| // mouse events state. |
| PostMouseMoveEventAfterWindowChange(); |
| // TODO(mazda): Add code to disable mouse events when |enabled| == false. |
| } |
| |
| void RootWindow::MoveCursorTo(const gfx::Point& location_in_dip) { |
| gfx::Point host_location(location_in_dip); |
| ConvertPointToHost(&host_location); |
| MoveCursorToInternal(location_in_dip, host_location); |
| } |
| |
| void RootWindow::MoveCursorToHostLocation(const gfx::Point& host_location) { |
| gfx::Point root_location(host_location); |
| ConvertPointFromHost(&root_location); |
| MoveCursorToInternal(root_location, host_location); |
| } |
| |
| 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; |
| } |
| waiting_on_compositing_end_ = true; |
| |
| TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::Draw", |
| compositor_->last_started_frame() + 1); |
| |
| compositor_->Draw(); |
| } |
| |
| void RootWindow::ScheduleFullRedraw() { |
| compositor_->ScheduleFullRedraw(); |
| } |
| |
| void RootWindow::ScheduleRedrawRect(const gfx::Rect& damage_rect) { |
| compositor_->ScheduleRedrawRect(damage_rect); |
| } |
| |
| Window* RootWindow::GetGestureTarget(ui::GestureEvent* event) { |
| Window* target = client::GetCaptureWindow(this); |
| if (!target) { |
| target = ConsumerToWindow( |
| gesture_recognizer_->GetTargetForGestureEvent(event)); |
| } |
| |
| return target; |
| } |
| |
| bool RootWindow::DispatchGestureEvent(ui::GestureEvent* event) { |
| DispatchHeldEvents(); |
| |
| Window* target = GetGestureTarget(event); |
| if (target) { |
| event->ConvertLocationToTarget(static_cast<Window*>(this), target); |
| ProcessEvent(target, event); |
| return event->handled(); |
| } |
| |
| return false; |
| } |
| |
| void RootWindow::OnWindowDestroying(Window* window) { |
| DispatchMouseExitToHidingWindow(window); |
| OnWindowHidden(window, WINDOW_DESTROYED); |
| |
| 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::DispatchMouseExitToHidingWindow(Window* window) { |
| // The mouse capture is intentionally ignored. Think that a mouse enters |
| // to a window, the window sets the capture, the mouse exits the window, |
| // and then it releases the capture. In that case OnMouseExited won't |
| // be called. So it is natural not to emit OnMouseExited even though |
| // |window| is the capture window. |
| gfx::Point last_mouse_location = GetLastMouseLocationInRoot(); |
| if (window->Contains(mouse_moved_handler_) && |
| window->ContainsPointInRoot(last_mouse_location)) { |
| ui::MouseEvent event(ui::ET_MOUSE_EXITED, |
| last_mouse_location, |
| last_mouse_location, |
| ui::EF_NONE); |
| DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED); |
| } |
| } |
| |
| void RootWindow::OnWindowVisibilityChanged(Window* window, bool is_visible) { |
| if (!is_visible) |
| OnWindowHidden(window, WINDOW_HIDDEN); |
| |
| 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::OnRootWindowHostCloseRequested() { |
| FOR_EACH_OBSERVER(RootWindowObserver, observers_, |
| OnRootWindowHostCloseRequested(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 { |
| ConvertPointToHost(point); |
| gfx::Point location = host_->GetLocationOnNativeScreen(); |
| point->Offset(location.x(), location.y()); |
| } |
| |
| void RootWindow::ConvertPointFromNativeScreen(gfx::Point* point) const { |
| gfx::Point location = host_->GetLocationOnNativeScreen(); |
| point->Offset(-location.x(), -location.y()); |
| ConvertPointFromHost(point); |
| } |
| |
| void RootWindow::ConvertPointToHost(gfx::Point* point) const { |
| gfx::Point3F point_3f(*point); |
| GetRootTransform().TransformPoint(point_3f); |
| *point = gfx::ToFlooredPoint(point_3f.AsPointF()); |
| } |
| |
| void RootWindow::ConvertPointFromHost(gfx::Point* point) const { |
| gfx::Point3F point_3f(*point); |
| GetInverseRootTransform().TransformPoint(point_3f); |
| *point = gfx::ToFlooredPoint(point_3f.AsPointF()); |
| } |
| |
| void RootWindow::ProcessedTouchEvent(ui::TouchEvent* event, |
| Window* window, |
| ui::EventResult result) { |
| scoped_ptr<ui::GestureRecognizer::Gestures> gestures; |
| gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( |
| *event, result, window)); |
| 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::HoldPointerMoves() { |
| if (!move_hold_count_) |
| held_event_factory_.InvalidateWeakPtrs(); |
| ++move_hold_count_; |
| TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::HoldPointerMoves", this); |
| } |
| |
| void RootWindow::ReleasePointerMoves() { |
| --move_hold_count_; |
| DCHECK_GE(move_hold_count_, 0); |
| if (!move_hold_count_ && held_move_event_) { |
| // We don't want to call DispatchHeldEvents 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 HoldPointerMoves is called again before it executes. |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&RootWindow::DispatchHeldEvents, |
| held_event_factory_.GetWeakPtr())); |
| } |
| TRACE_EVENT_ASYNC_END0("ui", "RootWindow::HoldPointerMoves", this); |
| } |
| |
| void RootWindow::SetFocusWhenShown(bool focused) { |
| host_->SetFocusWhenShown(focused); |
| } |
| |
| bool RootWindow::CopyAreaToSkCanvas(const gfx::Rect& source_bounds, |
| const gfx::Point& dest_offset, |
| SkCanvas* canvas) { |
| DCHECK(canvas); |
| DCHECK(bounds().Contains(source_bounds)); |
| gfx::Rect source_pixels = ui::ConvertRectToPixel(layer(), source_bounds); |
| return host_->CopyAreaToSkCanvas(source_pixels, dest_offset, canvas); |
| } |
| |
| 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 gfx::Transform& transform) { |
| scoped_ptr<RootWindowTransformer> transformer( |
| new SimpleRootWindowTransformer(this, transform)); |
| SetRootWindowTransformer(transformer.Pass()); |
| } |
| |
| void RootWindow::SetRootWindowTransformer( |
| scoped_ptr<RootWindowTransformer> transformer) { |
| transformer_ = transformer.Pass(); |
| host_->SetInsets(transformer_->GetHostInsets()); |
| Window::SetTransform(transformer_->GetTransform()); |
| // If the layer is not animating, then we need to update the root window |
| // size immediately. |
| if (!layer()->GetAnimator()->is_animating()) |
| UpdateRootWindowSize(GetHostSize()); |
| } |
| |
| gfx::Transform RootWindow::GetRootTransform() const { |
| float scale = ui::GetDeviceScaleFactor(layer()); |
| gfx::Transform transform; |
| transform.Scale(scale, scale); |
| transform *= transformer_->GetTransform(); |
| return transform; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, ui::EventTarget implementation: |
| |
| ui::EventTarget* RootWindow::GetParentTarget() { |
| return client::GetEventClient(this) ? |
| client::GetEventClient(this)->GetToplevelEventTarget() : |
| Env::GetInstance(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, ui::CompositorDelegate implementation: |
| |
| void RootWindow::ScheduleDraw() { |
| DCHECK(!ui::Compositor::WasInitializedWithThread()); |
| if (!defer_draw_scheduling_) { |
| defer_draw_scheduling_ = true; |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&RootWindow::Draw, schedule_paint_factory_.GetWeakPtr())); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, ui::CompositorObserver implementation: |
| |
| void RootWindow::OnCompositingDidCommit(ui::Compositor*) { |
| } |
| |
| void RootWindow::OnCompositingStarted(ui::Compositor*, |
| base::TimeTicks start_time) { |
| } |
| |
| 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*) { |
| } |
| |
| void RootWindow::OnCompositingLockStateChanged(ui::Compositor*) { |
| } |
| |
| void RootWindow::OnUpdateVSyncParameters(ui::Compositor* compositor, |
| base::TimeTicks timebase, |
| base::TimeDelta interval) { |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, ui::LayerDelegate implementation: |
| |
| void RootWindow::OnDeviceScaleFactorChanged( |
| float device_scale_factor) { |
| const bool cursor_is_in_bounds = |
| GetBoundsInScreen().Contains(Env::GetInstance()->last_mouse_location()); |
| bool cursor_visible = false; |
| client::CursorClient* cursor_client = client::GetCursorClient(this); |
| if (cursor_is_in_bounds && cursor_client) { |
| cursor_visible = cursor_client->IsCursorVisible(); |
| if (cursor_visible) |
| cursor_client->HideCursor(); |
| } |
| host_->OnDeviceScaleFactorChanged(device_scale_factor); |
| Window::OnDeviceScaleFactorChanged(device_scale_factor); |
| // Update the device scale factor of the cursor client only when the last |
| // mouse location is on this root window. |
| if (cursor_is_in_bounds) { |
| if (cursor_client) { |
| const gfx::Display& display = |
| gfx::Screen::GetScreenFor(this)->GetDisplayNearestWindow(this); |
| cursor_client->SetDisplay(display); |
| } |
| } |
| if (cursor_is_in_bounds && cursor_client && cursor_visible) |
| cursor_client->ShowCursor(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, overridden from aura::Window: |
| |
| bool RootWindow::CanFocus() const { |
| return IsVisible(); |
| } |
| |
| bool RootWindow::CanReceiveEvents() const { |
| return IsVisible(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 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); |
| |
| ProcessEvent(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(); |
| } |
| |
| bool RootWindow::QueryMouseLocationForTest(gfx::Point* point) const { |
| return host_->QueryMouseLocation(point); |
| } |
| |
| void RootWindow::ClearMouseHandlers() { |
| mouse_pressed_handler_ = NULL; |
| mouse_moved_handler_ = NULL; |
| mouse_event_dispatch_target_ = NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, private: |
| |
| void RootWindow::TransformEventForDeviceScaleFactor(ui::LocatedEvent* event) { |
| event->UpdateForRootTransform(GetInverseRootTransform()); |
| } |
| |
| void RootWindow::MoveCursorToInternal(const gfx::Point& root_location, |
| const gfx::Point& host_location) { |
| host_->MoveCursorTo(host_location); |
| SetLastMouseLocation(this, root_location); |
| client::CursorClient* cursor_client = client::GetCursorClient(this); |
| if (cursor_client) { |
| const gfx::Display& display = |
| gfx::Screen::GetScreenFor(this)->GetDisplayNearestWindow(this); |
| cursor_client->SetDisplay(display); |
| } |
| synthesize_mouse_move_ = false; |
| } |
| |
| void RootWindow::HandleMouseMoved(const ui::MouseEvent& event, Window* target) { |
| if (target == mouse_moved_handler_) |
| return; |
| |
| DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED); |
| |
| if (mouse_event_dispatch_target_ != target) { |
| mouse_moved_handler_ = NULL; |
| return; |
| } |
| |
| mouse_moved_handler_ = target; |
| |
| DispatchMouseEnterOrExit(event, ui::ET_MOUSE_ENTERED); |
| } |
| |
| void RootWindow::DispatchMouseEnterOrExit(const ui::MouseEvent& event, |
| ui::EventType type) { |
| if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate()) |
| return; |
| |
| ui::MouseEvent translated_event(event, |
| static_cast<Window*>(this), |
| mouse_moved_handler_, |
| type, |
| event.flags() | ui::EF_IS_SYNTHESIZED); |
| ProcessEvent(mouse_moved_handler_, &translated_event); |
| } |
| |
| void RootWindow::ProcessEvent(Window* target, ui::Event* event) { |
| Window* old_target = event_dispatch_target_; |
| event_dispatch_target_ = target; |
| if (DispatchEvent(target, event)) |
| event_dispatch_target_ = old_target; |
| } |
| |
| bool RootWindow::ProcessGestures(ui::GestureRecognizer::Gestures* gestures) { |
| if (!gestures || gestures->empty()) |
| return false; |
| |
| Window* target = GetGestureTarget(gestures->get().at(0)); |
| Window* old_target = event_dispatch_target_; |
| event_dispatch_target_ = target; |
| |
| bool handled = false; |
| for (size_t i = 0; i < gestures->size(); ++i) { |
| ui::GestureEvent* event = gestures->get().at(i); |
| event->ConvertLocationToTarget(static_cast<Window*>(this), target); |
| if (!DispatchEvent(target, event)) |
| return false; // |this| has been destroyed. |
| if (event->handled()) |
| handled = true; |
| if (event_dispatch_target_ != target) // |target| has been destroyed. |
| break; |
| } |
| event_dispatch_target_ = old_target; |
| return handled; |
| } |
| |
| void RootWindow::OnWindowRemovedFromRootWindow(Window* detached, |
| RootWindow* new_root) { |
| DCHECK(aura::client::GetCaptureWindow(this) != this); |
| |
| DispatchMouseExitToHidingWindow(detached); |
| OnWindowHidden(detached, new_root ? WINDOW_MOVING : WINDOW_HIDDEN); |
| |
| if (detached->IsVisible() && |
| detached->ContainsPointInRoot(GetLastMouseLocationInRoot())) { |
| PostMouseMoveEventAfterWindowChange(); |
| } |
| } |
| |
| void RootWindow::OnWindowHidden(Window* invisible, WindowHiddenReason reason) { |
| // TODO(beng): This should be removed once FocusController is turned on. |
| if (client::GetFocusClient(this)) { |
| client::GetFocusClient(this)->OnWindowHiddenInRootWindow( |
| invisible, this, reason == WINDOW_DESTROYED); |
| } |
| |
| // Do not clear the capture, and the |event_dispatch_target_| if the |
| // window is moving across root windows, because the target itself |
| // is actually still visible and clearing them stops further event |
| // processing, which can cause unexpected behaviors. See |
| // crbug.com/157583 |
| if (reason != WINDOW_MOVING) { |
| 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 (invisible->Contains(event_dispatch_target_)) |
| event_dispatch_target_ = NULL; |
| } |
| |
| // 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; |
| if (invisible->Contains(mouse_event_dispatch_target_)) |
| mouse_event_dispatch_target_ = NULL; |
| |
| CleanupGestureRecognizerState(invisible); |
| } |
| |
| void RootWindow::CleanupGestureRecognizerState(Window* window) { |
| gesture_recognizer_->CleanupStateForConsumer(window); |
| Windows windows = window->children(); |
| for (Windows::const_iterator iter = windows.begin(); |
| iter != windows.end(); |
| ++iter) { |
| CleanupGestureRecognizerState(*iter); |
| } |
| } |
| |
| void RootWindow::UpdateRootWindowSize(const gfx::Size& host_size) { |
| SetBounds(transformer_->GetRootWindowBounds(host_size)); |
| } |
| |
| void RootWindow::OnWindowAddedToRootWindow(Window* attached) { |
| if (attached->IsVisible() && |
| attached->ContainsPointInRoot(GetLastMouseLocationInRoot())) |
| PostMouseMoveEventAfterWindowChange(); |
| } |
| |
| bool RootWindow::CanDispatchToTarget(ui::EventTarget* target) { |
| return event_dispatch_target_ == target; |
| } |
| |
| bool RootWindow::DispatchLongPressGestureEvent(ui::GestureEvent* event) { |
| return DispatchGestureEvent(event); |
| } |
| |
| bool RootWindow::DispatchCancelTouchEvent(ui::TouchEvent* event) { |
| return OnHostTouchEvent(event); |
| } |
| |
| void RootWindow::OnLayerAnimationEnded( |
| ui::LayerAnimationSequence* animation) { |
| UpdateRootWindowSize(GetHostSize()); |
| } |
| |
| void RootWindow::OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* animation) { |
| } |
| |
| void RootWindow::OnLayerAnimationAborted( |
| ui::LayerAnimationSequence* animation) { |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, RootWindowHostDelegate implementation: |
| |
| bool RootWindow::OnHostKeyEvent(ui::KeyEvent* event) { |
| DispatchHeldEvents(); |
| if (event->key_code() == ui::VKEY_UNKNOWN) |
| return false; |
| client::EventClient* client = client::GetEventClient(GetRootWindow()); |
| Window* focused_window = client::GetFocusClient(this)->GetFocusedWindow(); |
| if (client && !client->CanProcessEventsWithinSubtree(focused_window)) { |
| client::GetFocusClient(this)->FocusWindow(NULL); |
| return false; |
| } |
| ProcessEvent(focused_window ? focused_window : this, event); |
| return event->handled(); |
| } |
| |
| bool RootWindow::OnHostMouseEvent(ui::MouseEvent* event) { |
| if (event->type() == ui::ET_MOUSE_DRAGGED || |
| (event->flags() & ui::EF_IS_SYNTHESIZED)) { |
| if (move_hold_count_) { |
| Window* null_window = static_cast<Window*>(NULL); |
| held_move_event_.reset( |
| new ui::MouseEvent(*event, null_window, null_window)); |
| return true; |
| } else { |
| // We may have a held event for a period between the time move_hold_count_ |
| // fell to 0 and the DispatchHeldEvents executes. Since we're going to |
| // dispatch the new event directly below, we can reset the old one. |
| held_move_event_.reset(); |
| } |
| } |
| DispatchHeldEvents(); |
| return DispatchMouseEventImpl(event); |
| } |
| |
| bool RootWindow::OnHostScrollEvent(ui::ScrollEvent* event) { |
| DispatchHeldEvents(); |
| |
| TransformEventForDeviceScaleFactor(event); |
| 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 = this; |
| |
| event->ConvertLocationToTarget(static_cast<Window*>(this), target); |
| int flags = event->flags(); |
| if (IsNonClientLocation(target, event->location())) |
| flags |= ui::EF_IS_NON_CLIENT; |
| event->set_flags(flags); |
| |
| ProcessEvent(target, event); |
| return event->handled(); |
| } |
| |
| bool RootWindow::OnHostTouchEvent(ui::TouchEvent* event) { |
| if ((event->type() == ui::ET_TOUCH_MOVED)) { |
| if (move_hold_count_) { |
| Window* null_window = static_cast<Window*>(NULL); |
| held_move_event_.reset( |
| new ui::TouchEvent(*event, null_window, null_window)); |
| return true; |
| } else { |
| // We may have a held event for a period between the time move_hold_count_ |
| // fell to 0 and the DispatchHeldEvents executes. Since we're going to |
| // dispatch the new event directly below, we can reset the old one. |
| held_move_event_.reset(); |
| } |
| } |
| DispatchHeldEvents(); |
| return DispatchTouchEventImpl(event); |
| } |
| |
| void RootWindow::OnHostCancelMode() { |
| ui::CancelModeEvent event; |
| Window* focused_window = client::GetFocusClient(this)->GetFocusedWindow(); |
| ProcessEvent(focused_window ? focused_window : this, &event); |
| } |
| |
| void RootWindow::OnHostActivated() { |
| Env::GetInstance()->RootWindowActivated(this); |
| } |
| |
| void RootWindow::OnHostLostWindowCapture() { |
| Window* capture_window = client::GetCaptureWindow(this); |
| if (capture_window && capture_window->GetRootWindow() == this) |
| capture_window->ReleaseCapture(); |
| } |
| |
| void RootWindow::OnHostLostMouseGrab() { |
| ClearMouseHandlers(); |
| } |
| |
| void RootWindow::OnHostPaint(const gfx::Rect& damage_rect) { |
| compositor_->ScheduleRedrawRect(damage_rect); |
| } |
| |
| void RootWindow::OnHostMoved(const gfx::Point& origin) { |
| FOR_EACH_OBSERVER(RootWindowObserver, observers_, |
| OnRootWindowHostMoved(this, origin)); |
| } |
| |
| void RootWindow::OnHostResized(const gfx::Size& size) { |
| DispatchHeldEvents(); |
| // 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); |
| |
| // The layer, and the observers should be notified of the |
| // transformed size of the root window. |
| UpdateRootWindowSize(size); |
| FOR_EACH_OBSERVER(RootWindowObserver, observers_, |
| OnRootWindowHostResized(this)); |
| } |
| |
| float RootWindow::GetDeviceScaleFactor() { |
| return compositor()->device_scale_factor(); |
| } |
| |
| RootWindow* RootWindow::AsRootWindow() { |
| return this; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // RootWindow, private: |
| |
| bool RootWindow::DispatchMouseEventImpl(ui::MouseEvent* event) { |
| TransformEventForDeviceScaleFactor(event); |
| Window* target = mouse_pressed_handler_ ? |
| mouse_pressed_handler_ : client::GetCaptureWindow(this); |
| if (!target) |
| target = GetEventHandlerForPoint(event->location()); |
| return DispatchMouseEventToTarget(event, target); |
| } |
| |
| bool RootWindow::DispatchMouseEventRepost(ui::MouseEvent* event) { |
| if (event->type() != ui::ET_MOUSE_PRESSED) |
| return false; |
| mouse_pressed_handler_ = NULL; |
| Window* target = GetEventHandlerForPoint(event->location()); |
| return DispatchMouseEventToTarget(event, target); |
| } |
| |
| bool RootWindow::DispatchMouseEventToTarget(ui::MouseEvent* event, |
| Window* target) { |
| client::CursorClient* cursor_client = client::GetCursorClient(this); |
| if (cursor_client && |
| !cursor_client->IsMouseEventsEnabled() && |
| (event->flags() & ui::EF_IS_SYNTHESIZED)) |
| return false; |
| |
| static const int kMouseButtonFlagMask = |
| ui::EF_LEFT_MOUSE_BUTTON | |
| ui::EF_MIDDLE_MOUSE_BUTTON | |
| ui::EF_RIGHT_MOUSE_BUTTON; |
| base::AutoReset<Window*> reset(&mouse_event_dispatch_target_, target); |
| SetLastMouseLocation(this, event->location()); |
| synthesize_mouse_move_ = false; |
| switch (event->type()) { |
| case ui::ET_MOUSE_EXITED: |
| if (!target) { |
| DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED); |
| mouse_moved_handler_ = NULL; |
| } |
| break; |
| case ui::ET_MOUSE_MOVED: |
| mouse_event_dispatch_target_ = target; |
| HandleMouseMoved(*event, target); |
| if (mouse_event_dispatch_target_ != target) |
| return false; |
| break; |
| case ui::ET_MOUSE_PRESSED: |
| // Don't set the mouse pressed handler for non client mouse down events. |
| // These are only sent by Windows and are not always followed with non |
| // client mouse up events which causes subsequent mouse events to be |
| // sent to the wrong target. |
| if (!(event->flags() & ui::EF_IS_NON_CLIENT) && !mouse_pressed_handler_) |
| mouse_pressed_handler_ = target; |
| Env::GetInstance()->set_mouse_button_flags( |
| event->flags() & kMouseButtonFlagMask); |
| break; |
| case ui::ET_MOUSE_RELEASED: |
| mouse_pressed_handler_ = NULL; |
| Env::GetInstance()->set_mouse_button_flags(event->flags() & |
| kMouseButtonFlagMask & ~event->changed_button_flags()); |
| break; |
| default: |
| break; |
| } |
| if (target) { |
| event->ConvertLocationToTarget(static_cast<Window*>(this), target); |
| if (IsNonClientLocation(target, event->location())) |
| event->set_flags(event->flags() | ui::EF_IS_NON_CLIENT); |
| ProcessEvent(target, event); |
| return event->handled(); |
| } |
| return false; |
| } |
| |
| bool RootWindow::DispatchTouchEventImpl(ui::TouchEvent* event) { |
| 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; |
| |
| // Handle ET_TOUCH_CANCELLED only if it has a native event. |
| case ui::ET_TOUCH_CANCELLED: |
| if (!event->HasNativeEvent()) |
| break; |
| // fallthrough |
| 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; |
| } |
| TransformEventForDeviceScaleFactor(event); |
| bool handled = false; |
| Window* target = client::GetCaptureWindow(this); |
| if (!target) { |
| target = ConsumerToWindow( |
| gesture_recognizer_->GetTouchLockedTarget(event)); |
| if (!target) { |
| target = ConsumerToWindow( |
| gesture_recognizer_->GetTargetForLocation(event->location())); |
| } |
| } |
| |
| // The gesture recognizer processes touch events in the system coordinates. So |
| // keep a copy of the touch event here before possibly converting the event to |
| // a window's local coordinate system. |
| ui::TouchEvent event_for_gr(*event); |
| |
| ui::EventResult result = ui::ER_UNHANDLED; |
| if (!target && !bounds().Contains(event->location())) { |
| // If the initial touch is outside the root window, target the root. |
| target = this; |
| ProcessEvent(target ? target : NULL, event); |
| result = event->result(); |
| } else { |
| // We only come here when the first contact was within the root window. |
| if (!target) { |
| target = GetEventHandlerForPoint(event->location()); |
| if (!target) |
| return false; |
| } |
| |
| event->ConvertLocationToTarget(static_cast<Window*>(this), target); |
| ProcessEvent(target, event); |
| handled = event->handled(); |
| result = event->result(); |
| } |
| |
| // Get the list of GestureEvents from GestureRecognizer. |
| scoped_ptr<ui::GestureRecognizer::Gestures> gestures; |
| gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( |
| event_for_gr, result, target)); |
| |
| return ProcessGestures(gestures.get()) ? true : handled; |
| } |
| |
| void RootWindow::DispatchHeldEvents() { |
| if (held_repostable_event_) { |
| if (held_repostable_event_->type() == ui::ET_MOUSE_PRESSED) { |
| ui::MouseEvent mouse_event( |
| static_cast<const ui::MouseEvent&>(*held_repostable_event_.get())); |
| held_repostable_event_.reset(); // must be reset before dispatch |
| DispatchMouseEventRepost(&mouse_event); |
| } else { |
| DCHECK(held_repostable_event_->type() == ui::ET_GESTURE_TAP_DOWN); |
| // TODO(sschmitz): add similar code for gesture events |
| } |
| held_repostable_event_.reset(); |
| } |
| if (held_move_event_ && held_move_event_->IsMouseEvent()) { |
| // If a mouse move has been synthesized, the target location is suspect, |
| // so drop the held event. |
| if (!synthesize_mouse_move_) |
| DispatchMouseEventImpl( |
| static_cast<ui::MouseEvent*>(held_move_event_.get())); |
| held_move_event_.reset(); |
| } else if (held_move_event_ && held_move_event_->IsTouchEvent()) { |
| DispatchTouchEventImpl( |
| static_cast<ui::TouchEvent*>(held_move_event_.get())); |
| held_move_event_.reset(); |
| } |
| } |
| |
| void RootWindow::PostMouseMoveEventAfterWindowChange() { |
| if (synthesize_mouse_move_) |
| return; |
| synthesize_mouse_move_ = true; |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&RootWindow::SynthesizeMouseMoveEvent, |
| event_factory_.GetWeakPtr())); |
| } |
| |
| void RootWindow::SynthesizeMouseMoveEvent() { |
| if (!synthesize_mouse_move_) |
| return; |
| synthesize_mouse_move_ = false; |
| gfx::Point root_mouse_location = GetLastMouseLocationInRoot(); |
| if (!bounds().Contains(root_mouse_location)) |
| return; |
| gfx::Point host_mouse_location = root_mouse_location; |
| ConvertPointToHost(&host_mouse_location); |
| |
| ui::MouseEvent event(ui::ET_MOUSE_MOVED, |
| host_mouse_location, |
| host_mouse_location, |
| ui::EF_IS_SYNTHESIZED); |
| OnHostMouseEvent(&event); |
| } |
| |
| gfx::Transform RootWindow::GetInverseRootTransform() const { |
| float scale = ui::GetDeviceScaleFactor(layer()); |
| gfx::Transform transform; |
| transform.Scale(1.0f / scale, 1.0f / scale); |
| return transformer_->GetInverseTransform() * transform; |
| } |
| |
| } // namespace aura |