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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Copyright 2012 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/linux/x11_util.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/types/cxx23_to_underlying.h"
#include "remoting/base/logging.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/xinput.h"
#include "ui/gfx/x/xproto_types.h"
#include "ui/gfx/x/xtest.h"
namespace remoting {
bool IgnoreXServerGrabs(x11::Connection* connection, bool ignore) {
if (!connection->xtest().present()) {
return false;
}
connection->xtest().GrabControl({ignore});
return true;
}
bool IsVirtualSession(x11::Connection* connection) {
// Try to identify a virtual session. Since there's no way to tell from the
// vendor string, we check for known virtual input devices.
// For Xorg+video-dummy, there is no input device that is specific to CRD, so
// we just check if all input devices are virtual and if the display outputs
// are all DUMMY*.
// TODO(lambroslambrou): Find a similar way to determine that the *output* is
// secure.
if (!connection->xinput().present()) {
// If XInput is not available, assume it is not a virtual session.
LOG(ERROR) << "X Input extension not available";
return false;
}
auto devices = connection->xinput().ListInputDevices().Sync();
if (!devices) {
LOG(ERROR) << "ListInputDevices failed";
return false;
}
bool found_xvfb_mouse = false;
bool found_xvfb_keyboard = false;
bool found_other_devices = false;
for (size_t i = 0; i < devices->devices.size(); i++) {
const auto& device_info = devices->devices[i];
const std::string& name = devices->names[i].name;
if (device_info.device_use == x11::Input::DeviceUse::IsXExtensionPointer) {
if (name == "Xvfb mouse") {
found_xvfb_mouse = true;
} else if (name == "Chrome Remote Desktop Input") {
// Ignored. This device only exists if xserver-xorg-input-void is
// installed, which is no longer available since Debian 11, so we can't
// use it to reliably check if the user is on Xorg+video-dummy.
} else if (name != "Virtual core XTEST pointer") {
found_other_devices = true;
HOST_LOG << "Non-virtual mouse found: " << name;
}
} else if (device_info.device_use ==
x11::Input::DeviceUse::IsXExtensionKeyboard) {
if (name == "Xvfb keyboard") {
found_xvfb_keyboard = true;
} else if (name != "Virtual core XTEST keyboard") {
found_other_devices = true;
HOST_LOG << "Non-virtual keyboard found: " << name;
}
} else if (device_info.device_use == x11::Input::DeviceUse::IsXPointer) {
if (name != "Virtual core pointer") {
found_other_devices = true;
HOST_LOG << "Non-virtual mouse found: " << name;
}
} else if (device_info.device_use == x11::Input::DeviceUse::IsXKeyboard) {
if (name != "Virtual core keyboard") {
found_other_devices = true;
HOST_LOG << "Non-virtual keyboard found: " << name;
}
} else {
found_other_devices = true;
HOST_LOG << "Non-virtual device found: " << name;
}
}
return ((found_xvfb_mouse && found_xvfb_keyboard) ||
IsUsingVideoDummyDriver(connection)) &&
!found_other_devices;
}
bool IsUsingVideoDummyDriver(x11::Connection* connection) {
if (!connection->randr().present()) {
// If RANDR is not available, assume it is not using a video dummy driver.
LOG(ERROR) << "RANDR extension not available";
return false;
}
auto root = connection->default_root();
auto randr = connection->randr();
auto resources = randr.GetScreenResourcesCurrent({root}).Sync();
if (!resources) {
LOG(ERROR) << "Cannot get screen resources";
return false;
}
if (resources->outputs.empty()) {
LOG(ERROR) << "RANDR returns no outputs";
return false;
}
bool has_only_dummy_outputs = true;
for (x11::RandR::Output output : resources->outputs) {
auto output_info =
randr
.GetOutputInfo({.output = output,
.config_timestamp = resources->config_timestamp})
.Sync();
if (!output_info) {
LOG(WARNING) << "Cannot get info for output "
<< base::to_underlying(output);
continue;
}
auto* output_name = reinterpret_cast<char*>(output_info->name.data());
if (!base::StartsWith(output_name, "DUMMY")) {
HOST_LOG << "Non-dummy output found: " << output_name;
has_only_dummy_outputs = false;
break;
}
}
return has_only_dummy_outputs;
}
x11::Atom GetX11Atom(x11::Connection* connection, const std::string& name) {
auto reply = connection->InternAtom({false, name}).Sync();
if (!reply) {
LOG(ERROR) << "Failed to intern atom " << name;
return x11::Atom::None;
}
return reply->atom;
}
void SetOutputPhysicalSizeInMM(x11::Connection* connection,
x11::RandR::Output output,
int width,
int height) {
static const x11::Atom width_mm_atom = GetX11Atom(connection, "WIDTH_MM");
static const x11::Atom height_mm_atom = GetX11Atom(connection, "HEIGHT_MM");
if (width_mm_atom == x11::Atom::None || height_mm_atom == x11::Atom::None) {
return;
}
auto width_32 = static_cast<uint32_t>(width);
auto height_32 = static_cast<uint32_t>(height);
x11::RandR::ChangeOutputPropertyRequest request = {
.output = output,
.property = width_mm_atom,
.type = x11::Atom::INTEGER,
.format = 32,
.mode = x11::PropMode::Replace,
.num_units = 1,
.data = base::MakeRefCounted<base::RefCountedStaticMemory>(
base::byte_span_from_ref(width_32)),
};
connection->randr().ChangeOutputProperty(request).Sync();
request.property = height_mm_atom;
request.data = base::MakeRefCounted<base::RefCountedStaticMemory>(
base::byte_span_from_ref(height_32));
connection->randr().ChangeOutputProperty(request).Sync();
}
} // namespace remoting
|