File: x11_util.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (174 lines) | stat: -rw-r--r-- 6,204 bytes parent folder | download | duplicates (6)
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