| // Copyright 2018 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/wayland_display_observer.h" |
| |
| #include <wayland-server-core.h> |
| #include <wayland-server-protocol-core.h> |
| #include <xdg-output-unstable-v1-server-protocol.h> |
| |
| #include "components/exo/wayland/output_metrics.h" |
| #include "components/exo/wayland/server_util.h" |
| #include "components/exo/wayland/wayland_display_output.h" |
| #include "components/exo/wayland/zaura_output_manager.h" |
| #include "ui/display/display_observer.h" |
| #include "ui/display/screen.h" |
| |
| using DisplayMetric = display::DisplayObserver::DisplayMetric; |
| |
| namespace exo { |
| namespace wayland { |
| |
| WaylandDisplayObserver::WaylandDisplayObserver() = default; |
| |
| WaylandDisplayObserver::~WaylandDisplayObserver() = default; |
| |
| WaylandDisplayHandler::WaylandDisplayHandler(WaylandDisplayOutput* output, |
| wl_resource* output_resource) |
| : output_(output), output_resource_(output_resource) {} |
| |
| WaylandDisplayHandler::~WaylandDisplayHandler() { |
| for (auto& obs : observers_) { |
| obs.OnOutputDestroyed(); |
| } |
| if (xdg_output_resource_) { |
| wl_resource_set_user_data(xdg_output_resource_, nullptr); |
| } |
| output_->UnregisterOutput(output_resource_); |
| } |
| |
| void WaylandDisplayHandler::Initialize() { |
| // Adding itself as an observer will send the initial display metrics. |
| AddObserver(this); |
| output_->RegisterOutput(output_resource_); |
| } |
| |
| void WaylandDisplayHandler::AddObserver(WaylandDisplayObserver* observer) { |
| observers_.AddObserver(observer); |
| |
| display::Display display; |
| bool exists = display::Screen::Get()->GetDisplayWithDisplayId(id(), &display); |
| if (!exists) { |
| // WaylandDisplayHandler is created asynchronously, and the |
| // display can be deleted before created. This usually won't happen |
| // in real environment, but can happen in test environment. |
| return; |
| } |
| |
| // Send the first round of changes to the observer. |
| constexpr uint32_t kAllChanges = 0xFFFFFFFF; |
| SendDisplayMetricsChanges(display, kAllChanges); |
| } |
| |
| void WaylandDisplayHandler::RemoveObserver(WaylandDisplayObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| int64_t WaylandDisplayHandler::id() const { |
| DCHECK(output_); |
| return output_->id(); |
| } |
| |
| void WaylandDisplayHandler::SendDisplayMetricsChanges( |
| const display::Display& display, |
| uint32_t changed_metrics) { |
| CHECK(output_resource_); |
| CHECK_EQ(id(), display.id()); |
| |
| bool needs_done = false; |
| |
| // If supported, the aura_output_manager must have been bound by clients |
| // before the wl_output associated with this WaylandDisplayHandler is bound. |
| if (auto* output_manager = GetAuraOutputManager()) { |
| // This sends all relevant output metrics to clients. These events are sent |
| // immediately after the client binds an output and again every time display |
| // metrics have changed. |
| needs_done |= output_manager->SendOutputMetrics(output_resource_, display, |
| changed_metrics); |
| } |
| |
| for (auto& observer : observers_) { |
| needs_done |= observer.SendDisplayMetrics(display, changed_metrics); |
| } |
| |
| if (needs_done) { |
| if (wl_resource_get_version(output_resource_) >= |
| WL_OUTPUT_DONE_SINCE_VERSION) { |
| wl_output_send_done(output_resource_); |
| } |
| wl_client_flush(wl_resource_get_client(output_resource_)); |
| } |
| } |
| |
| void WaylandDisplayHandler::SendDisplayActivated() { |
| for (auto& observer : observers_) { |
| observer.SendActiveDisplay(); |
| } |
| } |
| |
| void WaylandDisplayHandler::OnXdgOutputCreated( |
| wl_resource* xdg_output_resource) { |
| DCHECK(!xdg_output_resource_); |
| xdg_output_resource_ = xdg_output_resource; |
| |
| display::Display display; |
| if (!display::Screen::Get()->GetDisplayWithDisplayId(id(), &display)) { |
| return; |
| } |
| |
| if (SendXdgOutputMetrics(display, 0xFFFFFFFF)) { |
| if (wl_resource_get_version(output_resource_) >= |
| WL_OUTPUT_DONE_SINCE_VERSION) { |
| wl_output_send_done(output_resource_); |
| } |
| wl_client_flush(wl_resource_get_client(output_resource_)); |
| } |
| } |
| |
| void WaylandDisplayHandler::UnsetXdgOutputResource() { |
| DCHECK(xdg_output_resource_); |
| xdg_output_resource_ = nullptr; |
| } |
| |
| void WaylandDisplayHandler::XdgOutputSendLogicalPosition( |
| const gfx::Point& position) { |
| zxdg_output_v1_send_logical_position(xdg_output_resource_, position.x(), |
| position.y()); |
| } |
| |
| void WaylandDisplayHandler::XdgOutputSendLogicalSize(const gfx::Size& size) { |
| zxdg_output_v1_send_logical_size(xdg_output_resource_, size.width(), |
| size.height()); |
| } |
| |
| void WaylandDisplayHandler::XdgOutputSendDescription(const std::string& desc) { |
| if (wl_resource_get_version(xdg_output_resource_) < |
| ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION) { |
| return; |
| } |
| zxdg_output_v1_send_description(xdg_output_resource_, desc.c_str()); |
| } |
| |
| bool WaylandDisplayHandler::SendDisplayMetrics(const display::Display& display, |
| uint32_t changed_metrics) { |
| if (!output_resource_) { |
| return false; |
| } |
| |
| // There is no need to check DISPLAY_METRIC_PRIMARY because when primary |
| // changes, bounds always changes. (new primary should have had non |
| // 0,0 origin). |
| // Only exception is when switching to newly connected primary with |
| // the same bounds. This happens when you're in docked mode, suspend, |
| // unplug the display, then resume to the internal display which has |
| // the same resolution. Since metrics does not change, there is no need |
| // to notify clients. |
| |
| const OutputMetrics output_metrics(display); |
| bool result = false; |
| |
| if (changed_metrics & (DisplayMetric::DISPLAY_METRIC_BOUNDS | |
| DisplayMetric::DISPLAY_METRIC_ROTATION | |
| DisplayMetric::DISPLAY_METRIC_REFRESH_RATE)) { |
| wl_output_send_geometry( |
| output_resource_, output_metrics.origin.x(), output_metrics.origin.y(), |
| output_metrics.physical_size_mm.width(), |
| output_metrics.physical_size_mm.height(), output_metrics.subpixel, |
| output_metrics.make.c_str(), output_metrics.model.c_str(), |
| output_metrics.panel_transform); |
| wl_output_send_mode(output_resource_, output_metrics.mode_flags, |
| output_metrics.physical_size_px.width(), |
| output_metrics.physical_size_px.height(), |
| output_metrics.refresh_mhz); |
| result = true; |
| } |
| |
| if (changed_metrics & DisplayMetric::DISPLAY_METRIC_DEVICE_SCALE_FACTOR) { |
| if (wl_resource_get_version(output_resource_) >= |
| WL_OUTPUT_SCALE_SINCE_VERSION) { |
| wl_output_send_scale(output_resource_, output_metrics.scale); |
| result = true; |
| } |
| } |
| |
| if (SendXdgOutputMetrics(display, changed_metrics)) { |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| bool WaylandDisplayHandler::SendXdgOutputMetrics( |
| const display::Display& display, |
| uint32_t changed_metrics) { |
| if (!xdg_output_resource_) { |
| return false; |
| } |
| |
| const OutputMetrics output_metrics(display); |
| bool result = false; |
| |
| if (changed_metrics & (DisplayMetric::DISPLAY_METRIC_BOUNDS | |
| DisplayMetric::DISPLAY_METRIC_ROTATION | |
| DisplayMetric::DISPLAY_METRIC_DEVICE_SCALE_FACTOR)) { |
| XdgOutputSendLogicalPosition(output_metrics.logical_origin); |
| XdgOutputSendLogicalSize(output_metrics.logical_size); |
| XdgOutputSendDescription(output_metrics.description); |
| if (wl_resource_get_version(xdg_output_resource_) < 3) { |
| zxdg_output_v1_send_done(xdg_output_resource_); |
| } |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| void WaylandDisplayHandler::SendActiveDisplay() { |
| if (auto* output_manager = GetAuraOutputManager()) { |
| output_manager->SendOutputActivated(output_resource_); |
| } |
| } |
| |
| void WaylandDisplayHandler::OnOutputDestroyed() { |
| // destroying itself. |
| RemoveObserver(this); |
| } |
| |
| AuraOutputManager* WaylandDisplayHandler::GetAuraOutputManager() { |
| wl_client* client = wl_resource_get_client(output_resource_); |
| CHECK(client); |
| return AuraOutputManager::Get(client); |
| } |
| |
| size_t WaylandDisplayHandler::CountObserversForTesting() const { |
| size_t count = 0; |
| for (auto& obs : observers_) { |
| if (&obs != this) { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| } // namespace wayland |
| } // namespace exo |