| // Copyright 2015 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/event_processor.h" |
| |
| #include <algorithm> |
| |
| #include "base/time/time.h" |
| #include "services/ui/ws/accelerator.h" |
| #include "services/ui/ws/drag_controller.h" |
| #include "services/ui/ws/drag_source.h" |
| #include "services/ui/ws/event_dispatcher.h" |
| #include "services/ui/ws/event_location.h" |
| #include "services/ui/ws/event_processor_delegate.h" |
| #include "services/ui/ws/server_window.h" |
| #include "services/ui/ws/server_window_delegate.h" |
| #include "services/ui/ws/server_window_drawn_tracker.h" |
| #include "services/ui/ws/window_coordinate_conversions.h" |
| #include "services/ui/ws/window_finder.h" |
| #include "ui/base/cursor/cursor.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/point_conversions.h" |
| |
| namespace ui { |
| namespace ws { |
| |
| using Entry = std::pair<uint32_t, std::unique_ptr<Accelerator>>; |
| |
| namespace { |
| |
| bool IsOnlyOneMouseButtonDown(int flags) { |
| const uint32_t button_only_flags = |
| flags & (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON | |
| ui::EF_RIGHT_MOUSE_BUTTON); |
| return button_only_flags == ui::EF_LEFT_MOUSE_BUTTON || |
| button_only_flags == ui::EF_MIDDLE_MOUSE_BUTTON || |
| button_only_flags == ui::EF_RIGHT_MOUSE_BUTTON; |
| } |
| |
| // This is meant to mirror when implicit capture stops. Specifically non-mouse |
| // pointer up, or mouse and no more buttons down. |
| bool IsPointerGoingUp(const Event& event) { |
| return event.IsPointerEvent() && |
| (event.type() == ui::ET_POINTER_UP || |
| event.type() == ui::ET_POINTER_CANCELLED) && |
| (!event.IsMousePointerEvent() || |
| IsOnlyOneMouseButtonDown(event.flags())); |
| } |
| |
| // Get the pointer id from an event, only supports PointerEvents for now. |
| int32_t GetPointerId(const Event& event) { |
| if (event.IsPointerEvent()) |
| return event.AsPointerEvent()->pointer_details().id; |
| return PointerDetails::kUnknownPointerId; |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| EventProcessor::ObservedWindow::ObservedWindow() = default; |
| EventProcessor::ObservedWindow::~ObservedWindow() = default; |
| |
| EventProcessor::EventProcessor(EventProcessorDelegate* delegate, |
| EventDispatcher* event_dispatcher) |
| : delegate_(delegate), |
| event_dispatcher_(event_dispatcher), |
| capture_window_(nullptr), |
| capture_window_client_id_(kInvalidClientId), |
| event_targeter_(std::make_unique<EventTargeter>(this)), |
| mouse_button_down_(false), |
| mouse_cursor_source_window_(nullptr), |
| mouse_cursor_in_non_client_area_(false) {} |
| |
| EventProcessor::~EventProcessor() { |
| SetMouseCursorSourceWindow(nullptr); |
| if (capture_window_) { |
| UnobserveWindow(capture_window_); |
| capture_window_ = nullptr; |
| capture_window_client_id_ = kInvalidClientId; |
| } |
| for (const auto& pair : pointer_targets_) { |
| if (pair.second.window) |
| UnobserveWindow(pair.second.window); |
| } |
| pointer_targets_.clear(); |
| } |
| |
| void EventProcessor::Reset() { |
| if (capture_window_) { |
| CancelPointerEventsToTarget(capture_window_); |
| DCHECK(capture_window_ == nullptr); |
| } |
| |
| while (!pointer_targets_.empty()) |
| StopTrackingPointer(pointer_targets_.begin()->first); |
| |
| mouse_button_down_ = false; |
| } |
| |
| ui::CursorData EventProcessor::GetCurrentMouseCursor() const { |
| if (drag_controller_) |
| return drag_controller_->current_cursor(); |
| |
| if (!mouse_cursor_source_window_) |
| return ui::CursorData(ui::CursorType::kPointer); |
| |
| if (mouse_cursor_in_non_client_area_) |
| return mouse_cursor_source_window_->non_client_cursor(); |
| |
| const ServerWindow* window = GetWindowForMouseCursor(); |
| return window ? window->cursor() : ui::CursorData(ui::CursorType::kPointer); |
| } |
| |
| bool EventProcessor::SetCaptureWindow(ServerWindow* window, |
| ClientSpecificId client_id) { |
| if (!window) |
| client_id = kInvalidClientId; |
| |
| if (window == capture_window_ && client_id == capture_window_client_id_) |
| return true; |
| |
| // A window that is blocked by a modal window cannot gain capture. |
| if (window && modal_window_controller_.IsWindowBlocked(window)) |
| return false; |
| |
| // If we're currently performing a drag and drop, reject setting the capture |
| // window. |
| if (drag_controller_) |
| return false; |
| |
| if (capture_window_) { |
| // Stop observing old capture window. |pointer_targets_| are cleared on |
| // initial setting of a capture window. |
| UnobserveWindow(capture_window_); |
| } else { |
| CancelImplicitCaptureExcept(window, client_id); |
| } |
| |
| // Set the capture before changing native capture; otherwise, the callback |
| // from native platform might try to set the capture again. |
| const bool had_capture_window = capture_window_ != nullptr; |
| ServerWindow* old_capture_window = capture_window_; |
| capture_window_ = window; |
| capture_window_client_id_ = client_id; |
| |
| delegate_->OnCaptureChanged(capture_window_, old_capture_window); |
| |
| // Begin tracking the capture window if it is not yet being observed. |
| if (window) { |
| ObserveWindow(window); |
| // TODO(sky): this conditional is problematic for the case of capture moving |
| // to a different display. |
| if (!had_capture_window) |
| delegate_->SetNativeCapture(window); |
| } else { |
| delegate_->ReleaseNativeCapture(); |
| if (!mouse_button_down_) |
| UpdateCursorProviderByLastKnownLocation(); |
| } |
| return true; |
| } |
| |
| void EventProcessor::SetDragDropSourceWindow( |
| DragSource* drag_source, |
| ServerWindow* window, |
| DragTargetConnection* source_connection, |
| int32_t drag_pointer, |
| const base::flat_map<std::string, std::vector<uint8_t>>& mime_data, |
| uint32_t drag_operations) { |
| CancelImplicitCaptureExcept(nullptr, kInvalidClientId); |
| drag_controller_ = std::make_unique<DragController>( |
| this, drag_source, window, source_connection, drag_pointer, mime_data, |
| drag_operations); |
| } |
| |
| void EventProcessor::CancelDragDrop() { |
| if (drag_controller_) |
| drag_controller_->Cancel(); |
| } |
| |
| void EventProcessor::EndDragDrop() { |
| drag_controller_.reset(); |
| } |
| |
| void EventProcessor::OnWillDestroyDragTargetConnection( |
| DragTargetConnection* connection) { |
| if (drag_controller_) |
| drag_controller_->OnWillDestroyDragTargetConnection(connection); |
| } |
| |
| void EventProcessor::AddSystemModalWindow(ServerWindow* window) { |
| modal_window_controller_.AddSystemModalWindow(window); |
| ReleaseCaptureBlockedByAnyModalWindow(); |
| } |
| |
| void EventProcessor::ReleaseCaptureBlockedByAnyModalWindow() { |
| if (!capture_window_) |
| return; |
| |
| if (modal_window_controller_.IsWindowBlocked(capture_window_)) |
| SetCaptureWindow(nullptr, kInvalidClientId); |
| } |
| |
| const ServerWindow* EventProcessor::GetWindowForMouseCursor() const { |
| if (mouse_cursor_in_non_client_area_ || !mouse_cursor_source_window_) |
| return mouse_cursor_source_window_; |
| |
| // Return the ancestor (starting at |mouse_cursor_source_window_|) whose |
| // client id differs. In other words, return the first window ancestor that is |
| // an embed root. This is done to match the behavior of aura, which sets the |
| // cursor on the root. |
| const ClientSpecificId target_client_id = delegate_->GetEventTargetClientId( |
| mouse_cursor_source_window_, mouse_cursor_in_non_client_area_); |
| const ServerWindow* window = mouse_cursor_source_window_; |
| while (window && window->owning_tree_id() == target_client_id) |
| window = window->parent(); |
| return window; |
| } |
| |
| void EventProcessor::UpdateNonClientAreaForCurrentWindow() { |
| if (!mouse_button_down_ && mouse_cursor_source_window_) { |
| event_targeter_->FindTargetForLocation( |
| EventSource::MOUSE, |
| {mouse_pointer_last_location_, mouse_pointer_last_location_, |
| mouse_pointer_display_id_}, |
| base::BindOnce( |
| &EventProcessor::UpdateNonClientAreaForCurrentWindowOnFoundWindow, |
| base::Unretained(this))); |
| } |
| } |
| |
| void EventProcessor::UpdateCursorProviderByLastKnownLocation() { |
| if (!mouse_button_down_) { |
| event_targeter_->FindTargetForLocation( |
| EventSource::MOUSE, |
| {mouse_pointer_last_location_, mouse_pointer_last_location_, |
| mouse_pointer_display_id_}, |
| base::BindOnce(&EventProcessor:: |
| UpdateCursorProviderByLastKnownLocationOnFoundWindow, |
| base::Unretained(this))); |
| } |
| } |
| |
| bool EventProcessor::AddAccelerator(uint32_t id, |
| mojom::EventMatcherPtr event_matcher) { |
| std::unique_ptr<Accelerator> accelerator(new Accelerator(id, *event_matcher)); |
| // If an accelerator with the same id or matcher already exists, then abort. |
| for (const auto& pair : accelerators_) { |
| if (pair.first == id) { |
| DVLOG(1) << "duplicate accelerator. Accelerator id=" << accelerator->id() |
| << " type=" << event_matcher->type_matcher->type |
| << " flags=" << event_matcher->flags_matcher->flags; |
| return false; |
| } else if (accelerator->EqualEventMatcher(pair.second.get())) { |
| DVLOG(1) << "duplicate matcher. Accelerator id=" << accelerator->id() |
| << " type=" << event_matcher->type_matcher->type |
| << " flags=" << event_matcher->flags_matcher->flags; |
| return false; |
| } |
| } |
| accelerators_.insert(Entry(id, std::move(accelerator))); |
| return true; |
| } |
| |
| void EventProcessor::RemoveAccelerator(uint32_t id) { |
| auto it = accelerators_.find(id); |
| // Clients may pass bogus ids. |
| if (it != accelerators_.end()) |
| accelerators_.erase(it); |
| } |
| |
| void EventProcessor::SetKeyEventsThatDontHideCursor( |
| std::vector<::ui::mojom::EventMatcherPtr> dont_hide_cursor_list) { |
| dont_hide_cursor_matchers_.clear(); |
| for (auto& matcher_ptr : dont_hide_cursor_list) { |
| EventMatcher matcher(*matcher_ptr); |
| // Ensure we don't have pointer matchers in our key only list. |
| DCHECK(!matcher.HasFields(EventMatcher::POINTER_KIND | |
| EventMatcher::POINTER_LOCATION)); |
| dont_hide_cursor_matchers_.push_back(std::move(matcher)); |
| } |
| } |
| |
| bool EventProcessor::IsProcessingEvent() const { |
| return waiting_on_event_targeter_; |
| } |
| |
| void EventProcessor::ProcessEvent(const ui::Event& event, |
| const EventLocation& event_location, |
| AcceleratorMatchPhase match_phase) { |
| #if !defined(NDEBUG) |
| if (match_phase == AcceleratorMatchPhase::POST_ONLY) { |
| // POST_ONLY should always be preceeded by ANY with the same event. |
| DCHECK(previous_event_); |
| // Event doesn't define ==, so this compares the key fields. |
| DCHECK(event.type() == previous_event_->type() && |
| event.time_stamp() == previous_event_->time_stamp() && |
| event.flags() == previous_event_->flags()); |
| DCHECK_EQ(previous_accelerator_match_phase_, AcceleratorMatchPhase::ANY); |
| } |
| previous_event_ = Event::Clone(event); |
| previous_accelerator_match_phase_ = match_phase; |
| #endif |
| |
| if (event.IsKeyEvent()) { |
| const ui::KeyEvent* key_event = event.AsKeyEvent(); |
| if (!key_event->is_char() && match_phase == AcceleratorMatchPhase::ANY) { |
| Accelerator* pre_target = |
| FindAccelerator(*key_event, ui::mojom::AcceleratorPhase::PRE_TARGET); |
| if (pre_target) { |
| event_dispatcher_->OnAccelerator( |
| pre_target->id(), event_location.display_id, event, |
| EventDispatcher::AcceleratorPhase::kPre); |
| return; |
| } |
| } |
| ProcessKeyEvent(*key_event, event_location.display_id, match_phase); |
| return; |
| } |
| |
| DCHECK(event.IsPointerEvent() || event.IsScrollEvent()); |
| DCHECK(event_location.location == event.AsLocatedEvent()->root_location_f()); |
| DCHECK(event_location.location == event.AsLocatedEvent()->location_f()); |
| DCHECK(!waiting_on_event_targeter_); |
| if (ShouldUseEventTargeter(event)) { |
| waiting_on_event_targeter_ = true; |
| const EventSource event_source = |
| event.IsMousePointerEvent() ? EventSource::MOUSE : EventSource::TOUCH; |
| event_targeter_->FindTargetForLocation( |
| event_source, event_location, |
| base::BindOnce(&EventProcessor::ProcessEventOnFoundTarget, |
| base::Unretained(this), ui::Event::Clone(event))); |
| } else { |
| ProcessEventOnFoundTargetImpl(ui::Event::Clone(event), event_location, |
| nullptr); |
| } |
| } |
| |
| ServerWindow* EventProcessor::GetRootWindowForDisplay(int64_t display_id) { |
| return delegate_->GetRootWindowForDisplay(display_id); |
| } |
| |
| viz::HitTestQuery* EventProcessor::GetHitTestQueryForDisplay( |
| int64_t display_id) { |
| return delegate_->GetHitTestQueryForDisplay(display_id); |
| } |
| |
| ServerWindow* EventProcessor::GetWindowFromFrameSinkId( |
| const viz::FrameSinkId& frame_sink_id) { |
| return delegate_->GetWindowFromFrameSinkId(frame_sink_id); |
| } |
| |
| DeepestWindow EventProcessor::AdjustTargetForModal( |
| const DeepestWindow& target) const { |
| const ServerWindow* modal_transient = |
| modal_window_controller_.GetModalTransient(target.window); |
| if (!modal_transient && |
| !modal_window_controller_.IsWindowBlocked(target.window)) { |
| return target; |
| } |
| |
| DeepestWindow updated_target = target; |
| updated_target.in_non_client_area = true; |
| updated_target.window = |
| target.window ? delegate_->GetFallbackTargetForEventBlockedByModal( |
| target.window->GetRootForDrawn()) |
| : nullptr; |
| return updated_target; |
| } |
| |
| void EventProcessor::SetMouseCursorSourceWindow(ServerWindow* window) { |
| if (mouse_cursor_source_window_ == window) |
| return; |
| |
| if (mouse_cursor_source_window_) |
| UnobserveWindow(mouse_cursor_source_window_); |
| mouse_cursor_source_window_ = window; |
| if (mouse_cursor_source_window_) |
| ObserveWindow(mouse_cursor_source_window_); |
| } |
| |
| void EventProcessor::SetMousePointerLocation( |
| const gfx::PointF& new_mouse_location, |
| int64_t new_mouse_display_id) { |
| // TODO: this needs to account for grab and a different display. By that I |
| // mean during a grab |mouse_pointer_last_location_| may actually be in a |
| // different display. |
| mouse_pointer_last_location_ = new_mouse_location; |
| mouse_pointer_display_id_ = new_mouse_display_id; |
| } |
| |
| void EventProcessor::ProcessKeyEvent(const ui::KeyEvent& event, |
| int64_t display_id, |
| AcceleratorMatchPhase match_phase) { |
| Accelerator* post_target = |
| FindAccelerator(event, ui::mojom::AcceleratorPhase::POST_TARGET); |
| if (drag_controller_ && event.type() == ui::ET_KEY_PRESSED && |
| event.key_code() == ui::VKEY_ESCAPE) { |
| drag_controller_->Cancel(); |
| return; |
| } |
| ServerWindow* focused_window = |
| delegate_->GetFocusedWindowForEventProcessor(display_id); |
| if (focused_window) { |
| // We only hide the cursor when there's a window to receive the key |
| // event. We want to hide the cursor when the user is entering text |
| // somewhere so if the user is at the desktop with no window to react to |
| // the key press, there's no reason to hide the cursor. |
| HideCursorOnMatchedKeyEvent(event); |
| |
| // Assume key events are for the client area. |
| const bool in_nonclient_area = false; |
| const ClientSpecificId client_id = |
| delegate_->GetEventTargetClientId(focused_window, in_nonclient_area); |
| event_dispatcher_->DispatchInputEventToWindow(focused_window, client_id, |
| EventLocation(display_id), |
| event, post_target); |
| return; |
| } |
| delegate_->OnEventTargetNotFound(event, display_id); |
| if (post_target) |
| event_dispatcher_->OnAccelerator(post_target->id(), display_id, event, |
| EventDispatcher::AcceleratorPhase::kPost); |
| } |
| |
| void EventProcessor::HideCursorOnMatchedKeyEvent(const ui::KeyEvent& event) { |
| if (event.IsSynthesized()) { |
| // Don't bother performing the matching; it will be rejected anyway. |
| return; |
| } |
| |
| bool hide_cursor = !dont_hide_cursor_matchers_.empty(); |
| for (auto& matcher : dont_hide_cursor_matchers_) { |
| if (matcher.MatchesEvent(event)) { |
| hide_cursor = false; |
| break; |
| } |
| } |
| |
| if (hide_cursor) |
| delegate_->OnEventChangesCursorVisibility(event, false); |
| } |
| |
| bool EventProcessor::ShouldUseEventTargeter(const Event& event) const { |
| if (drag_controller_) |
| return true; |
| |
| if (capture_window_) |
| return false; |
| |
| auto iter = pointer_targets_.find(GetPointerId(event)); |
| if (iter == pointer_targets_.end() || !iter->second.is_pointer_down) |
| return true; |
| |
| return (event.IsMousePointerEvent() && IsPointerGoingUp(event)) || |
| event.type() == ET_POINTER_DOWN; |
| } |
| |
| void EventProcessor::ProcessEventOnFoundTarget( |
| std::unique_ptr<ui::Event> event, |
| const EventLocation& event_location, |
| const DeepestWindow& target) { |
| DCHECK(waiting_on_event_targeter_); |
| waiting_on_event_targeter_ = false; |
| ProcessEventOnFoundTargetImpl(std::move(event), event_location, &target); |
| } |
| |
| void EventProcessor::ProcessEventOnFoundTargetImpl( |
| std::unique_ptr<ui::Event> event, |
| const EventLocation& event_location, |
| const DeepestWindow* found_target) { |
| // WARNING: |found_target| may be null! |
| DCHECK(!waiting_on_event_targeter_); |
| |
| UpdateCursorRelatedProperties(*event, event_location); |
| |
| const bool is_mouse_event = event->IsMousePointerEvent(); |
| const bool is_pointer_going_up = IsPointerGoingUp(*event); |
| |
| // Update mouse down state upon events which change it. |
| if (is_mouse_event) { |
| if (event->type() == ui::ET_POINTER_DOWN) |
| mouse_button_down_ = true; |
| else if (is_pointer_going_up) |
| mouse_button_down_ = false; |
| } |
| |
| if (drag_controller_ && event->IsPointerEvent()) { |
| DCHECK(found_target); |
| if (drag_controller_->DispatchPointerEvent( |
| *event->AsPointerEvent(), |
| AdjustTargetForModal(*found_target).window)) { |
| return; |
| } |
| } |
| |
| if (capture_window_) { |
| SetMouseCursorSourceWindow(capture_window_); |
| DispatchToClient(capture_window_, capture_window_client_id_, |
| *event->AsLocatedEvent(), event_location); |
| return; |
| } |
| |
| std::unique_ptr<DeepestWindowAndTarget> result; |
| if (found_target) { |
| result = std::make_unique<DeepestWindowAndTarget>(); |
| result->deepest_window = AdjustTargetForModal(*found_target); |
| result->pointer_target.is_mouse_event = is_mouse_event; |
| result->pointer_target.window = result->deepest_window.window; |
| result->pointer_target.in_nonclient_area = |
| result->deepest_window.in_non_client_area; |
| result->pointer_target.is_pointer_down = |
| event->type() == ui::ET_POINTER_DOWN; |
| result->pointer_target.display_id = event_location.display_id; |
| } |
| |
| const int32_t pointer_id = GetPointerId(*event); |
| if (!IsTrackingPointer(pointer_id) || |
| !pointer_targets_[pointer_id].is_pointer_down) { |
| DCHECK(result); |
| const bool any_pointers_down = AreAnyPointersDown(); |
| UpdateTargetForPointer(pointer_id, *event->AsLocatedEvent(), |
| result->pointer_target, event_location); |
| if (is_mouse_event) |
| SetMouseCursorSourceWindow(pointer_targets_[pointer_id].window); |
| |
| PointerTarget& pointer_target = pointer_targets_[pointer_id]; |
| if (pointer_target.is_pointer_down) { |
| if (!any_pointers_down) { |
| // Don't attempt to change focus on pointer down. We assume client code |
| // will do that. |
| ServerWindow* capture_window = pointer_target.window; |
| if (!capture_window) { |
| capture_window = |
| delegate_->GetRootWindowForDisplay(event_location.display_id); |
| } |
| delegate_->SetNativeCapture(capture_window); |
| } |
| } |
| } |
| |
| // When we release the mouse button, we want the cursor to be sourced from |
| // the window under the mouse pointer, even though we're sending the button |
| // up event to the window that had implicit capture. We have to set this |
| // before we perform dispatch because the Delegate is going to read this |
| // information from us. |
| if (is_pointer_going_up && is_mouse_event) { |
| DCHECK(result); |
| UpdateCursorProvider(result->deepest_window); |
| } |
| |
| DispatchToPointerTarget(pointer_targets_[pointer_id], |
| *event->AsLocatedEvent(), event_location); |
| |
| if (is_pointer_going_up) { |
| if (is_mouse_event) |
| pointer_targets_[pointer_id].is_pointer_down = false; |
| else |
| StopTrackingPointer(pointer_id); |
| if (!AreAnyPointersDown()) |
| delegate_->ReleaseNativeCapture(); |
| } |
| |
| if (event->type() == ET_POINTER_DOWN) { |
| // Use |found_target| as |result| has already been adjusted for the |
| // modal window. |
| DCHECK(found_target); |
| if (found_target->window) |
| HandleClickOnBlockedWindow(*found_target); |
| } |
| } |
| |
| void EventProcessor::UpdateCursorRelatedProperties( |
| const ui::Event& event, |
| const EventLocation& event_location) { |
| if (event.IsMousePointerEvent()) { |
| // This corresponds to the code in CompoundEventFilter which updates |
| // visibility on each mouse event. Here, we're sure that we're a non-exit |
| // mouse event and FROM_TOUCH doesn't exist in mus so we shouldn't need |
| // further filtering. |
| delegate_->OnEventChangesCursorTouchVisibility(event, true); |
| delegate_->OnEventChangesCursorVisibility(event, true); |
| |
| SetMousePointerLocation(event_location.raw_location, |
| event_location.display_id); |
| delegate_->OnMouseCursorLocationChanged(event_location.raw_location, |
| event_location.display_id); |
| } else if (event.IsPointerEvent()) { |
| // When we have a non-touch event that wasn't synthesized, hide the mouse |
| // cursor until the next non-synthesized mouse event. |
| delegate_->OnEventChangesCursorTouchVisibility(event, false); |
| } |
| } |
| |
| void EventProcessor::UpdateNonClientAreaForCurrentWindowOnFoundWindow( |
| const EventLocation& event_location, |
| const DeepestWindow& target) { |
| if (!mouse_cursor_source_window_) |
| return; |
| |
| const DeepestWindow updated_target = AdjustTargetForModal(target); |
| if (updated_target.window == mouse_cursor_source_window_) { |
| mouse_cursor_in_non_client_area_ = |
| mouse_cursor_source_window_ ? updated_target.in_non_client_area : false; |
| } |
| delegate_->UpdateNativeCursorFromEventProcessor(); |
| } |
| |
| void EventProcessor::UpdateCursorProviderByLastKnownLocationOnFoundWindow( |
| const EventLocation& event_location, |
| const DeepestWindow& target) { |
| UpdateCursorProvider(AdjustTargetForModal(target)); |
| } |
| |
| void EventProcessor::UpdateCursorProvider(const DeepestWindow& target) { |
| if (mouse_button_down_) |
| return; |
| |
| SetMouseCursorSourceWindow(target.window); |
| if (mouse_cursor_source_window_) { |
| mouse_cursor_in_non_client_area_ = target.in_non_client_area; |
| } else { |
| SetMouseCursorSourceWindow( |
| delegate_->GetRootWindowForDisplay(mouse_pointer_display_id_)); |
| mouse_cursor_in_non_client_area_ = true; |
| } |
| delegate_->UpdateNativeCursorFromEventProcessor(); |
| } |
| |
| void EventProcessor::HandleClickOnBlockedWindow(const DeepestWindow& target) { |
| ServerWindow* modal_transient = |
| modal_window_controller_.GetModalTransient(target.window); |
| if (modal_transient) { |
| ServerWindow* toplevel = |
| modal_window_controller_.GetToplevelWindow(target.window); |
| DCHECK(toplevel); |
| delegate_->SetFocusedWindowFromEventProcessor(toplevel); |
| delegate_->OnEventOccurredOutsideOfModalWindow(modal_transient); |
| } else if (target.window->IsDrawn() && |
| modal_window_controller_.IsWindowBlocked(target.window) && |
| modal_window_controller_.GetActiveSystemModalWindow()) { |
| delegate_->OnEventOccurredOutsideOfModalWindow( |
| modal_window_controller_.GetActiveSystemModalWindow()); |
| } |
| } |
| |
| void EventProcessor::StartTrackingPointer(int32_t pointer_id, |
| const PointerTarget& pointer_target) { |
| DCHECK(!IsTrackingPointer(pointer_id)); |
| if (pointer_target.window) |
| ObserveWindow(pointer_target.window); |
| pointer_targets_[pointer_id] = pointer_target; |
| } |
| |
| void EventProcessor::StopTrackingPointer(int32_t pointer_id) { |
| DCHECK(IsTrackingPointer(pointer_id)); |
| ServerWindow* window = pointer_targets_[pointer_id].window; |
| pointer_targets_.erase(pointer_id); |
| if (window) |
| UnobserveWindow(window); |
| } |
| |
| void EventProcessor::UpdateTargetForPointer( |
| int32_t pointer_id, |
| const ui::LocatedEvent& event, |
| const PointerTarget& pointer_target, |
| const EventLocation& event_location) { |
| if (!IsTrackingPointer(pointer_id)) { |
| StartTrackingPointer(pointer_id, pointer_target); |
| return; |
| } |
| |
| if (pointer_target.window == pointer_targets_[pointer_id].window && |
| pointer_target.in_nonclient_area == |
| pointer_targets_[pointer_id].in_nonclient_area) { |
| // The targets are the same, only set the down state to true if necessary. |
| // Down going to up is handled by ProcessLocatedEvent(). |
| if (pointer_target.is_pointer_down) |
| pointer_targets_[pointer_id].is_pointer_down = true; |
| return; |
| } |
| |
| // The targets are changing. Send an exit if appropriate. |
| if (event.IsMousePointerEvent()) { |
| ui::PointerEvent exit_event( |
| ui::ET_POINTER_EXITED, event.location(), event.root_location(), |
| event.flags(), 0 /* changed_button_flags */, |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, |
| ui::MouseEvent::kMousePointerId), |
| event.time_stamp()); |
| DispatchToPointerTarget(pointer_targets_[pointer_id], exit_event, |
| event_location); |
| } |
| |
| // Technically we're updating in place, but calling start then stop makes for |
| // simpler code. |
| StopTrackingPointer(pointer_id); |
| StartTrackingPointer(pointer_id, pointer_target); |
| } |
| |
| bool EventProcessor::AreAnyPointersDown() const { |
| for (const auto& pair : pointer_targets_) { |
| if (pair.second.is_pointer_down) |
| return true; |
| } |
| return false; |
| } |
| |
| void EventProcessor::DispatchToPointerTarget( |
| const PointerTarget& target, |
| const ui::LocatedEvent& event, |
| const EventLocation& event_location) { |
| if (!target.window) { |
| delegate_->OnEventTargetNotFound(event, target.display_id); |
| return; |
| } |
| |
| if (target.is_mouse_event) |
| mouse_cursor_in_non_client_area_ = target.in_nonclient_area; |
| |
| DispatchToClient(target.window, |
| delegate_->GetEventTargetClientId(target.window, |
| target.in_nonclient_area), |
| event, event_location); |
| } |
| |
| void EventProcessor::DispatchToClient(ServerWindow* window, |
| ClientSpecificId client_id, |
| const ui::LocatedEvent& event, |
| const EventLocation& event_location) { |
| gfx::Point location = ConvertPointFromRootForEventDispatch( |
| delegate_->GetRootWindowForEventDispatch(window), window, |
| event.location()); |
| std::unique_ptr<ui::Event> clone = ui::Event::Clone(event); |
| clone->AsLocatedEvent()->set_location(location); |
| // TODO(jonross): add post-target accelerator support once accelerators |
| // support pointer events. |
| event_dispatcher_->DispatchInputEventToWindow( |
| window, client_id, event_location, *clone, nullptr); |
| } |
| |
| void EventProcessor::CancelPointerEventsToTarget(ServerWindow* window) { |
| if (capture_window_ == window) { |
| UnobserveWindow(window); |
| capture_window_ = nullptr; |
| capture_window_client_id_ = kInvalidClientId; |
| mouse_button_down_ = false; |
| // A window only cares to be informed that it lost capture if it explicitly |
| // requested capture. A window can lose capture if another window gains |
| // explicit capture. |
| delegate_->OnCaptureChanged(nullptr, window); |
| delegate_->ReleaseNativeCapture(); |
| UpdateCursorProviderByLastKnownLocation(); |
| return; |
| } |
| |
| for (auto& pair : pointer_targets_) { |
| if (pair.second.window == window) { |
| UnobserveWindow(window); |
| pair.second.window = nullptr; |
| } |
| } |
| } |
| |
| void EventProcessor::ObserveWindow(ServerWindow* window) { |
| auto iter = observed_windows_.find(window); |
| if (iter != observed_windows_.end()) { |
| iter->second->num_observers++; |
| return; |
| } |
| std::unique_ptr<ObservedWindow> observed_window = |
| std::make_unique<ObservedWindow>(); |
| observed_window->num_observers = 1; |
| observed_window->drawn_tracker = |
| std::make_unique<ServerWindowDrawnTracker>(window, this); |
| observed_windows_[window] = std::move(observed_window); |
| } |
| |
| void EventProcessor::UnobserveWindow(ServerWindow* window) { |
| auto it = observed_windows_.find(window); |
| DCHECK(it != observed_windows_.end()); |
| DCHECK_LT(0u, it->second->num_observers); |
| if (--it->second->num_observers == 0u) |
| observed_windows_.erase(it); |
| } |
| |
| Accelerator* EventProcessor::FindAccelerator( |
| const ui::KeyEvent& event, |
| const ui::mojom::AcceleratorPhase phase) { |
| for (const auto& pair : accelerators_) { |
| if (pair.second->MatchesEvent(event, phase)) |
| return pair.second.get(); |
| } |
| return nullptr; |
| } |
| |
| void EventProcessor::CancelImplicitCaptureExcept(ServerWindow* window, |
| ClientSpecificId client_id) { |
| for (const auto& pair : pointer_targets_) { |
| ServerWindow* target = pair.second.window; |
| if (!target) |
| continue; |
| UnobserveWindow(target); |
| if (target == window) |
| continue; |
| |
| // Don't send cancel events to the same client requesting capture, |
| // otherwise the client can easily get confused. |
| if (window && client_id == delegate_->GetEventTargetClientId( |
| target, pair.second.in_nonclient_area)) { |
| continue; |
| } |
| |
| ui::EventType event_type = pair.second.is_mouse_event |
| ? ui::ET_POINTER_EXITED |
| : ui::ET_POINTER_CANCELLED; |
| ui::EventPointerType pointer_type = |
| pair.second.is_mouse_event ? ui::EventPointerType::POINTER_TYPE_MOUSE |
| : ui::EventPointerType::POINTER_TYPE_TOUCH; |
| // TODO(jonross): Track previous location in PointerTarget for sending |
| // cancels. |
| ui::PointerEvent event(event_type, gfx::Point(), gfx::Point(), ui::EF_NONE, |
| 0 /* changed_button_flags */, |
| ui::PointerDetails(pointer_type, pair.first), |
| ui::EventTimeForNow()); |
| DispatchToPointerTarget(pair.second, event, |
| EventLocation(pair.second.display_id)); |
| } |
| pointer_targets_.clear(); |
| } |
| |
| void EventProcessor::WindowNoLongerValidTarget(ServerWindow* window) { |
| CancelPointerEventsToTarget(window); |
| if (mouse_cursor_source_window_ == window) |
| SetMouseCursorSourceWindow(nullptr); |
| } |
| |
| void EventProcessor::OnDrawnStateChanged(ServerWindow* ancestor, |
| ServerWindow* window, |
| bool is_drawn) { |
| if (!is_drawn) |
| WindowNoLongerValidTarget(window); |
| } |
| |
| void EventProcessor::OnRootDidChange(ServerWindow* ancestor, |
| ServerWindow* window) { |
| if (!delegate_->GetRootWindowForEventDispatch(window)) |
| WindowNoLongerValidTarget(window); |
| } |
| |
| void EventProcessor::OnDragCursorUpdated() { |
| delegate_->UpdateNativeCursorFromEventProcessor(); |
| } |
| |
| } // namespace ws |
| } // namespace ui |