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
|