| // Copyright 2016 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 "services/ui/ws/window_manager_state.h" |
| |
| #include <utility> |
| |
| #include "base/memory/weak_ptr.h" |
| #include "services/service_manager/public/interfaces/connector.mojom.h" |
| #include "services/ui/common/accelerator_util.h" |
| #include "services/ui/ws/accelerator.h" |
| #include "services/ui/ws/cursor_location_manager.h" |
| #include "services/ui/ws/display.h" |
| #include "services/ui/ws/display_manager.h" |
| #include "services/ui/ws/platform_display.h" |
| #include "services/ui/ws/server_window.h" |
| #include "services/ui/ws/user_display_manager.h" |
| #include "services/ui/ws/user_id_tracker.h" |
| #include "services/ui/ws/window_manager_display_root.h" |
| #include "services/ui/ws/window_server.h" |
| #include "services/ui/ws/window_tree.h" |
| #include "ui/events/event.h" |
| |
| namespace ui { |
| namespace ws { |
| namespace { |
| |
| // Flags that matter when checking if a key event matches an accelerator. |
| const int kAcceleratorEventFlags = |
| EF_SHIFT_DOWN | EF_CONTROL_DOWN | EF_ALT_DOWN | EF_COMMAND_DOWN; |
| |
| base::TimeDelta GetDefaultAckTimerDelay() { |
| #if defined(NDEBUG) |
| return base::TimeDelta::FromMilliseconds(100); |
| #else |
| return base::TimeDelta::FromMilliseconds(1000); |
| #endif |
| } |
| |
| bool EventsCanBeCoalesced(const ui::Event& one, const ui::Event& two) { |
| if (one.type() != two.type() || one.flags() != two.flags()) |
| return false; |
| |
| // TODO(sad): wheel events can also be merged. |
| if (one.type() != ui::ET_POINTER_MOVED) |
| return false; |
| |
| return one.AsPointerEvent()->pointer_details().id == |
| two.AsPointerEvent()->pointer_details().id; |
| } |
| |
| std::unique_ptr<ui::Event> CoalesceEvents(std::unique_ptr<ui::Event> first, |
| std::unique_ptr<ui::Event> second) { |
| DCHECK(first->type() == ui::ET_POINTER_MOVED) |
| << " Non-move events cannot be merged yet."; |
| // For mouse moves, the new event just replaces the old event. |
| return second; |
| } |
| |
| const ServerWindow* GetEmbedRoot(const ServerWindow* window) { |
| DCHECK(window); |
| const ServerWindow* embed_root = window->parent(); |
| while (embed_root && embed_root->id().client_id == window->id().client_id) |
| embed_root = embed_root->parent(); |
| return embed_root; |
| } |
| |
| } // namespace |
| |
| WindowManagerState::InFlightEventDetails::InFlightEventDetails( |
| WindowTree* tree, |
| int64_t display_id, |
| const Event& event, |
| EventDispatchPhase phase) |
| : tree(tree), |
| display_id(display_id), |
| event(Event::Clone(event)), |
| phase(phase) {} |
| |
| WindowManagerState::InFlightEventDetails::~InFlightEventDetails() {} |
| |
| class WindowManagerState::ProcessedEventTarget { |
| public: |
| ProcessedEventTarget(ServerWindow* window, |
| ClientSpecificId client_id, |
| Accelerator* accelerator) |
| : client_id_(client_id) { |
| tracker_.Add(window); |
| if (accelerator) |
| accelerator_ = accelerator->GetWeakPtr(); |
| } |
| |
| ~ProcessedEventTarget() {} |
| |
| // Return true if the event is still valid. The event becomes invalid if |
| // the window is destroyed while waiting to dispatch. |
| bool IsValid() const { return !tracker_.windows().empty(); } |
| |
| ServerWindow* window() { |
| DCHECK(IsValid()); |
| return tracker_.windows().front(); |
| } |
| |
| ClientSpecificId client_id() const { return client_id_; } |
| |
| base::WeakPtr<Accelerator> accelerator() { return accelerator_; } |
| |
| private: |
| ServerWindowTracker tracker_; |
| const ClientSpecificId client_id_; |
| base::WeakPtr<Accelerator> accelerator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ProcessedEventTarget); |
| }; |
| |
| bool WindowManagerState::DebugAccelerator::Matches( |
| const ui::KeyEvent& event) const { |
| return key_code == event.key_code() && |
| event_flags == (kAcceleratorEventFlags & event.flags()) && |
| !event.is_char(); |
| } |
| |
| WindowManagerState::QueuedEvent::QueuedEvent() {} |
| WindowManagerState::QueuedEvent::~QueuedEvent() {} |
| |
| WindowManagerState::WindowManagerState(WindowTree* window_tree) |
| : window_tree_(window_tree), event_dispatcher_(this), weak_factory_(this) { |
| frame_decoration_values_ = mojom::FrameDecorationValues::New(); |
| frame_decoration_values_->max_title_bar_button_width = 0u; |
| |
| AddDebugAccelerators(); |
| } |
| |
| WindowManagerState::~WindowManagerState() { |
| for (auto& display_root : window_manager_display_roots_) |
| display_root->display()->OnWillDestroyTree(window_tree_); |
| |
| for (auto& display_root : orphaned_window_manager_display_roots_) |
| display_root->root()->RemoveObserver(this); |
| } |
| |
| void WindowManagerState::SetFrameDecorationValues( |
| mojom::FrameDecorationValuesPtr values) { |
| got_frame_decoration_values_ = true; |
| frame_decoration_values_ = values.Clone(); |
| display_manager() |
| ->GetUserDisplayManager(user_id()) |
| ->OnFrameDecorationValuesChanged(); |
| } |
| |
| bool WindowManagerState::SetCapture(ServerWindow* window, |
| ClientSpecificId client_id) { |
| DCHECK(IsActive()); |
| if (capture_window() == window && |
| client_id == event_dispatcher_.capture_window_client_id()) { |
| return true; |
| } |
| #if !defined(NDEBUG) |
| if (window) { |
| WindowManagerDisplayRoot* display_root = |
| display_manager()->GetWindowManagerDisplayRoot(window); |
| DCHECK(display_root && display_root->window_manager_state() == this); |
| } |
| #endif |
| return event_dispatcher_.SetCaptureWindow(window, client_id); |
| } |
| |
| void WindowManagerState::ReleaseCaptureBlockedByModalWindow( |
| const ServerWindow* modal_window) { |
| event_dispatcher_.ReleaseCaptureBlockedByModalWindow(modal_window); |
| } |
| |
| void WindowManagerState::ReleaseCaptureBlockedByAnyModalWindow() { |
| event_dispatcher_.ReleaseCaptureBlockedByAnyModalWindow(); |
| } |
| |
| void WindowManagerState::SetDragDropSourceWindow( |
| DragSource* drag_source, |
| ServerWindow* window, |
| DragTargetConnection* source_connection, |
| const std::unordered_map<std::string, std::vector<uint8_t>>& drag_data, |
| uint32_t drag_operation) { |
| int32_t drag_pointer = PointerEvent::kMousePointerId; |
| if (in_flight_event_details_ && |
| in_flight_event_details_->event->IsPointerEvent()) { |
| drag_pointer = |
| in_flight_event_details_->event->AsPointerEvent()->pointer_details().id; |
| } else { |
| NOTIMPLEMENTED() << "Set drag drop set up during something other than a " |
| << "pointer event; rejecting drag."; |
| drag_source->OnDragCompleted(false, ui::mojom::kDropEffectNone); |
| return; |
| } |
| |
| event_dispatcher_.SetDragDropSourceWindow(drag_source, window, |
| source_connection, drag_pointer, |
| drag_data, drag_operation); |
| } |
| |
| void WindowManagerState::CancelDragDrop() { |
| event_dispatcher_.CancelDragDrop(); |
| } |
| |
| void WindowManagerState::EndDragDrop() { |
| event_dispatcher_.EndDragDrop(); |
| UpdateNativeCursorFromDispatcher(); |
| } |
| |
| void WindowManagerState::AddSystemModalWindow(ServerWindow* window) { |
| DCHECK(!window->transient_parent()); |
| event_dispatcher_.AddSystemModalWindow(window); |
| } |
| |
| const UserId& WindowManagerState::user_id() const { |
| return window_tree_->user_id(); |
| } |
| |
| void WindowManagerState::OnWillDestroyTree(WindowTree* tree) { |
| event_dispatcher_.OnWillDestroyDragTargetConnection(tree); |
| |
| if (!in_flight_event_details_ || in_flight_event_details_->tree != tree) |
| return; |
| |
| // The WindowTree is dying. So it's not going to ack the event. |
| // If the dying tree matches the root |tree_| marked as handled so we don't |
| // notify it of accelerators. |
| OnEventAck(in_flight_event_details_->tree, |
| tree == window_tree_ ? mojom::EventResult::HANDLED |
| : mojom::EventResult::UNHANDLED); |
| } |
| |
| ServerWindow* WindowManagerState::GetOrphanedRootWithId(const WindowId& id) { |
| for (auto& display_root_ptr : orphaned_window_manager_display_roots_) { |
| if (display_root_ptr->root()->id() == id) |
| return display_root_ptr->root(); |
| } |
| return nullptr; |
| } |
| |
| bool WindowManagerState::IsActive() const { |
| return window_server()->user_id_tracker()->active_id() == user_id(); |
| } |
| |
| void WindowManagerState::Activate(const gfx::Point& mouse_location_on_screen) { |
| SetAllRootWindowsVisible(true); |
| event_dispatcher_.Reset(); |
| event_dispatcher_.SetMousePointerScreenLocation(mouse_location_on_screen); |
| } |
| |
| void WindowManagerState::Deactivate() { |
| SetAllRootWindowsVisible(false); |
| event_dispatcher_.Reset(); |
| // The tree is no longer active, so no point in dispatching any further |
| // events. |
| std::queue<std::unique_ptr<QueuedEvent>> event_queue; |
| event_queue.swap(event_queue_); |
| } |
| |
| void WindowManagerState::ProcessEvent(const ui::Event& event, |
| int64_t display_id) { |
| // If this is still waiting for an ack from a previously sent event, then |
| // queue up the event to be dispatched once the ack is received. |
| if (in_flight_event_details_) { |
| if (!event_queue_.empty() && !event_queue_.back()->processed_target && |
| EventsCanBeCoalesced(*event_queue_.back()->event, event)) { |
| event_queue_.back()->event = CoalesceEvents( |
| std::move(event_queue_.back()->event), ui::Event::Clone(event)); |
| event_queue_.back()->display_id = display_id; |
| return; |
| } |
| QueueEvent(event, nullptr, display_id); |
| return; |
| } |
| |
| ProcessEventImpl(event, display_id); |
| } |
| |
| void WindowManagerState::OnEventAck(mojom::WindowTree* tree, |
| mojom::EventResult result) { |
| if (!in_flight_event_details_ || in_flight_event_details_->tree != tree || |
| in_flight_event_details_->phase != EventDispatchPhase::TARGET) { |
| // TODO(sad): The ack must have arrived after the timeout. We should do |
| // something here, and in OnEventAckTimeout(). |
| return; |
| } |
| std::unique_ptr<InFlightEventDetails> details = |
| std::move(in_flight_event_details_); |
| |
| base::WeakPtr<Accelerator> post_target_accelerator = post_target_accelerator_; |
| post_target_accelerator_.reset(); |
| |
| if (result == mojom::EventResult::UNHANDLED && post_target_accelerator) { |
| OnAccelerator(post_target_accelerator->id(), *details->event, |
| AcceleratorPhase::POST); |
| } |
| |
| ProcessNextEventFromQueue(); |
| } |
| |
| void WindowManagerState::OnAcceleratorAck(mojom::EventResult result) { |
| if (!in_flight_event_details_ || |
| in_flight_event_details_->phase != |
| EventDispatchPhase::PRE_TARGET_ACCELERATOR) { |
| // TODO(sad): The ack must have arrived after the timeout. We should do |
| // something here, and in OnEventAckTimeout(). |
| return; |
| } |
| |
| std::unique_ptr<InFlightEventDetails> details = |
| std::move(in_flight_event_details_); |
| |
| if (result == mojom::EventResult::UNHANDLED) { |
| event_processing_display_id_ = details->display_id; |
| event_dispatcher_.ProcessEvent( |
| *details->event, EventDispatcher::AcceleratorMatchPhase::POST_ONLY); |
| } else { |
| // We're not going to process the event any further, notify event observers. |
| // We don't do this first to ensure we don't send an event twice to clients. |
| window_server()->SendToPointerWatchers(*details->event, user_id(), nullptr, |
| nullptr, details->display_id); |
| ProcessNextEventFromQueue(); |
| } |
| } |
| |
| const WindowServer* WindowManagerState::window_server() const { |
| return window_tree_->window_server(); |
| } |
| |
| WindowServer* WindowManagerState::window_server() { |
| return window_tree_->window_server(); |
| } |
| |
| DisplayManager* WindowManagerState::display_manager() { |
| return window_tree_->display_manager(); |
| } |
| |
| const DisplayManager* WindowManagerState::display_manager() const { |
| return window_tree_->display_manager(); |
| } |
| |
| void WindowManagerState::AddWindowManagerDisplayRoot( |
| std::unique_ptr<WindowManagerDisplayRoot> display_root) { |
| window_manager_display_roots_.push_back(std::move(display_root)); |
| } |
| |
| void WindowManagerState::OnDisplayDestroying(Display* display) { |
| if (display->platform_display() == platform_display_with_capture_) |
| platform_display_with_capture_ = nullptr; |
| |
| for (auto iter = window_manager_display_roots_.begin(); |
| iter != window_manager_display_roots_.end(); ++iter) { |
| if ((*iter)->display() == display) { |
| (*iter)->root()->AddObserver(this); |
| orphaned_window_manager_display_roots_.push_back(std::move(*iter)); |
| window_manager_display_roots_.erase(iter); |
| window_tree_->OnDisplayDestroying(display->GetId()); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| void WindowManagerState::SetAllRootWindowsVisible(bool value) { |
| for (auto& display_root_ptr : window_manager_display_roots_) |
| display_root_ptr->root()->SetVisible(value); |
| } |
| |
| ServerWindow* WindowManagerState::GetWindowManagerRoot(ServerWindow* window) { |
| for (auto& display_root_ptr : window_manager_display_roots_) { |
| if (display_root_ptr->root()->parent() == window) |
| return display_root_ptr->root(); |
| } |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| void WindowManagerState::OnEventAckTimeout(ClientSpecificId client_id) { |
| WindowTree* hung_tree = window_server()->GetTreeWithId(client_id); |
| if (hung_tree && !hung_tree->janky()) |
| window_tree_->ClientJankinessChanged(hung_tree); |
| if (in_flight_event_details_->phase == |
| EventDispatchPhase::PRE_TARGET_ACCELERATOR) { |
| OnAcceleratorAck(mojom::EventResult::UNHANDLED); |
| } else { |
| OnEventAck( |
| in_flight_event_details_ ? in_flight_event_details_->tree : nullptr, |
| mojom::EventResult::UNHANDLED); |
| } |
| } |
| |
| void WindowManagerState::ProcessEventImpl(const ui::Event& event, |
| int64_t display_id) { |
| // Debug accelerators are always checked and don't interfere with processing. |
| ProcessDebugAccelerator(event); |
| event_processing_display_id_ = display_id; |
| event_dispatcher_.ProcessEvent(event, |
| EventDispatcher::AcceleratorMatchPhase::ANY); |
| } |
| |
| void WindowManagerState::QueueEvent( |
| const ui::Event& event, |
| std::unique_ptr<ProcessedEventTarget> processed_event_target, |
| int64_t display_id) { |
| std::unique_ptr<QueuedEvent> queued_event(new QueuedEvent); |
| queued_event->event = ui::Event::Clone(event); |
| queued_event->processed_target = std::move(processed_event_target); |
| queued_event->display_id = display_id; |
| event_queue_.push(std::move(queued_event)); |
| } |
| |
| void WindowManagerState::ProcessNextEventFromQueue() { |
| // Loop through |event_queue_| stopping after dispatching the first valid |
| // event. |
| while (!event_queue_.empty()) { |
| std::unique_ptr<QueuedEvent> queued_event = std::move(event_queue_.front()); |
| event_queue_.pop(); |
| if (!queued_event->processed_target) { |
| ProcessEventImpl(*queued_event->event, queued_event->display_id); |
| return; |
| } |
| if (queued_event->processed_target->IsValid()) { |
| DispatchInputEventToWindowImpl( |
| queued_event->processed_target->window(), |
| queued_event->processed_target->client_id(), *queued_event->event, |
| queued_event->processed_target->accelerator()); |
| return; |
| } |
| } |
| } |
| |
| void WindowManagerState::DispatchInputEventToWindowImpl( |
| ServerWindow* target, |
| ClientSpecificId client_id, |
| const ui::Event& event, |
| base::WeakPtr<Accelerator> accelerator) { |
| DCHECK(target); |
| if (target->parent() == nullptr) |
| target = GetWindowManagerRoot(target); |
| |
| if (event.IsMousePointerEvent()) { |
| DCHECK(event_dispatcher_.mouse_cursor_source_window()); |
| UpdateNativeCursorFromDispatcher(); |
| } |
| |
| WindowTree* tree = window_server()->GetTreeWithId(client_id); |
| DCHECK(tree); |
| ScheduleInputEventTimeout(tree, target, event, EventDispatchPhase::TARGET); |
| |
| if (accelerator) |
| post_target_accelerator_ = accelerator; |
| |
| // Ignore |tree| because it will receive the event via normal dispatch. |
| window_server()->SendToPointerWatchers(event, user_id(), target, tree, |
| in_flight_event_details_->display_id); |
| |
| tree->DispatchInputEvent(target, event); |
| } |
| |
| void WindowManagerState::AddDebugAccelerators() { |
| const DebugAccelerator accelerator = { |
| DebugAcceleratorType::PRINT_WINDOWS, ui::VKEY_S, |
| ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN}; |
| debug_accelerators_.push_back(accelerator); |
| } |
| |
| void WindowManagerState::ProcessDebugAccelerator(const ui::Event& event) { |
| if (event.type() != ui::ET_KEY_PRESSED) |
| return; |
| |
| const ui::KeyEvent& key_event = *event.AsKeyEvent(); |
| for (const DebugAccelerator& accelerator : debug_accelerators_) { |
| if (accelerator.Matches(key_event)) { |
| HandleDebugAccelerator(accelerator.type); |
| break; |
| } |
| } |
| } |
| |
| void WindowManagerState::HandleDebugAccelerator(DebugAcceleratorType type) { |
| #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) |
| // Error so it will be collected in system logs. |
| for (Display* display : display_manager()->displays()) { |
| WindowManagerDisplayRoot* display_root = |
| display->GetWindowManagerDisplayRootForUser(user_id()); |
| if (display_root) { |
| LOG(ERROR) << "ServerWindow hierarchy:\n" |
| << display_root->root()->GetDebugWindowHierarchy(); |
| } |
| } |
| ServerWindow* focused_window = GetFocusedWindowForEventDispatcher(); |
| LOG(ERROR) << "Focused window: " |
| << (focused_window ? focused_window->id().ToString() : "(null)"); |
| #endif |
| } |
| |
| void WindowManagerState::ScheduleInputEventTimeout(WindowTree* tree, |
| ServerWindow* target, |
| const Event& event, |
| EventDispatchPhase phase) { |
| std::unique_ptr<InFlightEventDetails> details = |
| base::MakeUnique<InFlightEventDetails>(tree, event_processing_display_id_, |
| event, phase); |
| |
| // TOOD(sad): Adjust this delay, possibly make this dynamic. |
| const base::TimeDelta max_delay = base::debug::BeingDebugged() |
| ? base::TimeDelta::FromDays(1) |
| : GetDefaultAckTimerDelay(); |
| details->timer.Start(FROM_HERE, max_delay, |
| base::Bind(&WindowManagerState::OnEventAckTimeout, |
| weak_factory_.GetWeakPtr(), tree->id())); |
| in_flight_event_details_ = std::move(details); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // EventDispatcherDelegate: |
| |
| void WindowManagerState::OnAccelerator(uint32_t accelerator_id, |
| const ui::Event& event, |
| AcceleratorPhase phase) { |
| DCHECK(IsActive()); |
| const bool needs_ack = phase == AcceleratorPhase::PRE; |
| if (needs_ack) { |
| DCHECK(!in_flight_event_details_); |
| ScheduleInputEventTimeout(window_tree_, nullptr, event, |
| EventDispatchPhase::PRE_TARGET_ACCELERATOR); |
| } |
| window_tree_->OnAccelerator(accelerator_id, event, needs_ack); |
| } |
| |
| void WindowManagerState::SetFocusedWindowFromEventDispatcher( |
| ServerWindow* new_focused_window) { |
| DCHECK(IsActive()); |
| window_server()->SetFocusedWindow(new_focused_window); |
| } |
| |
| ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher() { |
| return window_server()->GetFocusedWindow(); |
| } |
| |
| void WindowManagerState::SetNativeCapture(ServerWindow* window) { |
| DCHECK(window); |
| DCHECK(IsActive()); |
| WindowManagerDisplayRoot* display_root = |
| display_manager()->GetWindowManagerDisplayRoot(window); |
| DCHECK(display_root); |
| platform_display_with_capture_ = display_root->display()->platform_display(); |
| platform_display_with_capture_->SetCapture(); |
| } |
| |
| void WindowManagerState::ReleaseNativeCapture() { |
| // Tests trigger calling this without a corresponding SetNativeCapture(). |
| // TODO(sky): maybe abstract this away so that DCHECK can be added? |
| if (!platform_display_with_capture_) |
| return; |
| |
| platform_display_with_capture_->ReleaseCapture(); |
| platform_display_with_capture_ = nullptr; |
| } |
| |
| void WindowManagerState::UpdateNativeCursorFromDispatcher() { |
| const ui::mojom::Cursor cursor_id = event_dispatcher_.GetCurrentMouseCursor(); |
| for (Display* display : display_manager()->displays()) |
| display->UpdateNativeCursor(cursor_id); |
| } |
| |
| void WindowManagerState::OnCaptureChanged(ServerWindow* new_capture, |
| ServerWindow* old_capture) { |
| window_server()->ProcessCaptureChanged(new_capture, old_capture); |
| } |
| |
| void WindowManagerState::OnMouseCursorLocationChanged(const gfx::Point& point) { |
| window_server() |
| ->display_manager() |
| ->GetCursorLocationManager(user_id()) |
| ->OnMouseCursorLocationChanged(point); |
| } |
| |
| void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target, |
| ClientSpecificId client_id, |
| const ui::Event& event, |
| Accelerator* accelerator) { |
| DCHECK(IsActive()); |
| // TODO(sky): this needs to see if another wms has capture and if so forward |
| // to it. |
| if (in_flight_event_details_) { |
| std::unique_ptr<ProcessedEventTarget> processed_event_target( |
| new ProcessedEventTarget(target, client_id, accelerator)); |
| QueueEvent(event, std::move(processed_event_target), |
| event_processing_display_id_); |
| return; |
| } |
| |
| base::WeakPtr<Accelerator> weak_accelerator; |
| if (accelerator) |
| weak_accelerator = accelerator->GetWeakPtr(); |
| DispatchInputEventToWindowImpl(target, client_id, event, weak_accelerator); |
| } |
| |
| ClientSpecificId WindowManagerState::GetEventTargetClientId( |
| const ServerWindow* window, |
| bool in_nonclient_area) { |
| if (in_nonclient_area) { |
| // Events in the non-client area always go to the window manager. |
| return window_tree_->id(); |
| } |
| |
| // If the window is an embed root, it goes to the tree embedded in the window. |
| WindowTree* tree = window_server()->GetTreeWithRoot(window); |
| if (!tree) { |
| // Window is not an embed root, event goes to owner of the window. |
| tree = window_server()->GetTreeWithId(window->id().client_id); |
| } |
| DCHECK(tree); |
| |
| // Ascend to the first tree marked as not embedder_intercepts_events(). |
| const ServerWindow* embed_root = |
| tree->HasRoot(window) ? window : GetEmbedRoot(window); |
| while (tree && tree->embedder_intercepts_events()) { |
| DCHECK(tree->HasRoot(embed_root)); |
| tree = window_server()->GetTreeWithId(embed_root->id().client_id); |
| embed_root = GetEmbedRoot(embed_root); |
| } |
| DCHECK(tree); |
| return tree->id(); |
| } |
| |
| ServerWindow* WindowManagerState::GetRootWindowContaining( |
| gfx::Point* location) { |
| if (window_manager_display_roots_.empty()) |
| return nullptr; |
| |
| WindowManagerDisplayRoot* target_display_root = nullptr; |
| for (auto& display_root_ptr : window_manager_display_roots_) { |
| if (display_root_ptr->display()->platform_display()->GetBounds().Contains( |
| *location)) { |
| target_display_root = display_root_ptr.get(); |
| break; |
| } |
| } |
| |
| // TODO(kylechar): Better handle locations outside the window. Overlapping X11 |
| // windows, dragging and touch sensors need to be handled properly. |
| if (!target_display_root) { |
| DVLOG(1) << "Invalid event location " << location->ToString(); |
| target_display_root = window_manager_display_roots_.begin()->get(); |
| } |
| |
| // Translate the location to be relative to the display instead of relative |
| // to the screen space. |
| gfx::Point origin = |
| target_display_root->display()->platform_display()->GetBounds().origin(); |
| *location -= origin.OffsetFromOrigin(); |
| return target_display_root->root(); |
| } |
| |
| void WindowManagerState::OnEventTargetNotFound(const ui::Event& event) { |
| window_server()->SendToPointerWatchers(event, user_id(), nullptr, /* window */ |
| nullptr /* ignore_tree */, |
| event_processing_display_id_); |
| if (event.IsMousePointerEvent()) |
| UpdateNativeCursorFromDispatcher(); |
| } |
| |
| void WindowManagerState::OnWindowEmbeddedAppDisconnected(ServerWindow* window) { |
| for (auto iter = orphaned_window_manager_display_roots_.begin(); |
| iter != orphaned_window_manager_display_roots_.end(); ++iter) { |
| if ((*iter)->root() == window) { |
| window->RemoveObserver(this); |
| orphaned_window_manager_display_roots_.erase(iter); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| } // namespace ws |
| } // namespace ui |