MythTV master
mythdisplayx11.cpp
Go to the documentation of this file.
1// MythTV
4#include "mythdisplayx11.h"
5#include "mythxdisplay.h"
6
7// X11
8#if defined(_X11_XLIB_H_) && !defined(Bool)
9#define Bool int
10#endif
11#include <X11/Xatom.h>
12#define pointer Xpointer // Prevent conflicts with Qt6.
13#include <X11/extensions/Xrandr.h>
14#undef pointer
15
16#define LOC QString("DisplayX11: ")
17
18static XRROutputInfo* GetOutput(XRRScreenResources* Resources, MythXDisplay* mDisplay,
19 QScreen* qScreen, RROutput* Output = nullptr);
20
22{
23 Initialise();
24}
25
27{
28 static bool s_checked = false;
29 static bool s_available = false;
30 if (!s_checked)
31 {
32 s_checked = true;
33 MythXDisplay display;
34 s_available = display.Open();
35 }
36 return s_available;
37}
38
53{
54 // Get some Qt basics first
56
57 auto * display = MythXDisplay::OpenMythXDisplay();
58 if (display)
59 {
60 // XRANDR should always be accurate
61 GetEDID(display);
62 auto * res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
63 auto * output = GetOutput(res, display, m_screen);
64 if (output)
65 {
66 m_physicalSize = QSize(static_cast<int>(output->mm_width),
67 static_cast<int>(output->mm_height));
68 XRRFreeOutputInfo(output);
69 }
70
71 if (!m_crtc)
72 (void)GetVideoModes();
73 while (m_crtc && res)
74 {
75 auto * currentcrtc = XRRGetCrtcInfo(display->GetDisplay(), res, m_crtc);
76 if (!currentcrtc)
77 break;
78 for (int i = 0; i < res->nmode; ++i)
79 {
80 if (res->modes[i].id != currentcrtc->mode)
81 continue;
82 auto mode = res->modes[i];
83 m_resolution = QSize(static_cast<int>(mode.width),
84 static_cast<int>(mode.height));
85 if (mode.dotClock > 1 && mode.vTotal > 1 && mode.hTotal > 1)
86 {
87 m_refreshRate = static_cast<double>(mode.dotClock) / (mode.vTotal * mode.hTotal);
88 if (mode.modeFlags & RR_Interlace)
89 m_refreshRate *= 2.0;
90 }
91 }
92 XRRFreeCrtcInfo(currentcrtc);
93 break;
94 }
95 XRRFreeScreenResources(res);
96
97 delete display;
98 m_modeComplete = true;
99 return;
100 }
101}
102
104{
105 if (gCoreContext)
106 return gCoreContext->GetBoolSetting("UseVideoModes", false);
107 return false;
108}
109
111{
112 if (!m_videoModes.empty() || !m_screen)
113 return m_videoModes;
114
115 m_videoModes.clear();
116 m_modeMap.clear();
117
118 auto * display = MythXDisplay::OpenMythXDisplay();
119 if (!display)
120 return m_videoModes;
121
122 auto * res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
123 auto * output = GetOutput(res, display, m_screen);
124
125 if (!output)
126 {
127 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to find an output that matches '%1'")
128 .arg(m_screen->name()));
129 XRRFreeScreenResources(res);
130 delete display;
131 return m_videoModes;
132 }
133
134 auto mmwidth = static_cast<int>(output->mm_width);
135 auto mmheight = static_cast<int>(output->mm_height);
136 m_crtc = output->crtc;
137
138 DisplayModeMap screenmap;
139 for (int i = 0; i < output->nmode; ++i)
140 {
141 RRMode rrmode = output->modes[i];
142 for (int j = 0; j < res->nmode; ++j)
143 {
144 if (res->modes[j].id != rrmode)
145 continue;
146
147 auto mode = res->modes[j];
148 if (mode.id != rrmode)
149 continue;
150 if (mode.dotClock <= 1 || mode.vTotal <= 1 || mode.hTotal <= 1)
151 continue;
152 auto width = static_cast<int>(mode.width);
153 auto height = static_cast<int>(mode.height);
154 auto rate = static_cast<double>(mode.dotClock) / (mode.vTotal * mode.hTotal);
155
156 // TODO don't filter out interlaced modes but ignore them in MythDisplayMode
157 // when not required. This may then be used in future to allow 'exact' match
158 // display modes to display interlaced material on interlaced displays
159 if ((mode.modeFlags & RR_Interlace) != 0U)
160 {
161 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Ignoring interlaced mode %1x%2 %3i")
162 .arg(width).arg(height).arg(rate * 2.0, 2, 'f', 2, '0'));
163 continue;
164 }
165
166 QSize resolution(width, height);
167 QSize physical(mmwidth, mmheight);
168 auto key = MythDisplayMode::CalcKey(resolution, 0.0);
169 if (!screenmap.contains(key))
170 screenmap[key] = MythDisplayMode(resolution, physical, -1.0, rate);
171 else
172 screenmap[key].AddRefreshRate(rate);
173 m_modeMap.insert(MythDisplayMode::CalcKey(resolution, rate), rrmode);
174 }
175 }
176
177 for (auto & it : screenmap)
178 m_videoModes.push_back(it.second);
179
180 DebugModes();
181 XRRFreeOutputInfo(output);
182 XRRFreeScreenResources(res);
183 delete display;
184 return m_videoModes;
185}
186
187bool MythDisplayX11::SwitchToVideoMode(QSize Size, double DesiredRate)
188{
189 if (!m_crtc)
190 {
191 (void)GetVideoModes();
192 if (!m_crtc)
193 return false;
194 }
195
196 auto rate = static_cast<double>(NAN);
197 QSize dummy(0, 0);
198 MythDisplayMode desired(Size, dummy, -1.0, DesiredRate);
199 int idx = MythDisplayMode::FindBestMatch(m_videoModes, desired, rate);
200
201 if (idx < 0)
202 {
203 LOG(VB_GENERAL, LOG_ERR, LOC + "Desired resolution and frame rate not found.");
204 return false;
205 }
206
207 auto mode = MythDisplayMode::CalcKey(Size, rate);
208 if (!m_modeMap.contains(mode))
209 {
210 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find mode");
211 return false;
212 }
213
214 auto * display = MythXDisplay::OpenMythXDisplay();
215 if (!display)
216 return false;
217
218 Status status = RRSetConfigFailed;
219 auto * res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
220 if (res)
221 {
222 auto * currentcrtc = XRRGetCrtcInfo(display->GetDisplay(), res, m_crtc);
223 if (currentcrtc)
224 {
225 status = XRRSetCrtcConfig(display->GetDisplay(), res, m_crtc, CurrentTime,
226 currentcrtc->x, currentcrtc->y, m_modeMap.value(mode),
227 currentcrtc->rotation, currentcrtc->outputs,
228 currentcrtc->noutput);
229 XRRFreeCrtcInfo(currentcrtc);
230 auto * config = XRRGetScreenInfo(display->GetDisplay(), display->GetRoot());
231 if (config)
232 XRRFreeScreenConfigInfo(config);
233 }
234 XRRFreeScreenResources(res);
235 }
236 delete display;
237
238 if (RRSetConfigSuccess != status)
239 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set video mode");
240 return RRSetConfigSuccess == status;
241}
242
243static XRROutputInfo* GetOutput(XRRScreenResources* Resources,
244 MythXDisplay* mDisplay,
245 QScreen* qScreen, RROutput* Output)
246{
247 if (!(Resources && mDisplay && qScreen))
248 return nullptr;
249
250 XRROutputInfo* result = nullptr;
251 for (int i = 0; i < Resources->noutput; ++i)
252 {
253 if (result)
254 {
255 XRRFreeOutputInfo(result);
256 result = nullptr;
257 }
258
259 result = XRRGetOutputInfo(mDisplay->GetDisplay(), Resources, Resources->outputs[i]);
260 if (!result || result->nameLen < 1)
261 continue;
262 if (result->connection != RR_Connected)
263 {
264 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Output '%1' is disconnected")
265 .arg(result->name));
266 continue;
267 }
268
269 QString name(result->name);
270 if (name == qScreen->name())
271 {
272 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched '%1' to output %2")
273 .arg(qScreen->name()).arg(Resources->outputs[i]));
274 if (Output)
275 *Output = Resources->outputs[i];
276 return result;
277 }
278 }
279 XRRFreeOutputInfo(result);
280 return nullptr;
281}
282
284{
285 if (!mDisplay)
286 {
287 m_edid = MythEDID();
288 return;
289 }
290
291 auto * res = XRRGetScreenResourcesCurrent(mDisplay->GetDisplay(), mDisplay->GetRoot());
292 RROutput rroutput = 0;
293 auto * output = GetOutput(res, mDisplay, m_screen, &rroutput);
294
295 while (rroutput)
296 {
297 auto edidproperty = XInternAtom(mDisplay->GetDisplay(), RR_PROPERTY_RANDR_EDID,
298 static_cast<Bool>(false));
299 if (!edidproperty)
300 break;
301
302 int propertycount = 0;
303 auto * properties = XRRListOutputProperties(mDisplay->GetDisplay(), rroutput, &propertycount);
304 if (!properties)
305 break;
306
307 bool found = false;
308 for (int i = 0; i < propertycount; ++i)
309 {
310 if (properties[i] == edidproperty)
311 {
312 found = true;
313 break;
314 }
315 }
316 XFree(properties);
317 if (!found)
318 break;
319
320 Atom actualtype = 0;
321 int actualformat = 0;
322 unsigned long bytesafter = 0;
323 unsigned long nitems = 0;
324 unsigned char* data = nullptr;
325 if (XRRGetOutputProperty(mDisplay->GetDisplay(), rroutput, edidproperty,
326 0, 128, static_cast<Bool>(false), static_cast<Bool>(false),
327 AnyPropertyType, &actualtype,
328 &actualformat, &nitems, &bytesafter, &data) == Success)
329 {
330 if (actualtype == XA_INTEGER && actualformat == 8)
331 m_edid = MythEDID(reinterpret_cast<const char*>(data), static_cast<int>(nitems));
332 if (data)
333 {
334 XFree(data);
335 }
336 }
337 break;
338 }
339 XRRFreeOutputInfo(output);
340 XRRFreeScreenResources(res);
341}
bool GetBoolSetting(const QString &key, bool defaultval=false)
static int FindBestMatch(const MythDisplayModes &Modes, const MythDisplayMode &Mode, double &TargetRate)
static uint64_t CalcKey(QSize Size, double Rate)
static bool IsAvailable()
bool SwitchToVideoMode(QSize Size, double DesiredRate) override
bool UsingVideoModes() override
const MythDisplayModes & GetVideoModes() override
QMap< uint64_t, unsigned long > m_modeMap
unsigned long m_crtc
void UpdateCurrentMode() override
Retrieve details for the current video mode.
MythEDID & GetEDID()
QSize m_resolution
Definition: mythdisplay.h:92
virtual void UpdateCurrentMode()
Retrieve screen details.
QSize m_physicalSize
Definition: mythdisplay.h:93
void Initialise()
double m_refreshRate
Definition: mythdisplay.h:90
MythDisplayModes m_videoModes
Definition: mythdisplay.h:98
QScreen * m_screen
Definition: mythdisplay.h:97
MythEDID m_edid
Definition: mythdisplay.h:94
bool m_modeComplete
Definition: mythdisplay.h:89
void DebugModes() const
Window GetRoot() const
Definition: mythxdisplay.h:32
Display * GetDisplay()
Definition: mythxdisplay.h:28
bool Open()
Open the display.
static MythXDisplay * OpenMythXDisplay(bool Warn=true)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
std::map< uint64_t, MythDisplayMode > DisplayModeMap
std::vector< MythDisplayMode > MythDisplayModes
#define LOC
static XRROutputInfo * GetOutput(XRRScreenResources *Resources, MythXDisplay *mDisplay, QScreen *qScreen, RROutput *Output=nullptr)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define output
QList< Resource * > Resources