1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Output.h"
10 
11 #include <cassert>
12 #include <cmath>
13 #include <set>
14 #include <stdexcept>
15 #include <utility>
16 
17 using namespace KODI::WINDOWING::WAYLAND;
18 
COutput(std::uint32_t globalName,wayland::output_t const & output,std::function<void ()> doneHandler)19 COutput::COutput(std::uint32_t globalName,
20                  wayland::output_t const& output,
21                  std::function<void()> doneHandler)
22   : m_globalName{globalName}, m_output{output}, m_doneHandler{std::move(doneHandler)}
23 {
24   assert(m_output);
25 
26   m_output.on_geometry() = [this](std::int32_t x, std::int32_t y, std::int32_t physWidth,
27                                   std::int32_t physHeight, wayland::output_subpixel,
28                                   std::string const& make, std::string const& model,
__anonbdd65a6c0102(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&) 29                                   const wayland::output_transform&) {
30     CSingleLock lock(m_geometryCriticalSection);
31     m_position = {x, y};
32     // Some monitors report invalid (negative) values that would cause an exception
33     // with CSizeInt
34     if (physWidth < 0 || physHeight < 0)
35       m_physicalSize = {};
36     else
37       m_physicalSize = {physWidth, physHeight};
38     m_make = make;
39     m_model = model;
40   };
41   m_output.on_mode() = [this](const wayland::output_mode& flags, std::int32_t width,
__anonbdd65a6c0202(const wayland::output_mode& flags, std::int32_t width, std::int32_t height, std::int32_t refresh) 42                               std::int32_t height, std::int32_t refresh) {
43     // std::set.emplace returns pair of iterator to the (possibly) inserted
44     // element and boolean information whether the element was actually added
45     // which we do not need
46     auto modeIterator = m_modes.emplace(CSizeInt{width, height}, refresh).first;
47     CSingleLock lock(m_iteratorCriticalSection);
48     // Remember current and preferred mode
49     // Current mode is the last one that was sent with current flag set
50     if (flags & wayland::output_mode::current)
51     {
52       m_currentMode = modeIterator;
53     }
54     if (flags & wayland::output_mode::preferred)
55     {
56       m_preferredMode = modeIterator;
57     }
58   };
59   m_output.on_scale() = [this](std::int32_t scale)
__anonbdd65a6c0302(std::int32_t scale) 60   {
61     m_scale = scale;
62   };
63 
64   m_output.on_done() = [this]()
__anonbdd65a6c0402() 65   {
66     m_doneHandler();
67   };
68 }
69 
~COutput()70 COutput::~COutput() noexcept
71 {
72   // Reset event handlers - someone might still hold a reference to the output_t,
73   // causing events to be dispatched. They should not go to a deleted class.
74   m_output.on_geometry() = nullptr;
75   m_output.on_mode() = nullptr;
76   m_output.on_done() = nullptr;
77   m_output.on_scale() = nullptr;
78 }
79 
GetCurrentMode() const80 const COutput::Mode& COutput::GetCurrentMode() const
81 {
82   CSingleLock lock(m_iteratorCriticalSection);
83   if (m_currentMode == m_modes.end())
84   {
85     throw std::runtime_error("Current mode not set");
86   }
87   return *m_currentMode;
88 }
89 
GetPreferredMode() const90 const COutput::Mode& COutput::GetPreferredMode() const
91 {
92   CSingleLock lock(m_iteratorCriticalSection);
93   if (m_preferredMode == m_modes.end())
94   {
95     throw std::runtime_error("Preferred mode not set");
96   }
97   return *m_preferredMode;
98 }
99 
GetPixelRatioForMode(const Mode & mode) const100 float COutput::GetPixelRatioForMode(const Mode& mode) const
101 {
102   CSingleLock lock(m_geometryCriticalSection);
103   if (m_physicalSize.IsZero() || mode.size.IsZero())
104   {
105     return 1.0f;
106   }
107   else
108   {
109     return (
110             (static_cast<float> (m_physicalSize.Width()) / static_cast<float> (mode.size.Width()))
111             /
112             (static_cast<float> (m_physicalSize.Height()) / static_cast<float> (mode.size.Height()))
113             );
114   }
115 }
116 
GetDpiForMode(const Mode & mode) const117 float COutput::GetDpiForMode(const Mode& mode) const
118 {
119   CSingleLock lock(m_geometryCriticalSection);
120   if (m_physicalSize.IsZero())
121   {
122     // We really have no idea, so use a "sane" default
123     return 96.0;
124   }
125   else
126   {
127     constexpr float INCH_MM_RATIO{25.4f};
128 
129     float diagonalPixels = std::sqrt(mode.size.Width() * mode.size.Width() + mode.size.Height() * mode.size.Height());
130     // physicalWidth/physicalHeight is in millimeters
131     float diagonalInches = std::sqrt(m_physicalSize.Width() * m_physicalSize.Width() + m_physicalSize.Height() * m_physicalSize.Height()) / INCH_MM_RATIO;
132 
133     return diagonalPixels / diagonalInches;
134   }
135 }
136 
GetCurrentDpi() const137 float COutput::GetCurrentDpi() const
138 {
139   return GetDpiForMode(GetCurrentMode());
140 }
141