| // 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/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) { |
| // 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; |
| } |
| |
| } // namespace remoting |