Commit efd38a8e authored by Nick Diego Yamane's avatar Nick Diego Yamane Committed by Commit Bot

ozone/x11: Handle XRandR events in X11ScreenOzone

This CL adds event handling for XRandR |XEvent|s, re-fetching and updating
display list dynamically and communicating |DisplayObserver|s about the
changes.

No unit tests are added due to the current dependency on XRandR/Xlib API.

Bug: 891175
Test: None
Change-Id: I96c5fe83fc4f1792503b34b0676b778b601d42d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1709491
Commit-Queue: Nick Yamane <nickdiego@igalia.com>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680591}
parent 24725be2
...@@ -96,7 +96,9 @@ class OzonePlatformX11 : public OzonePlatform { ...@@ -96,7 +96,9 @@ class OzonePlatformX11 : public OzonePlatform {
std::unique_ptr<PlatformScreen> CreateScreen() override { std::unique_ptr<PlatformScreen> CreateScreen() override {
DCHECK(window_manager_); DCHECK(window_manager_);
return std::make_unique<X11ScreenOzone>(window_manager_.get()); auto screen = std::make_unique<X11ScreenOzone>(window_manager_.get());
screen->Init();
return screen;
} }
PlatformClipboard* GetPlatformClipboard() override { PlatformClipboard* GetPlatformClipboard() override {
......
...@@ -4,16 +4,18 @@ ...@@ -4,16 +4,18 @@
#include "ui/ozone/platform/x11/x11_screen_ozone.h" #include "ui/ozone/platform/x11/x11_screen_ozone.h"
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/x/x11_display_util.h" #include "ui/base/x/x11_display_util.h"
#include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util.h"
#include "ui/display/display_finder.h" #include "ui/display/display_finder.h"
#include "ui/display/util/display_util.h" #include "ui/display/util/display_util.h"
#include "ui/display/util/x11/edid_parser_x11.h" #include "ui/display/util/x11/edid_parser_x11.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/font_render_params.h" #include "ui/gfx/font_render_params.h"
#include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/ozone/platform/x11/x11_window_manager_ozone.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h"
#include "ui/ozone/platform/x11/x11_window_ozone.h" #include "ui/ozone/platform/x11/x11_window_ozone.h"
...@@ -23,6 +25,8 @@ namespace { ...@@ -23,6 +25,8 @@ namespace {
constexpr int kMinVersionXrandr = 103; // Need at least xrandr version 1.3. constexpr int kMinVersionXrandr = 103; // Need at least xrandr version 1.3.
constexpr auto kDisplayListUpdateDelay = base::TimeDelta::FromMilliseconds(250);
float GetDeviceScaleFactor() { float GetDeviceScaleFactor() {
float device_scale_factor = 1.0f; float device_scale_factor = 1.0f;
// TODO(crbug.com/891175): Implement PlatformScreen for X11 // TODO(crbug.com/891175): Implement PlatformScreen for X11
...@@ -105,32 +109,44 @@ bool LocalProcessWindowFinder::MatchWindow(X11WindowOzone* window) const { ...@@ -105,32 +109,44 @@ bool LocalProcessWindowFinder::MatchWindow(X11WindowOzone* window) const {
} // namespace } // namespace
X11ScreenOzone::X11ScreenOzone(X11WindowManagerOzone* wm, bool fetch) X11ScreenOzone::X11ScreenOzone(X11WindowManagerOzone* window_manager)
: window_manager_(wm), : window_manager_(window_manager),
xdisplay_(gfx::GetXDisplay()), xdisplay_(gfx::GetXDisplay()),
x_root_window_(DefaultRootWindow(xdisplay_)), x_root_window_(DefaultRootWindow(xdisplay_)),
xrandr_version_(GetXrandrVersion(xdisplay_)) { xrandr_version_(GetXrandrVersion(xdisplay_)) {
DCHECK(window_manager_); DCHECK(window_manager_);
// TODO(nickdiego): Factor this out from ctor
if (fetch)
FetchDisplayList();
} }
X11ScreenOzone::~X11ScreenOzone() { X11ScreenOzone::~X11ScreenOzone() {
if (xrandr_version_ >= kMinVersionXrandr && if (xrandr_version_ >= kMinVersionXrandr &&
PlatformEventSource::GetInstance()) { X11EventSourceLibevent::GetInstance()) {
PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); X11EventSourceLibevent::GetInstance()->RemoveXEventDispatcher(this);
}
}
void X11ScreenOzone::Init() {
// Need at least xrandr version 1.3.
if (xrandr_version_ >= kMinVersionXrandr) {
int error_base_ignored = 0;
XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
DCHECK(X11EventSourceLibevent::GetInstance());
X11EventSourceLibevent::GetInstance()->AddXEventDispatcher(this);
XRRSelectInput(xdisplay_, x_root_window_,
RRScreenChangeNotifyMask | RROutputChangeNotifyMask |
RRCrtcChangeNotifyMask);
} }
FetchDisplayList();
} }
const std::vector<display::Display>& X11ScreenOzone::GetAllDisplays() const { const std::vector<display::Display>& X11ScreenOzone::GetAllDisplays() const {
return display_list_.displays(); return displays_;
} }
display::Display X11ScreenOzone::GetPrimaryDisplay() const { display::Display X11ScreenOzone::GetPrimaryDisplay() const {
auto iter = display_list_.GetPrimaryDisplayIterator(); auto iter = displays_.begin();
if (iter == display_list_.displays().end()) if (iter == displays_.end())
return display::Display::GetDefaultDisplay(); return display::Display::GetDefaultDisplay();
return *iter; return *iter;
} }
...@@ -174,74 +190,72 @@ display::Display X11ScreenOzone::GetDisplayMatching( ...@@ -174,74 +190,72 @@ display::Display X11ScreenOzone::GetDisplayMatching(
const gfx::Rect& match_rect) const { const gfx::Rect& match_rect) const {
const display::Display* matching_display = const display::Display* matching_display =
display::FindDisplayWithBiggestIntersection( display::FindDisplayWithBiggestIntersection(
display_list_.displays(), displays_, gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect));
gfx::ConvertRectToDIP(GetDeviceScaleFactor(), match_rect));
return matching_display ? *matching_display : GetPrimaryDisplay(); return matching_display ? *matching_display : GetPrimaryDisplay();
} }
void X11ScreenOzone::AddObserver(display::DisplayObserver* observer) { void X11ScreenOzone::AddObserver(display::DisplayObserver* observer) {
display_list_.AddObserver(observer); change_notifier_.AddObserver(observer);
} }
void X11ScreenOzone::RemoveObserver(display::DisplayObserver* observer) { void X11ScreenOzone::RemoveObserver(display::DisplayObserver* observer) {
display_list_.RemoveObserver(observer); change_notifier_.RemoveObserver(observer);
} }
bool X11ScreenOzone::CanDispatchEvent(const ui::PlatformEvent& event) { // TODO(nickdiego): Factor event dispatching and display fetching so that it
// TODO(crbug.com/891175): Implement PlatformScreen for X11 // can be shared between ozone and non-ozone code paths.
NOTIMPLEMENTED_LOG_ONCE(); bool X11ScreenOzone::DispatchXEvent(XEvent* xev) {
DCHECK(xev);
int ev_type = xev->type - xrandr_event_base_;
if (ev_type == RRScreenChangeNotify) {
// Pass the event through to xlib.
XRRUpdateConfiguration(xev);
return true;
}
if (ev_type == RRNotify ||
(xev->type == PropertyNotify &&
xev->xproperty.atom == gfx::GetAtom("_NET_WORKAREA"))) {
RestartDelayedUpdateDisplayListTask();
return true;
}
return false; return false;
} }
uint32_t X11ScreenOzone::DispatchEvent(const ui::PlatformEvent& event) {
// TODO(crbug.com/891175): Implement PlatformScreen for X11
NOTIMPLEMENTED_LOG_ONCE();
return ui::POST_DISPATCH_NONE;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// X11ScreenOzone, private: // X11ScreenOzone, private:
void X11ScreenOzone::AddDisplay(const display::Display& display, void X11ScreenOzone::SetDisplayList(std::vector<display::Display> displays) {
bool is_primary) { displays_ = std::move(displays);
display_list_.AddDisplay( gfx::SetFontRenderParamsDeviceScaleFactor(
display, is_primary ? display::DisplayList::Type::PRIMARY GetPrimaryDisplay().device_scale_factor());
: display::DisplayList::Type::NOT_PRIMARY);
if (is_primary) {
gfx::SetFontRenderParamsDeviceScaleFactor(
GetPrimaryDisplay().device_scale_factor());
}
}
void X11ScreenOzone::RemoveDisplay(const display::Display& display) {
display_list_.RemoveDisplay(display.id());
} }
// Talks to xrandr to get the information of the outputs for a screen and // Talks to xrandr to get the information of the outputs for a screen and
// updates display::Display list. The minimum required version of xrandr is // updates display::Display list. The minimum required version of xrandr is
// 1.3. // 1.3.
void X11ScreenOzone::FetchDisplayList() { void X11ScreenOzone::FetchDisplayList() {
float scale = GetDeviceScaleFactor();
std::vector<display::Display> displays; std::vector<display::Display> displays;
// Need at least xrandr version 1.3. float scale = GetDeviceScaleFactor();
if (xrandr_version_ >= kMinVersionXrandr) { if (xrandr_version_ >= kMinVersionXrandr) {
int error_base_ignored = 0;
XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
if (PlatformEventSource::GetInstance())
PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
XRRSelectInput(xdisplay_, x_root_window_,
RRScreenChangeNotifyMask | RROutputChangeNotifyMask |
RRCrtcChangeNotifyMask);
displays = BuildDisplaysFromXRandRInfo(xrandr_version_, scale, displays = BuildDisplaysFromXRandRInfo(xrandr_version_, scale,
&primary_display_index_); &primary_display_index_);
} else { } else {
displays = GetFallbackDisplayList(scale); displays = GetFallbackDisplayList(scale);
} }
for (auto& display : displays) SetDisplayList(std::move(displays));
AddDisplay(display, display.id() == primary_display_index_); }
void X11ScreenOzone::UpdateDisplayList() {
std::vector<display::Display> old_displays = displays_;
FetchDisplayList();
change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
}
void X11ScreenOzone::RestartDelayedUpdateDisplayListTask() {
delayed_update_task_.Reset(base::BindOnce(&X11ScreenOzone::UpdateDisplayList,
base::Unretained(this)));
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, delayed_update_task_.callback(), kDisplayListUpdateDelay);
} }
gfx::Point X11ScreenOzone::GetCursorLocation() const { gfx::Point X11ScreenOzone::GetCursorLocation() const {
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "base/cancelable_callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "ui/display/display_list.h" #include "ui/display/display.h"
#include "ui/events/platform/platform_event_dispatcher.h" #include "ui/display/display_change_notifier.h"
#include "ui/events/platform/x11/x11_event_source_libevent.h"
#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/x11_types.h" #include "ui/gfx/x/x11_types.h"
#include "ui/ozone/public/platform_screen.h" #include "ui/ozone/public/platform_screen.h"
...@@ -21,12 +23,15 @@ namespace ui { ...@@ -21,12 +23,15 @@ namespace ui {
class X11WindowManagerOzone; class X11WindowManagerOzone;
// A PlatformScreen implementation for X11. // A PlatformScreen implementation for X11.
class X11ScreenOzone : public PlatformScreen, public PlatformEventDispatcher { class X11ScreenOzone : public PlatformScreen, public XEventDispatcher {
public: public:
explicit X11ScreenOzone(X11WindowManagerOzone* wm, bool fetch = true); explicit X11ScreenOzone(X11WindowManagerOzone* window_manager);
~X11ScreenOzone() override; ~X11ScreenOzone() override;
// PlatformScreen: // Fetch display list through Xlib/XRandR
void Init();
// Overridden from ui::PlatformScreen:
const std::vector<display::Display>& GetAllDisplays() const override; const std::vector<display::Display>& GetAllDisplays() const override;
display::Display GetPrimaryDisplay() const override; display::Display GetPrimaryDisplay() const override;
display::Display GetDisplayForAcceleratedWidget( display::Display GetDisplayForAcceleratedWidget(
...@@ -41,22 +46,22 @@ class X11ScreenOzone : public PlatformScreen, public PlatformEventDispatcher { ...@@ -41,22 +46,22 @@ class X11ScreenOzone : public PlatformScreen, public PlatformEventDispatcher {
void AddObserver(display::DisplayObserver* observer) override; void AddObserver(display::DisplayObserver* observer) override;
void RemoveObserver(display::DisplayObserver* observer) override; void RemoveObserver(display::DisplayObserver* observer) override;
// PlatformEventDispatcher: // Overridden from ui::XEventDispatcher:
bool CanDispatchEvent(const ui::PlatformEvent& event) override; bool DispatchXEvent(XEvent* event) override;
uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
private: private:
friend class X11ScreenOzoneTest; friend class X11ScreenOzoneTest;
void AddDisplay(const display::Display& display, bool is_primary); void SetDisplayList(std::vector<display::Display> displays);
void RemoveDisplay(const display::Display& display);
void FetchDisplayList(); void FetchDisplayList();
void UpdateDisplayList();
void RestartDelayedUpdateDisplayListTask();
gfx::Point GetCursorLocation() const; gfx::Point GetCursorLocation() const;
X11WindowManagerOzone* const window_manager_; std::vector<display::Display> displays_;
display::DisplayList display_list_; display::DisplayChangeNotifier change_notifier_;
base::ObserverList<display::DisplayObserver> observers_; X11WindowManagerOzone* const window_manager_;
XDisplay* const xdisplay_; XDisplay* const xdisplay_;
XID x_root_window_; XID x_root_window_;
...@@ -69,6 +74,10 @@ class X11ScreenOzone : public PlatformScreen, public PlatformEventDispatcher { ...@@ -69,6 +74,10 @@ class X11ScreenOzone : public PlatformScreen, public PlatformEventDispatcher {
// decoding events regarding output add/remove. // decoding events regarding output add/remove.
int xrandr_event_base_ = 0; int xrandr_event_base_ = 0;
// The task to delay fetching display info. We delay it so that we can
// coalesce events.
base::CancelableOnceClosure delayed_update_task_;
DISALLOW_COPY_AND_ASSIGN(X11ScreenOzone); DISALLOW_COPY_AND_ASSIGN(X11ScreenOzone);
}; };
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#include <memory> #include <memory>
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/display_observer.h"
#include "ui/events/platform/x11/x11_event_source_libevent.h" #include "ui/events/platform/x11/x11_event_source_libevent.h"
#include "ui/ozone/platform/x11/x11_window_manager_ozone.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h"
#include "ui/ozone/platform/x11/x11_window_ozone.h" #include "ui/ozone/platform/x11/x11_window_ozone.h"
...@@ -34,6 +36,14 @@ int64_t NextDisplayId() { ...@@ -34,6 +36,14 @@ int64_t NextDisplayId() {
return next_id++; return next_id++;
} }
struct MockDisplayObserver : public display::DisplayObserver {
MockDisplayObserver() = default;
~MockDisplayObserver() override = default;
MOCK_METHOD1(OnDisplayAdded, void(const display::Display& new_display));
MOCK_METHOD1(OnDisplayRemoved, void(const display::Display& old_display));
};
} // namespace } // namespace
class X11ScreenOzoneTest : public testing::Test { class X11ScreenOzoneTest : public testing::Test {
...@@ -49,8 +59,9 @@ class X11ScreenOzoneTest : public testing::Test { ...@@ -49,8 +59,9 @@ class X11ScreenOzoneTest : public testing::Test {
window_manager_ = std::make_unique<X11WindowManagerOzone>(); window_manager_ = std::make_unique<X11WindowManagerOzone>();
primary_display_ = std::make_unique<display::Display>( primary_display_ = std::make_unique<display::Display>(
NextDisplayId(), kPrimaryDisplayBounds); NextDisplayId(), kPrimaryDisplayBounds);
screen_.reset(new X11ScreenOzone(window_manager_.get(), false)); screen_.reset(new X11ScreenOzone(window_manager_.get()));
screen_->AddDisplay(*primary_display_, true); screen_->SetDisplayList({*primary_display_});
screen_->AddObserver(&display_observer_);
} }
protected: protected:
...@@ -62,11 +73,23 @@ class X11ScreenOzoneTest : public testing::Test { ...@@ -62,11 +73,23 @@ class X11ScreenOzoneTest : public testing::Test {
} }
void AddDisplayForTest(const display::Display& display) { void AddDisplayForTest(const display::Display& display) {
screen_->AddDisplay(display, false); std::vector<display::Display> new_displays(screen_->displays_);
new_displays.push_back(display);
UpdateDisplayListForTest(std::move(new_displays));
} }
void RemoveDisplayForTest(const display::Display& display) { void RemoveDisplayForTest(const display::Display& display_to_remove) {
screen_->RemoveDisplay(display); std::vector<display::Display> new_displays(screen_->displays_.size() - 1);
std::remove_copy(screen_->displays_.begin(), screen_->displays_.end(),
new_displays.begin(), display_to_remove);
UpdateDisplayListForTest(std::move(new_displays));
}
void UpdateDisplayListForTest(std::vector<display::Display> displays) {
std::vector<display::Display> old_displays = std::move(screen_->displays_);
screen_->SetDisplayList(std::move(displays));
screen_->change_notifier_.NotifyDisplaysChanged(old_displays,
screen_->displays_);
} }
std::unique_ptr<X11WindowOzone> CreatePlatformWindow( std::unique_ptr<X11WindowOzone> CreatePlatformWindow(
...@@ -80,6 +103,8 @@ class X11ScreenOzoneTest : public testing::Test { ...@@ -80,6 +103,8 @@ class X11ScreenOzoneTest : public testing::Test {
window_manager_.get()); window_manager_.get());
} }
MockDisplayObserver display_observer_;
private: private:
std::unique_ptr<X11WindowManagerOzone> window_manager_; std::unique_ptr<X11WindowManagerOzone> window_manager_;
std::unique_ptr<display::Display> primary_display_; std::unique_ptr<display::Display> primary_display_;
...@@ -95,6 +120,8 @@ class X11ScreenOzoneTest : public testing::Test { ...@@ -95,6 +120,8 @@ class X11ScreenOzoneTest : public testing::Test {
TEST_F(X11ScreenOzoneTest, AddRemoveListDisplays) { TEST_F(X11ScreenOzoneTest, AddRemoveListDisplays) {
// Initially only primary display is expected to be in place // Initially only primary display is expected to be in place
EXPECT_EQ(1u, screen()->GetAllDisplays().size()); EXPECT_EQ(1u, screen()->GetAllDisplays().size());
EXPECT_CALL(display_observer_, OnDisplayAdded(_)).Times(2);
EXPECT_CALL(display_observer_, OnDisplayRemoved(_)).Times(3);
auto display_2 = CreateDisplay(gfx::Rect(800, 0, 1280, 720)); auto display_2 = CreateDisplay(gfx::Rect(800, 0, 1280, 720));
AddDisplayForTest(*display_2); AddDisplayForTest(*display_2);
......
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