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