Commit 4306670f authored by Maksim Sisov's avatar Maksim Sisov Committed by Commit Bot

ozone/wayland: use capture requests to reroute events.

We used to be using complicated logic how to reroute and process
events based on the assumption that it must be the toplevel menu
window receiving located events, etc.

However, there is an easier way to determine what window should
receive the events, etc.

1) Remove unnecessary bits in CanDispatchEvent and return true iff the
window has focus.
2) Add Grab/UnGrabEvents methods to WaylandWindowManager - it is going
to tell WaylandWindows what window should finally get the event.
3) Fix the DispatchEvent so that:
 - It reroutes all keyboard events to root parent wayland window.
 - It reroutes events to the event grabber (and fixes the location
   of the events as it used to be doing) iff it belongs to the same
   "family" aka the event grabber and original event dispatcher has the
   same root parent window.
4) Fix the tests verifying that:
 - WaylandWindows process the events based on the state of the focus.
 - The dispatcher honors the event grabber and reroutes the events
   the same way as it used to be before (PlatformWindowDelegate doesn't
   see any changes in the logic and continues to operate as it used to
   operate before).
 - The event grabber doesn't receive events if they were initially sent
   to another window that doesn't belong to its family.
5) Do not set implicit capture on mouse press events. Also remove the
   bits related to that.

