Commit ade517cc authored by Maksim Sisov's avatar Maksim Sisov Committed by Commit Bot

[ozone/x11] Resolve event clicking locations in X11

This patch concerns so called external window mode,
which is being developed downstream now [1].

The problem this CL fixes is the following:
Even though events are passed to the right X11WindowOzone
based on an XID target now, there can be cases when the
events must be rerouted to another X11WindowOzone, for
example, a context menu or a nested 3-dot menu. In case of a
nested 3-dot menu, events must always be passed to the parent
menu, so that the menu controller would handle events properly.
Another case is handling events when a user clicks outside the
menu's area: even though the events can be targeted for another
window, there can be an explicit grab that must reroute them
to the menu window, which will be closed as soon as a user
clicks outside the menu's area.

The patch fixes the above mentioned problem the following way:

1) Once an explicit grab is set, the events are passed to an
X11WindowOzone have their locations converted and are rerouted
to a grabbed window.

2) Whenever a grabbed window looses an explicit capture,
X11WindowManager tells X11WindowOzone the capture is lost
and the |delegate_| is notified about that.

[1] https://github.com/Igalia/chromium

Bug: 707406
Change-Id: I1b00e79c8ddc17b000c2f55b7f39c9ea16c0c71a
Reviewed-on: https://chromium-review.googlesource.com/790773
Commit-Queue: Maksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542927}
parent f70d2f25
......@@ -352,13 +352,14 @@ static_library("test_support") {
libs = [ "Carbon.framework" ]
}
if (use_x11) {
if (use_x11 || ozone_platform_x11) {
sources += [
"test/events_test_utils_x11.cc",
"test/events_test_utils_x11.h",
]
deps += [
"//ui/events/devices/x11",
"//ui/events/keycodes:x11",
"//ui/gfx/x",
]
}
......
......@@ -59,12 +59,19 @@ source_set("x11_unittests") {
testonly = true
sources = [
"x11_cursor_factory_ozone_unittest.cc",
"x11_window_ozone_unittest.cc",
]
deps = [
":x11",
"//testing/gmock",
"//testing/gtest",
"//ui/events:test_support",
"//ui/events/devices/x11",
"//ui/events/platform/x11",
"//ui/ozone:platform",
"//ui/ozone:test_support",
"//ui/ozone/common",
"//ui/platform_window/x11:x11",
]
}
......@@ -13,14 +13,20 @@ X11WindowManagerOzone::X11WindowManagerOzone() : event_grabber_(nullptr) {}
X11WindowManagerOzone::~X11WindowManagerOzone() {}
void X11WindowManagerOzone::GrabEvents(X11WindowOzone* window) {
if (event_grabber_ != window)
if (event_grabber_ == window)
return;
X11WindowOzone* old_grabber = event_grabber_;
if (old_grabber)
old_grabber->OnLostCapture();
event_grabber_ = window;
}
void X11WindowManagerOzone::UngrabEvents(X11WindowOzone* window) {
if (event_grabber_ != window)
return;
event_grabber_->OnLostCapture();
event_grabber_ = nullptr;
}
......
......@@ -16,11 +16,12 @@ class X11WindowManagerOzone {
X11WindowManagerOzone();
~X11WindowManagerOzone();
// Tries to set a given X11WindowOzone as the recipient for events. It will
// fail if there is already another X11WindowOzone as recipient.
// Sets a given X11WindowOzone as the recipient for events and calls
// OnLostCapture for another |event_grabber_| if it has been set previously.
void GrabEvents(X11WindowOzone* window);
// Unsets a given X11WindowOzone as the recipient for events.
// Unsets a given X11WindowOzone as the recipient for events and calls
// OnLostCapture.
void UngrabEvents(X11WindowOzone* window);
// Gets the current X11WindowOzone recipient of mouse events.
......
......@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/ozone/events_ozone.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/geometry/point.h"
......@@ -73,12 +74,29 @@ bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& platform_event) {
}
uint32_t X11WindowOzone::DispatchEvent(const PlatformEvent& platform_event) {
// This is unfortunately needed otherwise events that depend on global state
// (eg. double click) are broken.
DispatchEventFromNativeUiEvent(
platform_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
base::Unretained(delegate())));
return POST_DISPATCH_STOP_PROPAGATION;
auto* event = static_cast<Event*>(platform_event);
if (!window_manager_->event_grabber() ||
window_manager_->event_grabber() == this) {
// This is unfortunately needed otherwise events that depend on global state
// (eg. double click) are broken.
DispatchEventFromNativeUiEvent(
event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
base::Unretained(delegate())));
return POST_DISPATCH_STOP_PROPAGATION;
}
if (event->IsLocatedEvent()) {
// Another X11WindowOzone has installed itself as capture. Translate the
// event's location and dispatch to the other.
ConvertEventLocationToTargetWindowLocation(
window_manager_->event_grabber()->GetBounds().origin(),
GetBounds().origin(), event->AsLocatedEvent());
}
return window_manager_->event_grabber()->DispatchEvent(event);
}
void X11WindowOzone::OnLostCapture() {
delegate()->OnLostCapture();
}
} // namespace ui
......@@ -24,6 +24,9 @@ class X11WindowOzone : public X11WindowBase,
const gfx::Rect& bounds);
~X11WindowOzone() override;
// Called by |window_manager_| once capture is set to another X11WindowOzone.
void OnLostCapture();
// PlatformWindow:
void PrepareForShutdown() override;
void SetCapture() override;
......
// Copyright 2017 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/ozone/platform/x11/x11_window_ozone.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event.h"
#include "ui/events/platform/x11/x11_event_source_libevent.h"
#include "ui/events/test/events_test_utils_x11.h"
#include "ui/ozone/platform/x11/x11_window_manager_ozone.h"
#include "ui/ozone/test/mock_platform_window_delegate.h"
#include "ui/platform_window/platform_window_delegate.h"
namespace ui {
namespace {
using ::testing::Eq;
using ::testing::_;
constexpr int kPointerDeviceId = 1;
ACTION_P(StoreWidget, widget_ptr) {
*widget_ptr = arg0;
}
ACTION_P(CloneEvent, event_ptr) {
*event_ptr = Event::Clone(*arg0);
}
} // namespace
class X11WindowOzoneTest : public testing::Test {
public:
X11WindowOzoneTest()
: task_env_(std::make_unique<base::test::ScopedTaskEnvironment>(
base::test::ScopedTaskEnvironment::MainThreadType::UI)) {}
~X11WindowOzoneTest() override = default;
void SetUp() override {
XDisplay* display = gfx::GetXDisplay();
event_source_ = std::make_unique<X11EventSourceLibevent>(display);
window_manager_ = std::make_unique<X11WindowManagerOzone>();
TouchFactory::GetInstance()->SetPointerDeviceForTest({kPointerDeviceId});
}
protected:
std::unique_ptr<PlatformWindow> CreatePlatformWindow(
MockPlatformWindowDelegate* delegate,
const gfx::Rect& bounds,
gfx::AcceleratedWidget* widget) {
EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_, _))
.WillOnce(StoreWidget(widget));
auto window = std::make_unique<X11WindowOzone>(window_manager_.get(),
delegate, bounds);
return std::move(window);
}
void DispatchXEvent(XEvent* event, gfx::AcceleratedWidget widget) {
DCHECK_EQ(GenericEvent, event->type);
XIDeviceEvent* device_event =
static_cast<XIDeviceEvent*>(event->xcookie.data);
device_event->event = widget;
event_source_->ProcessXEvent(event);
}
private:
std::unique_ptr<base::test::ScopedTaskEnvironment> task_env_;
std::unique_ptr<X11WindowManagerOzone> window_manager_;
std::unique_ptr<X11EventSourceLibevent> event_source_;
DISALLOW_COPY_AND_ASSIGN(X11WindowOzoneTest);
};
// This test ensures that events are handled by a right target(widget).
TEST_F(X11WindowOzoneTest, SendPlatformEventToRightTarget) {
MockPlatformWindowDelegate delegate;
gfx::AcceleratedWidget widget;
constexpr gfx::Rect bounds(30, 80, 800, 600);
auto window = CreatePlatformWindow(&delegate, bounds, &widget);
ScopedXI2Event xi_event;
xi_event.InitGenericButtonEvent(kPointerDeviceId, ET_MOUSE_PRESSED,
gfx::Point(218, 290), EF_NONE);
// First check events can be received by a target window.
std::unique_ptr<Event> event;
EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event));
DispatchXEvent(xi_event, widget);
EXPECT_EQ(ET_MOUSE_PRESSED, event->type());
testing::Mock::VerifyAndClearExpectations(&delegate);
MockPlatformWindowDelegate delegate_2;
gfx::AcceleratedWidget widget_2;
gfx::Rect bounds_2(525, 155, 296, 407);
auto window_2 = CreatePlatformWindow(&delegate_2, bounds_2, &widget_2);
// Check event goes to right target without capture being set.
event.reset();
EXPECT_CALL(delegate, DispatchEvent(_)).Times(0);
EXPECT_CALL(delegate_2, DispatchEvent(_)).WillOnce(CloneEvent(&event));
DispatchXEvent(xi_event, widget_2);
EXPECT_EQ(ET_MOUSE_PRESSED, event->type());
EXPECT_CALL(delegate, OnClosed()).Times(1);
EXPECT_CALL(delegate_2, OnClosed()).Times(1);
}
// This test case ensures that events are consumed by a window with explicit
// capture, even though the event is sent to other window.
TEST_F(X11WindowOzoneTest, SendPlatformEventToCapturedWindow) {
MockPlatformWindowDelegate delegate;
gfx::AcceleratedWidget widget;
constexpr gfx::Rect bounds(30, 80, 800, 600);
EXPECT_CALL(delegate, OnClosed()).Times(1);
auto window = CreatePlatformWindow(&delegate, bounds, &widget);
MockPlatformWindowDelegate delegate_2;
gfx::AcceleratedWidget widget_2;
gfx::Rect bounds_2(525, 155, 296, 407);
EXPECT_CALL(delegate_2, OnClosed()).Times(1);
auto window_2 = CreatePlatformWindow(&delegate_2, bounds_2, &widget_2);
ScopedXI2Event xi_event;
xi_event.InitGenericButtonEvent(kPointerDeviceId, ET_MOUSE_PRESSED,
gfx::Point(218, 290), EF_NONE);
// Set capture to the second window, but send an event to another window
// target. The event must have its location converted and received by the
// captured window instead.
window_2->SetCapture();
std::unique_ptr<Event> event;
EXPECT_CALL(delegate, DispatchEvent(_)).Times(0);
EXPECT_CALL(delegate_2, DispatchEvent(_)).WillOnce(CloneEvent(&event));
DispatchXEvent(xi_event, widget);
EXPECT_EQ(ET_MOUSE_PRESSED, event->type());
EXPECT_EQ(gfx::Point(-277, 215), event->AsLocatedEvent()->location());
}
} // namespace ui
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