blob: 26c4fbe30107db049d8c50a857a52ed688f62b7d [file] [log] [blame]
// Copyright 2021 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 "remoting/host/desktop_display_info_loader.h"
#include <algorithm>
#include <memory>
#include "base/numerics/safe_conversions.h"
#include "remoting/base/logging.h"
#include "ui/base/x/x11_display_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/event.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11_window_event_manager.h"
namespace remoting {
namespace {
// Monitors were added in XRANDR 1.5.
constexpr int kMinRandrVersion = 105;
constexpr int kDefaultScreenDpi = 96;
constexpr double kMillimetersPerInch = 25.4;
class DesktopDisplayInfoLoaderX11 : public DesktopDisplayInfoLoader,
public x11::EventObserver {
public:
DesktopDisplayInfoLoaderX11() = default;
~DesktopDisplayInfoLoaderX11() override;
// DesktopDisplayInfoLoader implementation.
void Init() override;
DesktopDisplayInfo GetCurrentDisplayInfo() override;
// x11::EventObserver implementation.
void OnEvent(const x11::Event& xevent) override;
private:
// Queries the X server and updates |monitors_|.
void LoadMonitors();
// XRANDR version as MAJOR * 100 + MINOR, or 0 if XRANDR is not present.
int xrandr_version_ = 0;
x11::Connection* connection_ = nullptr;
x11::RandR* randr_ = nullptr;
// Selector for root window events.
std::unique_ptr<x11::XScopedEventSelector> root_window_events_;
std::vector<x11::RandR::MonitorInfo> monitors_;
};
DesktopDisplayInfoLoaderX11::~DesktopDisplayInfoLoaderX11() {
if (connection_) {
connection_->RemoveEventObserver(this);
}
}
void DesktopDisplayInfoLoaderX11::Init() {
connection_ = x11::Connection::Get();
connection_->AddEventObserver(this);
randr_ = &connection_->randr();
if (!randr_->present()) {
HOST_LOG << "No XRANDR extension found.";
return;
}
xrandr_version_ = ui::GetXrandrVersion();
if (xrandr_version_ < kMinRandrVersion) {
HOST_LOG << "XRANDR version (" << xrandr_version_ << ") is too old.";
return;
}
root_window_events_ = std::make_unique<x11::XScopedEventSelector>(
ui::GetX11RootWindow(), x11::EventMask::StructureNotify);
auto randr_event_mask =
x11::RandR::NotifyMask::ScreenChange | x11::RandR::NotifyMask::CrtcChange;
randr_->SelectInput({ui::GetX11RootWindow(), randr_event_mask});
LoadMonitors();
}
DesktopDisplayInfo DesktopDisplayInfoLoaderX11::GetCurrentDisplayInfo() {
DesktopDisplayInfo result;
for (const auto& monitor : monitors_) {
DisplayGeometry info;
// webrtc::ScreenCapturerX11 uses the |name| Atom as the monitor ID.
info.id = static_cast<int32_t>(monitor.name);
info.is_default = monitor.primary;
info.x = monitor.x;
info.y = monitor.y;
info.width = monitor.width;
info.height = monitor.height;
info.dpi = kDefaultScreenDpi;
info.bpp = 24;
// Calculate DPI if possible, using width in millimeters.
if (monitor.width_in_millimeters != 0) {
double pixelsPerMillimeter =
static_cast<double>(monitor.width) / monitor.width_in_millimeters;
double pixelsPerInch = pixelsPerMillimeter * kMillimetersPerInch;
info.dpi = base::ClampRound(pixelsPerInch);
}
result.AddDisplay(info);
}
return result;
}
void DesktopDisplayInfoLoaderX11::OnEvent(const x11::Event& xevent) {
const auto* configure_notify = xevent.As<x11::ConfigureNotifyEvent>();
const auto* screen_change = xevent.As<x11::RandR::ScreenChangeNotifyEvent>();
const auto* notify = xevent.As<x11::RandR::NotifyEvent>();
if (configure_notify) {
HOST_LOG << "Got X11 ConfigureNotify event.";
} else if (screen_change) {
HOST_LOG << "Got RANDR ScreenChange event.";
} else if (notify) {
HOST_LOG << "Got RANDR Notify event.";
} else {
// Unhandled event received on root window, ignore.
return;
}
LoadMonitors();
}
void DesktopDisplayInfoLoaderX11::LoadMonitors() {
if (xrandr_version_ < kMinRandrVersion)
return;
auto reply = randr_->GetMonitors({ui::GetX11RootWindow()}).Sync();
if (reply) {
monitors_ = std::move(reply->monitors);
} else {
LOG(ERROR) << "RRGetMonitors request failed.";
}
}
} // namespace
// static
std::unique_ptr<DesktopDisplayInfoLoader> DesktopDisplayInfoLoader::Create() {
return std::make_unique<DesktopDisplayInfoLoaderX11>();
}
} // namespace remoting