desktop_resizer_mac.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // Copyright (c) 2012 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_resizer.h"
  5. #include <Carbon/Carbon.h>
  6. #include <stdint.h>
  7. #include "base/mac/foundation_util.h"
  8. #include "base/mac/mac_util.h"
  9. #include "base/mac/scoped_cftyperef.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "remoting/base/logging.h"
  12. namespace {
  13. // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
  14. const int kDefaultDPI = 96;
  15. } // namespace
  16. namespace remoting {
  17. class DesktopResizerMac : public DesktopResizer {
  18. public:
  19. DesktopResizerMac() = default;
  20. DesktopResizerMac(const DesktopResizerMac&) = delete;
  21. DesktopResizerMac& operator=(const DesktopResizerMac&) = delete;
  22. // DesktopResizer interface
  23. ScreenResolution GetCurrentResolution(webrtc::ScreenId screen_id) override;
  24. std::list<ScreenResolution> GetSupportedResolutions(
  25. const ScreenResolution& preferred,
  26. webrtc::ScreenId screen_id) override;
  27. void SetResolution(const ScreenResolution& resolution,
  28. webrtc::ScreenId screen_id) override;
  29. void RestoreResolution(const ScreenResolution& original,
  30. webrtc::ScreenId screen_id) override;
  31. private:
  32. // If there is a single display, get its id and return true, otherwise return
  33. // false. We don't currently support resize-to-client on multi-monitor Macs.
  34. bool GetSoleDisplayId(CGDirectDisplayID* display);
  35. void GetSupportedModesAndResolutions(
  36. base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
  37. std::list<ScreenResolution>* resolutions);
  38. };
  39. ScreenResolution DesktopResizerMac::GetCurrentResolution(
  40. webrtc::ScreenId screen_id) {
  41. CGDirectDisplayID display;
  42. if (GetSoleDisplayId(&display)) {
  43. CGRect rect = CGDisplayBounds(display);
  44. return ScreenResolution(
  45. webrtc::DesktopSize(rect.size.width, rect.size.height),
  46. webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
  47. }
  48. return ScreenResolution();
  49. }
  50. std::list<ScreenResolution> DesktopResizerMac::GetSupportedResolutions(
  51. const ScreenResolution& preferred,
  52. webrtc::ScreenId screen_id) {
  53. base::ScopedCFTypeRef<CFMutableArrayRef> modes;
  54. std::list<ScreenResolution> resolutions;
  55. GetSupportedModesAndResolutions(&modes, &resolutions);
  56. return resolutions;
  57. }
  58. void DesktopResizerMac::SetResolution(const ScreenResolution& resolution,
  59. webrtc::ScreenId screen_id) {
  60. CGDirectDisplayID display;
  61. if (!GetSoleDisplayId(&display)) {
  62. return;
  63. }
  64. base::ScopedCFTypeRef<CFMutableArrayRef> modes;
  65. std::list<ScreenResolution> resolutions;
  66. GetSupportedModesAndResolutions(&modes, &resolutions);
  67. // There may be many modes with the requested resolution. Pick the one with
  68. // the highest color depth.
  69. int index = 0, best_depth = 0;
  70. CGDisplayModeRef best_mode = nullptr;
  71. for (std::list<ScreenResolution>::const_iterator i = resolutions.begin();
  72. i != resolutions.end(); ++i, ++index) {
  73. if (i->Equals(resolution)) {
  74. CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
  75. static_cast<const CGDisplayMode*>(
  76. CFArrayGetValueAtIndex(modes, index)));
  77. int depth = 0;
  78. base::ScopedCFTypeRef<CFStringRef> encoding(
  79. CGDisplayModeCopyPixelEncoding(mode));
  80. if (CFStringCompare(encoding, CFSTR(IO32BitDirectPixels),
  81. kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
  82. depth = 32;
  83. } else if (CFStringCompare(
  84. encoding, CFSTR(IO16BitDirectPixels),
  85. kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
  86. depth = 16;
  87. } else if(CFStringCompare(
  88. encoding, CFSTR(IO8BitIndexedPixels),
  89. kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
  90. depth = 8;
  91. }
  92. if (depth > best_depth) {
  93. best_depth = depth;
  94. best_mode = mode;
  95. }
  96. }
  97. }
  98. if (best_mode) {
  99. HOST_LOG << "Changing mode to " << best_mode << " ("
  100. << resolution.dimensions().width() << "x"
  101. << "x" << resolution.dimensions().height() << "x"
  102. << best_depth << " @ "
  103. << resolution.dpi().x() << "x" << resolution.dpi().y() << " dpi)";
  104. CGDisplaySetDisplayMode(display, best_mode, nullptr);
  105. }
  106. }
  107. void DesktopResizerMac::RestoreResolution(const ScreenResolution& original,
  108. webrtc::ScreenId screen_id) {
  109. SetResolution(original, screen_id);
  110. }
  111. void DesktopResizerMac::GetSupportedModesAndResolutions(
  112. base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
  113. std::list<ScreenResolution>* resolutions) {
  114. CGDirectDisplayID display;
  115. if (!GetSoleDisplayId(&display)) {
  116. return;
  117. }
  118. base::ScopedCFTypeRef<CFArrayRef> all_modes(
  119. CGDisplayCopyAllDisplayModes(display, nullptr));
  120. if (!all_modes) {
  121. return;
  122. }
  123. modes->reset(CFArrayCreateMutableCopy(nullptr, 0, all_modes));
  124. CFIndex count = CFArrayGetCount(*modes);
  125. for (CFIndex i = 0; i < count; ++i) {
  126. CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
  127. static_cast<const CGDisplayMode*>(
  128. CFArrayGetValueAtIndex(*modes, i)));
  129. if (CGDisplayModeIsUsableForDesktopGUI(mode)) {
  130. // TODO(jamiewalch): Get the correct DPI: http://crbug.com/172405.
  131. ScreenResolution resolution(
  132. webrtc::DesktopSize(CGDisplayModeGetWidth(mode),
  133. CGDisplayModeGetHeight(mode)),
  134. webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
  135. resolutions->push_back(resolution);
  136. } else {
  137. CFArrayRemoveValueAtIndex(*modes, i);
  138. --count;
  139. --i;
  140. }
  141. }
  142. }
  143. bool DesktopResizerMac::GetSoleDisplayId(CGDirectDisplayID* display) {
  144. // This code only supports a single display, but allocates space for two
  145. // to allow the multi-monitor case to be detected.
  146. CGDirectDisplayID displays[2];
  147. uint32_t num_displays;
  148. CGError err =
  149. CGGetActiveDisplayList(std::size(displays), displays, &num_displays);
  150. if (err != kCGErrorSuccess || num_displays != 1) {
  151. return false;
  152. }
  153. *display = displays[0];
  154. return true;
  155. }
  156. std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
  157. return base::WrapUnique(new DesktopResizerMac);
  158. }
  159. } // namespace remoting