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