desktop_display_info_loader_x11.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/desktop_display_info_loader.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/numerics/safe_conversions.h"
  9. #include "remoting/base/logging.h"
  10. #include "ui/base/x/x11_display_util.h"
  11. #include "ui/base/x/x11_util.h"
  12. #include "ui/gfx/x/connection.h"
  13. #include "ui/gfx/x/event.h"
  14. #include "ui/gfx/x/future.h"
  15. #include "ui/gfx/x/randr.h"
  16. #include "ui/gfx/x/x11_window_event_manager.h"
  17. namespace remoting {
  18. namespace {
  19. // Monitors were added in XRANDR 1.5.
  20. constexpr int kMinRandrVersion = 105;
  21. constexpr int kDefaultScreenDpi = 96;
  22. constexpr double kMillimetersPerInch = 25.4;
  23. class DesktopDisplayInfoLoaderX11 : public DesktopDisplayInfoLoader,
  24. public x11::EventObserver {
  25. public:
  26. DesktopDisplayInfoLoaderX11() = default;
  27. ~DesktopDisplayInfoLoaderX11() override;
  28. // DesktopDisplayInfoLoader implementation.
  29. void Init() override;
  30. DesktopDisplayInfo GetCurrentDisplayInfo() override;
  31. // x11::EventObserver implementation.
  32. void OnEvent(const x11::Event& xevent) override;
  33. private:
  34. // Queries the X server and updates |monitors_|.
  35. void LoadMonitors();
  36. // XRANDR version as MAJOR * 100 + MINOR, or 0 if XRANDR is not present.
  37. int xrandr_version_ = 0;
  38. raw_ptr<x11::Connection> connection_ = nullptr;
  39. raw_ptr<x11::RandR> randr_ = nullptr;
  40. // Selector for root window events.
  41. std::unique_ptr<x11::XScopedEventSelector> root_window_events_;
  42. std::vector<x11::RandR::MonitorInfo> monitors_;
  43. };
  44. DesktopDisplayInfoLoaderX11::~DesktopDisplayInfoLoaderX11() {
  45. if (connection_) {
  46. connection_->RemoveEventObserver(this);
  47. }
  48. }
  49. void DesktopDisplayInfoLoaderX11::Init() {
  50. connection_ = x11::Connection::Get();
  51. connection_->AddEventObserver(this);
  52. randr_ = &connection_->randr();
  53. if (!randr_->present()) {
  54. HOST_LOG << "No XRANDR extension found.";
  55. return;
  56. }
  57. xrandr_version_ = ui::GetXrandrVersion();
  58. if (xrandr_version_ < kMinRandrVersion) {
  59. HOST_LOG << "XRANDR version (" << xrandr_version_ << ") is too old.";
  60. return;
  61. }
  62. root_window_events_ = std::make_unique<x11::XScopedEventSelector>(
  63. ui::GetX11RootWindow(), x11::EventMask::StructureNotify);
  64. auto randr_event_mask =
  65. x11::RandR::NotifyMask::ScreenChange | x11::RandR::NotifyMask::CrtcChange;
  66. randr_->SelectInput({ui::GetX11RootWindow(), randr_event_mask});
  67. LoadMonitors();
  68. }
  69. DesktopDisplayInfo DesktopDisplayInfoLoaderX11::GetCurrentDisplayInfo() {
  70. DesktopDisplayInfo result;
  71. for (const auto& monitor : monitors_) {
  72. DisplayGeometry info;
  73. // webrtc::ScreenCapturerX11 uses the |name| Atom as the monitor ID.
  74. info.id = static_cast<int32_t>(monitor.name);
  75. info.is_default = monitor.primary;
  76. info.x = monitor.x;
  77. info.y = monitor.y;
  78. info.width = monitor.width;
  79. info.height = monitor.height;
  80. info.dpi = kDefaultScreenDpi;
  81. info.bpp = 24;
  82. // Calculate DPI if possible, using width in millimeters.
  83. if (monitor.width_in_millimeters != 0) {
  84. double pixelsPerMillimeter =
  85. static_cast<double>(monitor.width) / monitor.width_in_millimeters;
  86. double pixelsPerInch = pixelsPerMillimeter * kMillimetersPerInch;
  87. info.dpi = base::ClampRound(pixelsPerInch);
  88. }
  89. result.AddDisplay(info);
  90. }
  91. return result;
  92. }
  93. void DesktopDisplayInfoLoaderX11::OnEvent(const x11::Event& xevent) {
  94. const auto* configure_notify = xevent.As<x11::ConfigureNotifyEvent>();
  95. const auto* screen_change = xevent.As<x11::RandR::ScreenChangeNotifyEvent>();
  96. const auto* notify = xevent.As<x11::RandR::NotifyEvent>();
  97. if (configure_notify) {
  98. HOST_LOG << "Got X11 ConfigureNotify event.";
  99. } else if (screen_change) {
  100. HOST_LOG << "Got RANDR ScreenChange event.";
  101. } else if (notify) {
  102. HOST_LOG << "Got RANDR Notify event.";
  103. } else {
  104. // Unhandled event received on root window, ignore.
  105. return;
  106. }
  107. LoadMonitors();
  108. }
  109. void DesktopDisplayInfoLoaderX11::LoadMonitors() {
  110. if (xrandr_version_ < kMinRandrVersion)
  111. return;
  112. auto reply = randr_->GetMonitors({ui::GetX11RootWindow()}).Sync();
  113. if (reply) {
  114. monitors_ = std::move(reply->monitors);
  115. } else {
  116. LOG(ERROR) << "RRGetMonitors request failed.";
  117. }
  118. }
  119. } // namespace
  120. // static
  121. std::unique_ptr<DesktopDisplayInfoLoader> DesktopDisplayInfoLoader::Create() {
  122. return std::make_unique<DesktopDisplayInfoLoaderX11>();
  123. }
  124. } // namespace remoting