// 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 #include #include "base/memory/raw_ptr.h" #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; raw_ptr connection_ = nullptr; raw_ptr randr_ = nullptr; // Selector for root window events. std::unique_ptr root_window_events_; std::vector 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( 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(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(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(); const auto* screen_change = xevent.As(); const auto* notify = xevent.As(); 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::Create() { return std::make_unique(); } } // namespace remoting