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