File: display_port_observer.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (144 lines) | stat: -rw-r--r-- 5,522 bytes parent folder | download | duplicates (7)
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
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/display/manager/display_port_observer.h"

#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/display/types/display_snapshot.h"

namespace display {

namespace {

const LazyRE2 kTypecConnUeventPattern = {R"(TYPEC_PORT=port(\d+))"};

std::vector<uint32_t> ParseDrmSysfsAndFindPort(
    const std::vector<std::pair<uint64_t, base::FilePath>>&
        base_connector_id_and_syspath) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  std::vector<uint32_t> port_nums;
  // Each pair is from each DisplaySnapshot.
  for (const auto& pair : base_connector_id_and_syspath) {
    auto base_connector_id = pair.first;
    const auto& sys_path = pair.second;
    // `sys_path` of a DRM device, i.e. /sys/class/drm/cardX.
    base::FileEnumerator enumerator(sys_path, false,
                                    base::FileEnumerator::DIRECTORIES,
                                    FILE_PATH_LITERAL("card*-DP-*"));
    // Each directory in `sys_path` represents a connector, so we fetch each
    // connector's ID by reading the `/sys/class/drm/*/connector_id` file
    // (e.g. /sys/class/drm/card0/card0-DP-1/connector_id stores 256). We use
    // the fetched connector id to compare and match the corresponding base
    // connector (root of MST hub tree) per each DisplaySnapshot.
    for (auto path = enumerator.Next(); !path.empty();
         path = enumerator.Next()) {
      std::string connector_id_str;
      uint64_t connector_id_int;
      if (!base::ReadFileToString(path.Append("connector_id"),
                                  &connector_id_str)) {
        continue;
      }
      base::TrimWhitespaceASCII(connector_id_str, base::TRIM_ALL,
                                &connector_id_str);
      if (!base::StringToUint64(connector_id_str, &connector_id_int)) {
        LOG(WARNING) << "Invalid connector id " << connector_id_str << " at "
                     << path.value();
        continue;
      }
      if (connector_id_int != base_connector_id) {
        continue;
      }
      // A connector with a matching id is found at this point, so break the
      // loop beyond here as there is no need to further look for a connector.

      // We find the USB-C port which the DP connector is routed to by
      // referring to the symlink to Type C connector at `typec_connector`
      // (i.e. /sys/class/drm/cardX/cardX-DP-X/typec_connector). This symlink
      // may not be present for older kernel and firmware.
      std::string typec_conn_uevent;
      uint32_t port_num;
      if (!base::ReadFileToString(path.Append("typec_connector/uevent"),
                                  &typec_conn_uevent)) {
        break;
      }
      if (!RE2::PartialMatch(typec_conn_uevent, *kTypecConnUeventPattern,
                             &port_num)) {
        break;
      }
      port_nums.push_back(port_num);
      break;
    }
  }
  return port_nums;
}

}  // namespace

DisplayPortObserver::DisplayPortObserver(
    DisplayConfigurator* configurator,
    base::RepeatingCallback<void(const std::vector<uint32_t>&)>
        on_port_change_callback)
    : configurator_(configurator),
      on_port_change_callback_(on_port_change_callback) {
  if (configurator_) {
    configurator_->AddObserver(this);
  }
}

DisplayPortObserver::~DisplayPortObserver() {
  if (configurator_) {
    configurator_->RemoveObserver(this);
  }
}

void DisplayPortObserver::OnDisplayConfigurationChanged(
    const DisplayConfigurator::DisplayStateList& display_states) {
  // If no changes in base connector ids, then assume no changes on ports, so
  // return early to prevent overkill.
  std::set<uint64_t> base_connector_ids_;
  for (display::DisplaySnapshot* state : display_states) {
    base_connector_ids_.insert(state->base_connector_id());
  }
  if (base_connector_ids_ == prev_base_connector_ids_) {
    return;
  }
  prev_base_connector_ids_ = base_connector_ids_;

  // For each DisplaySnapshot, extract base_connector_id and sys_path.
  std::vector<std::pair<uint64_t, base::FilePath>>
      base_connector_id_and_syspath;
  for (display::DisplaySnapshot* state : display_states) {
    base_connector_id_and_syspath.push_back(
        std::make_pair(state->base_connector_id(), state->sys_path()));
  }

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE,
      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
      base::BindOnce(&ParseDrmSysfsAndFindPort, base_connector_id_and_syspath),
      base::BindOnce(&DisplayPortObserver::SetTypeCPortsUsingDisplays,
                     weak_ptr_factory_.GetWeakPtr()));
}

void DisplayPortObserver::OnDisplayConfigurationChangeFailed(
    const DisplayConfigurator::DisplayStateList& displays,
    MultipleDisplayState failed_new_state) {}

void DisplayPortObserver::SetTypeCPortsUsingDisplays(
    std::vector<uint32_t> port_nums) {
  on_port_change_callback_.Run(port_nums);
}

}  // namespace display