/* * Copyright (C) 2017-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "Output.h" #include #include #include #include #include #include using namespace KODI::WINDOWING::WAYLAND; COutput::COutput(std::uint32_t globalName, wayland::output_t const& output, std::function doneHandler) : m_globalName{globalName}, m_output{output}, m_doneHandler{std::move(doneHandler)} { assert(m_output); m_output.on_geometry() = [this](std::int32_t x, std::int32_t y, std::int32_t physWidth, std::int32_t physHeight, wayland::output_subpixel, std::string const& make, std::string const& model, const wayland::output_transform&) { std::unique_lock lock(m_geometryCriticalSection); m_position = {x, y}; // Some monitors report invalid (negative) values that would cause an exception // with CSizeInt if (physWidth < 0 || physHeight < 0) m_physicalSize = {}; else m_physicalSize = {physWidth, physHeight}; m_make = make; m_model = model; }; m_output.on_mode() = [this](const wayland::output_mode& flags, std::int32_t width, std::int32_t height, std::int32_t refresh) { // std::set.emplace returns pair of iterator to the (possibly) inserted // element and boolean information whether the element was actually added // which we do not need auto modeIterator = m_modes.emplace(CSizeInt{width, height}, refresh).first; std::unique_lock lock(m_iteratorCriticalSection); // Remember current and preferred mode // Current mode is the last one that was sent with current flag set if (flags & wayland::output_mode::current) { m_currentMode = modeIterator; } if (flags & wayland::output_mode::preferred) { m_preferredMode = modeIterator; } }; m_output.on_scale() = [this](std::int32_t scale) { m_scale = scale; }; m_output.on_done() = [this]() { m_doneHandler(); }; } COutput::~COutput() noexcept { // Reset event handlers - someone might still hold a reference to the output_t, // causing events to be dispatched. They should not go to a deleted class. m_output.on_geometry() = nullptr; m_output.on_mode() = nullptr; m_output.on_done() = nullptr; m_output.on_scale() = nullptr; } const COutput::Mode& COutput::GetCurrentMode() const { std::unique_lock lock(m_iteratorCriticalSection); if (m_currentMode == m_modes.end()) { throw std::runtime_error("Current mode not set"); } return *m_currentMode; } const COutput::Mode& COutput::GetPreferredMode() const { std::unique_lock lock(m_iteratorCriticalSection); if (m_preferredMode == m_modes.end()) { throw std::runtime_error("Preferred mode not set"); } return *m_preferredMode; } float COutput::GetPixelRatioForMode(const Mode& mode) const { std::unique_lock lock(m_geometryCriticalSection); if (m_physicalSize.IsZero() || mode.size.IsZero()) { return 1.0f; } else { return ( (static_cast (m_physicalSize.Width()) / static_cast (mode.size.Width())) / (static_cast (m_physicalSize.Height()) / static_cast (mode.size.Height())) ); } } float COutput::GetDpiForMode(const Mode& mode) const { std::unique_lock lock(m_geometryCriticalSection); if (m_physicalSize.IsZero()) { // We really have no idea, so use a "sane" default return 96.0; } else { constexpr float INCH_MM_RATIO{25.4f}; float diagonalPixels = std::sqrt(mode.size.Width() * mode.size.Width() + mode.size.Height() * mode.size.Height()); // physicalWidth/physicalHeight is in millimeters float diagonalInches = std::sqrt(static_cast(m_physicalSize.Width() * m_physicalSize.Width() + m_physicalSize.Height() * m_physicalSize.Height())) / INCH_MM_RATIO; return diagonalPixels / diagonalInches; } } float COutput::GetCurrentDpi() const { return GetDpiForMode(GetCurrentMode()); }