#include "remoting/host/linux/x11_util.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 {
ScopedXGrabServer::ScopedXGrabServer(x11::Connection* connection)
: connection_(connection) {
connection_->GrabServer();
}
ScopedXGrabServer::~ScopedXGrabServer() {
connection_->UngrabServer();
connection_->Flush();
}
bool IgnoreXServerGrabs(x11::Connection* connection, bool ignore) {
if (!connection->xtest()
.GetVersion({x11::Test::major_version, x11::Test::minor_version})
.Sync()) {
return false;
}
connection->xtest().GrabControl({ignore});
return true;
}
bool IsVirtualSession(x11::Connection* connection) {
if (!connection->xinput().present()) {
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") {
} 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()) {
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;
}
}