1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
// Copyright 2021 The Chromium Authors
// 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 <windows.h>
#include <algorithm>
#include <limits>
#include <vector>
#include "base/compiler_specific.h"
#include "base/strings/utf_string_conversions.h"
namespace remoting {
namespace {
struct PathWithName {
DISPLAYCONFIG_PATH_INFO path;
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_device_name;
};
std::vector<DISPLAYCONFIG_PATH_INFO> GetDisplayConfigPathInfos() {
LONG result;
do {
uint32_t path_elements, mode_elements;
if (::GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_elements,
&mode_elements) != ERROR_SUCCESS) {
return {};
}
std::vector<DISPLAYCONFIG_PATH_INFO> path_infos(path_elements);
std::vector<DISPLAYCONFIG_MODE_INFO> mode_infos(mode_elements);
result = ::QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_elements,
path_infos.data(), &mode_elements,
mode_infos.data(), nullptr);
if (result == ERROR_SUCCESS) {
path_infos.resize(path_elements);
return path_infos;
}
} while (result == ERROR_INSUFFICIENT_BUFFER);
return {};
}
std::vector<PathWithName> GetDisplayPathsWithNames() {
std::vector<PathWithName> result;
for (const auto& path : GetDisplayConfigPathInfos()) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
source_name.header.size = sizeof(source_name);
source_name.header.adapterId = path.sourceInfo.adapterId;
source_name.header.id = path.sourceInfo.id;
if (::DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {
result.push_back({path, source_name});
}
}
return result;
}
std::string GetFriendlyDeviceName(const DISPLAYCONFIG_PATH_INFO& path) {
DISPLAYCONFIG_TARGET_DEVICE_NAME target_name = {};
target_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
target_name.header.size = sizeof(target_name);
target_name.header.adapterId = path.targetInfo.adapterId;
target_name.header.id = path.targetInfo.id;
if (::DisplayConfigGetDeviceInfo(&target_name.header) == ERROR_SUCCESS) {
return base::WideToUTF8(target_name.monitorFriendlyDeviceName);
}
return std::string();
}
class DesktopDisplayInfoLoaderWin : public DesktopDisplayInfoLoader {
public:
DesktopDisplayInfoLoaderWin() = default;
~DesktopDisplayInfoLoaderWin() override = default;
DesktopDisplayInfo GetCurrentDisplayInfo() override;
};
DesktopDisplayInfo DesktopDisplayInfoLoaderWin::GetCurrentDisplayInfo() {
// Obtain the paths and names of all display devices on the system. This
// list will be used to lookup the path for each DISPLAY_DEVICE enumerated
// below. If found, the path is used to obtain the device's friendly name.
auto paths_with_names = GetDisplayPathsWithNames();
int32_t lowest_x = std::numeric_limits<int32_t>::max();
int32_t lowest_y = std::numeric_limits<int32_t>::max();
std::vector<DisplayGeometry> displays;
BOOL enum_result = TRUE;
for (int device_index = 0;; ++device_index) {
DISPLAY_DEVICE device = {};
device.cb = sizeof(device);
enum_result = EnumDisplayDevices(NULL, device_index, &device, 0);
// |enum_result| is 0 if we have enumerated all devices.
if (!enum_result) {
break;
}
// We only care about active displays.
if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
continue;
}
bool is_default = false;
if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
is_default = true;
}
// Find the path corresponding to this display device. If found, use it to
// get the friendly name for the device.
std::string monitor_name;
for (const auto& entry : paths_with_names) {
if (UNSAFE_TODO(wcscmp(entry.source_device_name.viewGdiDeviceName,
device.DeviceName)) == 0) {
monitor_name = GetFriendlyDeviceName(entry.path);
break;
}
}
// Get additional info about device.
DEVMODE devmode;
devmode.dmSize = sizeof(devmode);
EnumDisplaySettingsEx(device.DeviceName, ENUM_CURRENT_SETTINGS, &devmode,
0);
int32_t x = devmode.dmPosition.x;
int32_t y = devmode.dmPosition.y;
displays.emplace_back(
/* id */ device_index, x, y, devmode.dmPelsWidth, devmode.dmPelsHeight,
/* dpi */ devmode.dmLogPixels, devmode.dmBitsPerPel, is_default,
monitor_name);
lowest_x = std::min(x, lowest_x);
lowest_y = std::min(y, lowest_y);
}
// Normalize the displays so the bounding-box's top-left corner is at (0, 0).
// This matches the coordinate system used by InputInjectorWin, so that
// the FractionalInputFilter produces correct x,y-coordinates for injection.
DesktopDisplayInfo result;
for (DisplayGeometry& info : displays) {
info.x -= lowest_x;
info.y -= lowest_y;
result.AddDisplay(info);
}
return result;
}
} // namespace
// static
std::unique_ptr<DesktopDisplayInfoLoader> DesktopDisplayInfoLoader::Create() {
return std::make_unique<DesktopDisplayInfoLoaderWin>();
}
} // namespace remoting
|