Bug: 1079221
Change-Id: Iaf2fd175f6ede6779d58863832245dca87178d60
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2187313
Commit-Queue: Maksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarNick Yamane <nickdiego@igalia.com>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Cr-Commit-Position: refs/heads/master@{#768728}
parent bb12959a
......@@ -77,6 +77,9 @@ class WaylandConnection {
// Returns the current pointer, which may be null.
WaylandPointer* pointer() const { return pointer_.get(); }
// Returns the current keyboard, which may be null.
WaylandKeyboard* keyboard() const { return keyboard_.get(); }
WaylandClipboard* clipboard() const { return clipboard_.get(); }
WaylandDataSource* drag_data_source() const {
......
......@@ -176,22 +176,7 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type,
int flags = pointer_flags_ | keyboard_modifiers_ | changed_button;
MouseEvent event(type, pointer_location_, pointer_location_,
EventTimeForNow(), flags, changed_button);
// Update implicit grab state for the currently focused window, if any, before
// dispatching the event, so event dispatchers can determine if they should or
// not process this event.
if (type == ET_MOUSE_PRESSED)
UpdateImplicitGrab();
DispatchEvent(&event);
// Reset implicit grab only after the event has been sent. Otherwise, we may
// end up in a situation where a target checks for a pointer grab on the
// MouseRelease event type, and fails to release capture due to early pointer
// focus reset. Setting implicit grab is done normally before the event has
// been sent.
if (type == ET_MOUSE_RELEASED)
UpdateImplicitGrab();
}
void WaylandEventSource::OnPointerMotionEvent(const gfx::PointF& location) {
......@@ -312,16 +297,6 @@ void WaylandEventSource::OnWindowRemoved(WaylandWindow* window) {
});
}
void WaylandEventSource::UpdateImplicitGrab() {
if (!pointer_)
return;
if (auto* focused_window = window_manager_->GetCurrentFocusedWindow()) {
focused_window->set_has_implicit_grab(
HasAnyPointerButtonFlag(pointer_flags_));
}
}
// Currently EF_MOD3_DOWN means that the CapsLock key is currently down, and
// EF_CAPS_LOCK_ON means the caps lock state is enabled (and the key may or
// may not be down, but usually isn't). There does need to be two different
......@@ -361,8 +336,6 @@ void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window,
// menus), in this case, |window| is null, otherwise it must be equal to
// |window_with_pointer_focus_|. In both cases, they must be equal.
DCHECK_EQ(window_with_pointer_focus_, window);
if (window)
window->set_has_implicit_grab(false);
window_with_pointer_focus_ = nullptr;
}
}
......
......@@ -115,7 +115,6 @@ class WaylandEventSource : public PlatformEventSource,
// WaylandWindowObserver
void OnWindowRemoved(WaylandWindow* window) override;
void UpdateImplicitGrab();
void UpdateKeyboardModifiers(int modifier, bool down);
void HandleKeyboardFocusChange(WaylandWindow* window, bool focused);
void HandlePointerFocusChange(WaylandWindow* window, bool focused);
......
......@@ -57,7 +57,7 @@ class WaylandEventSourceTest : public WaylandTest {
};
// Verify WaylandEventSource properly manages its internal state as pointer
// button events are sent. More specifically, pointer flags and implicit grab.
// button events are sent. More specifically - pointer flags.
TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
MockPlatformWindowDelegate delegate;
wl_seat_send_capabilities(server_.seat()->resource(),
......@@ -74,7 +74,6 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
kDefaultBounds, &delegate);
Sync();
EXPECT_FALSE(window1->has_implicit_grab());
ASSERT_TRUE(server_.seat()->pointer());
uint32_t serial = 0;
......@@ -90,7 +89,6 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
Sync();
EXPECT_TRUE(event_source_->IsPointerButtonPressed(EF_LEFT_MOUSE_BUTTON));
EXPECT_TRUE(window1->has_implicit_grab());
wl_pointer_send_button(pointer_res, serial++, tstamp++, BTN_RIGHT,
WL_POINTER_BUTTON_STATE_PRESSED);
......@@ -98,7 +96,6 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
Sync();
EXPECT_TRUE(event_source_->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON));
EXPECT_TRUE(window1->has_implicit_grab());
wl_pointer_send_button(pointer_res, serial++, tstamp++, BTN_LEFT,
WL_POINTER_BUTTON_STATE_RELEASED);
......@@ -109,7 +106,6 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
EXPECT_FALSE(event_source_->IsPointerButtonPressed(EF_LEFT_MOUSE_BUTTON));
EXPECT_FALSE(event_source_->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON));
EXPECT_FALSE(window1->has_implicit_grab());
}
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
......
......@@ -155,114 +155,6 @@ TEST_P(WaylandPointerTest, MotionDragged) {
EXPECT_EQ(gfx::PointF(400, 500), mouse_event->root_location_f());
}
TEST_P(WaylandPointerTest, ButtonPressAndCheckCapture) {
wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(),
wl_fixed_from_int(200), wl_fixed_from_int(150));
Sync();
wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_RIGHT,
WL_POINTER_BUTTON_STATE_PRESSED);
std::unique_ptr<Event> right_press_event;
// By the time ET_MOUSE_PRESSED event comes, WaylandWindow must have capture
// set.
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillOnce(
CloneEventAndCheckCapture(window_.get(), true, &right_press_event));
Sync();
ASSERT_TRUE(right_press_event);
ASSERT_TRUE(right_press_event->IsMouseEvent());
auto* right_press_mouse_event = right_press_event->AsMouseEvent();
EXPECT_EQ(ET_MOUSE_PRESSED, right_press_mouse_event->type());
EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, right_press_mouse_event->button_flags());
EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON,
right_press_mouse_event->changed_button_flags());
std::unique_ptr<Event> left_press_event;
// Ensure capture is still set before DispatchEvent returns.
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillOnce(
CloneEventAndCheckCapture(window_.get(), true, &left_press_event));
wl_pointer_send_button(pointer_->resource(), 3, 1003, BTN_LEFT,
WL_POINTER_BUTTON_STATE_PRESSED);
Sync();
// Ensure capture is still set after DispatchEvent returns.
ASSERT_TRUE(window_->HasCapture());
ASSERT_TRUE(left_press_event);
ASSERT_TRUE(left_press_event->IsMouseEvent());
auto* left_press_mouse_event = left_press_event->AsMouseEvent();
EXPECT_EQ(ET_MOUSE_PRESSED, left_press_mouse_event->type());
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON,
left_press_mouse_event->button_flags());
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON,
left_press_mouse_event->changed_button_flags());
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON,
left_press_mouse_event->changed_button_flags());
EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->location_f());
EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->root_location_f());
}
TEST_P(WaylandPointerTest, ButtonReleaseAndCheckCapture) {
wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(),
wl_fixed_from_int(50), wl_fixed_from_int(50));
wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_BACK,
WL_POINTER_BUTTON_STATE_PRESSED);
wl_pointer_send_button(pointer_->resource(), 3, 1003, BTN_LEFT,
WL_POINTER_BUTTON_STATE_PRESSED);
Sync();
std::unique_ptr<Event> event;
// Ensure capture is set before DispatchEvent returns.
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillOnce(CloneEventAndCheckCapture(window_.get(), true, &event));
wl_pointer_send_button(pointer_->resource(), 4, 1004, BTN_LEFT,
WL_POINTER_BUTTON_STATE_RELEASED);
Sync();
ASSERT_TRUE(event);
ASSERT_TRUE(event->IsMouseEvent());
auto* mouse_event = event->AsMouseEvent();
EXPECT_EQ(ET_MOUSE_RELEASED, mouse_event->type());
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON,
mouse_event->button_flags());
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_event->changed_button_flags());
EXPECT_EQ(gfx::PointF(50, 50), mouse_event->location_f());
EXPECT_EQ(gfx::PointF(50, 50), mouse_event->root_location_f());
// Ensure capture is still set after DispatchEvent returns.
ASSERT_TRUE(window_->HasCapture());
mouse_event = nullptr;
event.reset();
// Ensure capture has not been reset before DispatchEvent returns, otherwise
// the code on top of Ozone (aura and etc), might get a wrong result, when
// calling HasCapture. If it is false, it can lead to mouse pressed handlers
// to be never released.
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillOnce(CloneEventAndCheckCapture(window_.get(), true, &event));
wl_pointer_send_button(pointer_->resource(), 5, 1005, BTN_BACK,
WL_POINTER_BUTTON_STATE_RELEASED);
Sync();
ASSERT_TRUE(event);
ASSERT_TRUE(event->IsMouseEvent());
mouse_event = event->AsMouseEvent();
EXPECT_EQ(ET_MOUSE_RELEASED, mouse_event->type());
EXPECT_EQ(EF_BACK_MOUSE_BUTTON, mouse_event->button_flags());
EXPECT_EQ(EF_BACK_MOUSE_BUTTON, mouse_event->changed_button_flags());
EXPECT_EQ(gfx::PointF(50, 50), mouse_event->location_f());
EXPECT_EQ(gfx::PointF(50, 50), mouse_event->root_location_f());
// It is safe to release capture now.
ASSERT_TRUE(!window_->HasCapture());
}
TEST_P(WaylandPointerTest, AxisVertical) {
wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(),
wl_fixed_from_int(0), wl_fixed_from_int(0));
......
......@@ -41,8 +41,6 @@ void WaylandPopup::Show(bool inactive) {
if (shell_popup_)
return;
set_keyboard_focus(true);
if (!CreateShellPopup()) {
Close();
return;
......@@ -73,11 +71,6 @@ bool WaylandPopup::IsVisible() const {
return !!shell_popup_;
}
bool WaylandPopup::HasCapture() const {
// WaylandPopups always have captures.
return shell_popup();
}
void WaylandPopup::HandlePopupConfigure(const gfx::Rect& bounds_dip) {
DCHECK(shell_popup());
DCHECK(parent_window());
......
......@@ -23,7 +23,6 @@ class WaylandPopup : public WaylandWindow {
void Show(bool inactive) override;
void Hide() override;
bool IsVisible() const override;
bool HasCapture() const override;
private:
// WaylandWindow overrides:
......
......@@ -94,7 +94,6 @@ void WaylandSurface::Show(bool inactive) {
return;
}
set_keyboard_focus(true);
UpdateBufferScale(false);
}
......
......@@ -45,6 +45,10 @@ WaylandWindow* WaylandWindow::FromSurface(wl_surface* surface) {
wl_proxy_get_user_data(reinterpret_cast<wl_proxy*>(surface)));
}
void WaylandWindow::OnWindowLostCapture() {
delegate_->OnLostCapture();
}
void WaylandWindow::UpdateBufferScale(bool update_bounds) {
DCHECK(connection_->wayland_output_manager());
const auto* screen = connection_->wayland_output_manager()->wayland_screen();
......@@ -128,17 +132,25 @@ gfx::Rect WaylandWindow::GetBounds() {
void WaylandWindow::SetTitle(const base::string16& title) {}
void WaylandWindow::SetCapture() {
// Wayland does implicit grabs, and doesn't allow for explicit grabs. The
// exception to that are menus, but we explicitly send events to a
// parent menu if such exists.
// Wayland doesn't allow explicit grabs. Instead, it sends events to "entered"
// windows. That is, if user enters their mouse pointer to a window, that
// window starts to receive events. However, Chromium may want to reroute
// these events to another window. In this case, tell the window manager that
// this specific window has grabbed the events, and they will be rerouted in
// WaylandWindow::DispatchEvent method.
if (!HasCapture())
connection_->wayland_window_manager()->GrabLocatedEvents(this);
}
void WaylandWindow::ReleaseCapture() {
if (HasCapture())
connection_->wayland_window_manager()->UngrabLocatedEvents(this);
// See comment in SetCapture() for details on wayland and grabs.
}
bool WaylandWindow::HasCapture() const {
return has_implicit_grab_;
return connection_->wayland_window_manager()->located_events_grabber() ==
this;
}
void WaylandWindow::ToggleFullscreen() {}
......@@ -222,20 +234,6 @@ void WaylandWindow::SetWindowIcons(const gfx::ImageSkia& window_icon,
void WaylandWindow::SizeConstraintsChanged() {}
bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) {
// This window is a nested menu window, all the events must be forwarded
// to the main menu window.
if (child_window_ && wl::IsMenuType(child_window_->type()))
return wl::IsMenuType(type());
// If this is a nested menu window with a parent, it mustn't recieve any
// events.
if (parent_window_ && wl::IsMenuType(parent_window_->type()))
return false;
// If another window has capture, return early before checking focus.
if (HasCapture())
return true;
if (event->IsMouseEvent())
return has_pointer_focus_;
if (event->IsKeyEvent())
......@@ -247,38 +245,35 @@ bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) {
uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) {
Event* event = static_cast<Event*>(native_event);
if (event->IsLocatedEvent()) {
// Wayland sends locations in DIP so they need to be translated to
// physical pixels.
event->AsLocatedEvent()->set_location_f(gfx::ScalePoint(
event->AsLocatedEvent()->location_f(), buffer_scale_, buffer_scale_));
// If the window does not have a pointer focus, but received this event, it
// means the window is a menu window with a child menu window. In this case,
// the location of the event must be converted from the nested menu to the
// main menu, which the menu controller needs to properly handle events.
if (wl::IsMenuType(type())) {
// Parent window of the main menu window is not a menu, but rather an
// xdg surface.
DCHECK(!wl::IsMenuType(parent_window_->type()) ||
parent_window_->type() != PlatformWindowType::kTooltip);
auto* window =
connection_->wayland_window_manager()->GetCurrentFocusedWindow();
if (window) {
ConvertEventLocationToTargetWindowLocation(GetBounds().origin(),
window->GetBounds().origin(),
event->AsLocatedEvent());
}
auto* event_grabber =
connection_->wayland_window_manager()->located_events_grabber();
auto* root_parent_window = GetRootParentWindow();
// We must reroute the events to the event grabber iff these windows belong
// to the same root parent window. For example, there are 2 top level
// Wayland windows. One of them (window_1) has a child menu window that is
// the event grabber. If the mouse is moved over the window_1, it must
// reroute the events to the event grabber. If the mouse is moved over the
// window_2, the events mustn't be rerouted, because that belongs to another
// stack of windows. Remember that Wayland sends local surface coordinates,
// and continuing rerouting all the events may result in events sent to the
// grabber even though the mouse is over another root window.
//
if (event_grabber &&
root_parent_window == event_grabber->GetRootParentWindow()) {
ConvertEventLocationToTargetWindowLocation(
event_grabber->GetBounds().origin(), GetBounds().origin(),
event->AsLocatedEvent());
return event_grabber->DispatchEventToDelegate(native_event);
}
auto copied_event = Event::Clone(*event);
UpdateCursorPositionFromEvent(std::move(copied_event));
}
DispatchEventFromNativeUiEvent(
native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
base::Unretained(delegate_)));
return POST_DISPATCH_STOP_PROPAGATION;
// Dispatch all keyboard events to the root window.
if (event->IsKeyEvent())
return GetRootParentWindow()->DispatchEventToDelegate(event);
return DispatchEventToDelegate(native_event);
}
void WaylandWindow::HandleSurfaceConfigure(int32_t widht,
......@@ -489,6 +484,18 @@ bool WaylandWindow::IsOpaqueWindow() const {
return opacity_ == ui::PlatformWindowOpacity::kOpaqueWindow;
}
uint32_t WaylandWindow::DispatchEventToDelegate(
const PlatformEvent& native_event) {
auto* event = static_cast<Event*>(native_event);
if (event->IsLocatedEvent())
UpdateCursorPositionFromEvent(Event::Clone(*event));
DispatchEventFromNativeUiEvent(
native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
base::Unretained(delegate_)));
return POST_DISPATCH_STOP_PROPAGATION;
}
// static
void WaylandWindow::Enter(void* data,
struct wl_surface* wl_surface,
......
......@@ -44,6 +44,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
static WaylandWindow* FromSurface(wl_surface* surface);
void OnWindowLostCapture();
// Updates the surface buffer scale of the window. Top level windows take
// scale from the output attached to either their current display or the
// primary one if their widget is not yet created, children inherit scale from
......@@ -78,11 +80,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
void set_child_window(WaylandWindow* window) { child_window_ = window; }
WaylandWindow* child_window() const { return child_window_; }
// Set whether this window has an implicit grab (often referred to as capture
// in Chrome code). Implicit grabs happen while a pointer is down.
void set_has_implicit_grab(bool value) { has_implicit_grab_ = value; }
bool has_implicit_grab() const { return has_implicit_grab_; }
int32_t buffer_scale() const { return buffer_scale_; }
int32_t ui_scale() const { return ui_scale_; }
......@@ -197,6 +194,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
bool IsOpaqueWindow() const;
uint32_t DispatchEventToDelegate(const PlatformEvent& native_event);
// Additional initialization of derived classes.
virtual bool OnInitialize(PlatformWindowInitProperties properties) = 0;
......@@ -226,7 +225,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
bool has_pointer_focus_ = false;
bool has_keyboard_focus_ = false;
bool has_touch_focus_ = false;
bool has_implicit_grab_ = false;
// Wayland's scale factor for the output that this window currently belongs
// to.
int32_t buffer_scale_ = 1;
......
......@@ -20,6 +20,25 @@ void WaylandWindowManager::RemoveObserver(WaylandWindowObserver* observer) {
observers_.RemoveObserver(observer);
}
void WaylandWindowManager::GrabLocatedEvents(WaylandWindow* window) {
DCHECK_NE(located_events_grabber_, window);
// Wayland doesn't allow to grab the mouse. However, we start forwarding all
// mouse events received by WaylandWindow to the aura::WindowEventDispatcher
// which has capture.
auto* old_grabber = located_events_grabber_;
located_events_grabber_ = window;
if (old_grabber)
old_grabber->OnWindowLostCapture();
}
void WaylandWindowManager::UngrabLocatedEvents(WaylandWindow* window) {
DCHECK_EQ(located_events_grabber_, window);
auto* old_grabber = located_events_grabber_;
located_events_grabber_ = nullptr;
old_grabber->OnWindowLostCapture();
}
WaylandWindow* WaylandWindowManager::GetWindow(
gfx::AcceleratedWidget widget) const {
auto it = window_map_.find(widget);
......
......@@ -27,6 +27,17 @@ class WaylandWindowManager {
void AddObserver(WaylandWindowObserver* observer);
void RemoveObserver(WaylandWindowObserver* observer);
// Stores the window that should grab the located events.
void GrabLocatedEvents(WaylandWindow* event_grabber);
// Removes the window that should grab the located events.
void UngrabLocatedEvents(WaylandWindow* event_grabber);
// Returns current event grabber.
WaylandWindow* located_events_grabber() const {
return located_events_grabber_;
}
// Returns a window found by |widget|.
WaylandWindow* GetWindow(gfx::AcceleratedWidget widget) const;
......@@ -54,6 +65,8 @@ class WaylandWindowManager {
base::flat_map<gfx::AcceleratedWidget, WaylandWindow*> window_map_;
WaylandWindow* located_events_grabber_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(WaylandWindowManager);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment