File: output_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 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 (193 lines) | stat: -rw-r--r-- 8,072 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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/exo/wayland/output_controller.h"

#include <aura-output-management-server-protocol.h>
#include <secure-output-unstable-v1-server-protocol.h>
#include <wayland-server-core.h>
#include <xdg-output-unstable-v1-server-protocol.h>

#include <algorithm>

#include "ash/shell.h"
#include "base/containers/contains.h"
#include "components/exo/wayland/output_configuration_change.h"
#include "components/exo/wayland/wayland_display_output.h"
#include "components/exo/wayland/wl_output.h"
#include "components/exo/wayland/zaura_output_manager.h"
#include "components/exo/wayland/zaura_output_manager_v2.h"
#include "components/exo/wayland/zcr_secure_output.h"
#include "components/exo/wayland/zxdg_output_manager.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"

namespace exo::wayland {

OutputController::OutputController(Delegate* delegate) : delegate_(delegate) {
  // Clients need to bind aura output manager globals before binding any of the
  // wl_output globals. This is necessary to ensure clients won't receive events
  // for outputs before receiving events for the aura output manager.
  wl_display* display = delegate_->GetWaylandDisplay();
  aura_output_manager_v2_ =
      std::make_unique<AuraOutputManagerV2>(base::BindRepeating(
          &OutputController::GetActiveOutputs, base::Unretained(this)));
  wl_global_create(display, &zaura_output_manager_v2_interface,
                   kZAuraOutputManagerV2Version, aura_output_manager_v2_.get(),
                   bind_aura_output_manager_v2);
  wl_global_create(display, &zaura_output_manager_interface,
                   kZAuraOutputManagerVersion, nullptr,
                   bind_aura_output_manager);

  // Populate the initial output state.
  OnDidProcessDisplayChanges(
      {ash::Shell::Get()->display_manager()->active_display_list(),
       Displays(),
       {}});

  wl_global_create(display, &zcr_secure_output_v1_interface,
                   /*version=*/1, nullptr, bind_secure_output);
  wl_global_create(display, &zxdg_output_manager_v1_interface,
                   /*version=*/3, nullptr, bind_zxdg_output_manager);

  display_manager_observation_.Observe(ash::Shell::Get()->display_manager());
  ash::Shell::Get()->AddShellObserver(this);
}

OutputController::~OutputController() {
  ash::Shell::Get()->RemoveShellObserver(this);
}

void OutputController::OnDidProcessDisplayChanges(
    const DisplayConfigurationChange& configuration_change) {
  // Process added displays before removed displays to ensure exo does not leave
  // clients in a temporary state where no outputs are present.
  for (const display::Display& added_display :
       configuration_change.added_displays) {
    auto output = std::make_unique<WaylandDisplayOutput>(added_display);
    output->set_global(wl_global_create(delegate_->GetWaylandDisplay(),
                                        &wl_output_interface, kWlOutputVersion,
                                        output.get(), bind_output));
    CHECK_EQ(outputs_.count(added_display.id()), 0u);
    outputs_.insert(std::make_pair(added_display.id(), std::move(output)));
  }

  for (const auto& change : configuration_change.display_metrics_changes) {
    if (auto* wayland_display_output =
            GetWaylandDisplayOutput(change.display->id())) {
      wayland_display_output->SendDisplayMetricsChanges(change.display.get(),
                                                        change.changed_metrics);
    }
  }

  // Propagate display configuration changes to aura output manager clients.
  const bool needs_done =
      ProcessDisplayChangesForAuraOutputManager(configuration_change);

  // Remove outputs after propagating config changes as the WaylandDisplayOutput
  // object may be needed during change notification propagation.
  for (const display::Display& removed_display :
       configuration_change.removed_displays) {
    // There should always be at least one display tracked by Exo.
    CHECK(outputs_.size() > 1);
    CHECK_EQ(outputs_.count(removed_display.id()), 1u);
    std::unique_ptr<WaylandDisplayOutput> output =
        std::move(outputs_[removed_display.id()]);
    outputs_.erase(removed_display.id());
    output.release()->OnDisplayRemoved();
  }

  if (needs_done) {
    aura_output_manager_v2_->SendDone();
  }

  UpdateActivatedDisplayIfNecessary();

  // Flush updated outputs to clients immediately.
  // TODO(crbug.com/40943061): Exo should be updated to automatically flush
  // buffers at the end of task processing if necessary.
  delegate_->Flush();
}

void OutputController::OnDisplayForNewWindowsChanged() {
  UpdateActivatedDisplayIfNecessary();
}

wl_resource* OutputController::GetOutputResource(wl_client* client,
                                                 int64_t display_id) {
  CHECK_NE(display_id, display::kInvalidDisplayId);
  WaylandDisplayOutput* wayland_display_output =
      GetWaylandDisplayOutput(display_id);
  return wayland_display_output
             ? wayland_display_output->GetOutputResourceForClient(client)
             : nullptr;
}

WaylandDisplayOutput* OutputController::GetWaylandDisplayOutput(
    int64_t display_id) {
  CHECK_NE(display_id, display::kInvalidDisplayId);
  auto iter = outputs_.find(display_id);
  return iter == outputs_.end() ? nullptr : iter->second.get();
}

WaylandOutputList OutputController::GetActiveOutputs() const {
  WaylandOutputList output_list;
  std::ranges::transform(outputs_, std::back_inserter(output_list),
                         [](auto& pair) { return pair.second.get(); });
  return output_list;
}

bool OutputController::ProcessDisplayChangesForAuraOutputManager(
    const DisplayConfigurationChange& configuration_change) {
  OutputConfigurationChange output_config_change;

  std::ranges::transform(configuration_change.added_displays,
                         std::back_inserter(output_config_change.added_outputs),
                         [this](const display::Display& display) {
                           return GetWaylandDisplayOutput(display.id());
                         });
  std::ranges::transform(
      configuration_change.removed_displays,
      std::back_inserter(output_config_change.removed_outputs),
      [this](const display::Display& display) {
        return GetWaylandDisplayOutput(display.id());
      });
  for (const DisplayMetricsChange& change :
       configuration_change.display_metrics_changes) {
    // Added displays may appear in both added and changed lists, ensure added
    // outputs are not represented in both added and changed lists.
    if (!base::Contains(configuration_change.added_displays,
                        change.display.get())) {
      output_config_change.changed_outputs.emplace_back(
          GetWaylandDisplayOutput(change.display->id()),
          change.changed_metrics);
    }
  }

  return aura_output_manager_v2_->OnDidProcessDisplayChanges(
      output_config_change);
}

void OutputController::UpdateActivatedDisplayIfNecessary() {
  const int64_t current_active_display_id =
      display::Screen::GetScreen()->GetDisplayForNewWindows().id();
  if (dispatched_activated_display_id_ == current_active_display_id) {
    return;
  }

  // Since the ordering of observers on the display manager is not well defined
  // there may be observers that respond to display changes before the
  // controller, resulting in attempted activations of outputs that have not yet
  // been created (see b/323403137).
  // TODO(tluk): Explore solutions that may improve ordering guarantees without
  // relying on state-tracking within the controller class.
  auto output_pair = outputs_.find(current_active_display_id);
  if (output_pair != outputs_.end()) {
    output_pair->second->SendOutputActivated();
    aura_output_manager_v2_->SendOutputActivated(*output_pair->second);
    dispatched_activated_display_id_ = current_active_display_id;
  }
}

}  // namespace exo::wayland