blob: 65af54df3dcd3246e93487621705e613ec8ed135 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/x/x11_display_manager.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "components/device_event_log/device_event_log.h"
#include "ui/base/x/x11_display_util.h"
#include "ui/display/types/display_config.h"
#include "ui/gfx/x/atom_cache.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/xproto.h"
#if BUILDFLAG(IS_LINUX)
#include "ui/linux/linux_ui.h"
#endif
namespace ui {
namespace {
// Need at least xrandr version 1.3
constexpr std::pair<uint32_t, uint32_t> kMinXrandrVersion{1, 3};
} // namespace
XDisplayManager::XDisplayManager(Delegate* delegate)
: delegate_(delegate),
displays_{display::Display()},
connection_(x11::Connection::Get()),
x_root_window_(connection_->default_screen().root),
workspace_handler_(this) {}
XDisplayManager::~XDisplayManager() = default;
void XDisplayManager::Init() {
if (IsXrandrAvailable()) {
auto& randr = connection_->randr();
randr.SelectInput(
{x_root_window_, x11::RandR::NotifyMask::ScreenChange |
x11::RandR::NotifyMask::OutputChange |
x11::RandR::NotifyMask::CrtcChange});
}
FetchDisplayList();
}
// Need at least xrandr version 1.3
bool XDisplayManager::IsXrandrAvailable() const {
return connection_->randr_version() >= kMinXrandrVersion;
}
const display::Display& XDisplayManager::GetPrimaryDisplay() const {
DCHECK(!displays_.empty());
return displays_[primary_display_index_];
}
void XDisplayManager::AddObserver(display::DisplayObserver* observer) {
change_notifier_.AddObserver(observer);
}
void XDisplayManager::RemoveObserver(display::DisplayObserver* observer) {
change_notifier_.RemoveObserver(observer);
}
void XDisplayManager::OnEvent(const x11::Event& xev) {
auto* prop = xev.As<x11::PropertyNotifyEvent>();
if (xev.As<x11::RandR::NotifyEvent>() ||
(prop && prop->atom == x11::GetAtom("_NET_WORKAREA"))) {
DispatchDelayedDisplayListUpdate();
}
}
void XDisplayManager::SetDisplayList(std::vector<display::Display> displays,
size_t primary_display_index) {
displays_ = std::move(displays);
primary_display_index_ = primary_display_index;
delegate_->OnXDisplayListUpdated();
}
// 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
// 1.3.
void XDisplayManager::FetchDisplayList() {
std::vector<display::Display> displays;
display::DisplayConfig empty_display_config{
display::Display::HasForceDeviceScaleFactor()
? display::Display::GetForcedDeviceScaleFactor()
: 1.0f};
const auto* display_config = &empty_display_config;
#if BUILDFLAG(IS_LINUX)
if (const auto* linux_ui = ui::LinuxUi::instance()) {
display_config = &linux_ui->display_config();
}
#endif
size_t primary_display_index = 0;
if (IsXrandrAvailable()) {
displays =
BuildDisplaysFromXRandRInfo(*display_config, &primary_display_index);
} else {
displays = GetFallbackDisplayList(display_config->primary_scale,
&primary_display_index);
}
if (displays != displays_) {
DISPLAY_LOG(EVENT) << "Displays updated, count: " << displays.size();
for (const auto& display : displays) {
DISPLAY_LOG(EVENT) << display.ToString();
}
}
SetDisplayList(std::move(displays), primary_display_index);
}
void XDisplayManager::OnCurrentWorkspaceChanged(
const std::string& new_workspace) {
change_notifier_.NotifyCurrentWorkspaceChanged(new_workspace);
}
void XDisplayManager::UpdateDisplayList() {
std::vector<display::Display> old_displays = displays_;
FetchDisplayList();
change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
}
void XDisplayManager::DispatchDelayedDisplayListUpdate() {
update_task_.Reset(base::BindOnce(&XDisplayManager::UpdateDisplayList,
base::Unretained(this)));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, update_task_.callback());
}
std::string XDisplayManager::GetCurrentWorkspace() {
return workspace_handler_.GetCurrentWorkspace();
}
} // namespace ui