MythTV  master
mythdisplayx11.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "config.h"
3 #include "mythcorecontext.h"
4 #include "mythlogging.h"
5 #include "mythdisplayx11.h"
6 
7 // X11
8 #include <X11/Xatom.h>
9 
10 #define LOC QString("DisplayX11: ")
11 
13  : MythDisplay()
14 {
15  Initialise();
16 }
17 
19 {
20  static bool s_checked = false;
21  static bool s_available = false;
22  if (!s_checked)
23  {
24  s_checked = true;
25  MythXDisplay display;
26  s_available = display.Open();
27  }
28  return s_available;
29 }
30 
45 {
46  // Get some Qt basics first
48 
50  if (display)
51  {
52 #ifdef USING_XRANDR
53  // XRANDR should always be accurate
54  GetEDID(display);
55  XRRScreenResources* res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
56  XRROutputInfo *output = GetOutput(res, display, m_screen);
57  if (output)
58  {
59  m_physicalSize = QSize(static_cast<int>(output->mm_width),
60  static_cast<int>(output->mm_height));
61  XRRFreeOutputInfo(output);
62  }
63 
64  if (!m_crtc)
65  (void)GetVideoModes();
66  while (m_crtc && res)
67  {
68  XRRCrtcInfo *currentcrtc = XRRGetCrtcInfo(display->GetDisplay(), res, m_crtc);
69  if (!currentcrtc)
70  break;
71  for (int i = 0; i < res->nmode; ++i)
72  {
73  if (res->modes[i].id != currentcrtc->mode)
74  continue;
75  XRRModeInfo mode = res->modes[i];
76  m_resolution = QSize(static_cast<int>(mode.width),
77  static_cast<int>(mode.height));
78  if (mode.dotClock > 1 && mode.vTotal > 1 && mode.hTotal > 1)
79  {
80  m_refreshRate = static_cast<double>(mode.dotClock) / (mode.vTotal * mode.hTotal);
81  if (mode.modeFlags & RR_Interlace)
82  m_refreshRate *= 2.0;
83  }
84  }
85  XRRFreeCrtcInfo(currentcrtc);
86  break;
87  }
88  XRRFreeScreenResources(res);
89 #else
90  // Use Qt version apart from refresh rate
91  m_refreshRate = display->GetRefreshRate();
92 #endif
93  delete display;
94  m_modeComplete = true;
95  return;
96  }
97 }
98 
99 #ifdef USING_XRANDR
101 {
102  if (gCoreContext)
103  return gCoreContext->GetBoolSetting("UseVideoModes", false);
104  return false;
105 }
106 
107 const std::vector<MythDisplayMode>& MythDisplayX11::GetVideoModes(void)
108 {
109  if (!m_videoModes.empty() || !m_screen)
110  return m_videoModes;
111 
112  m_videoModes.clear();
113  m_modeMap.clear();
114 
116  if (!display)
117  return m_videoModes;
118 
119  XRRScreenResources* res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
120  XRROutputInfo *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  int mmwidth = static_cast<int>(output->mm_width);
132  int 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  XRRModeInfo 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  int width = static_cast<int>(mode.width);
150  int height = static_cast<int>(mode.height);
151  double rate = static_cast<double>(mode.dotClock) / (mode.vTotal * mode.hTotal);
152  bool interlaced = mode.modeFlags & RR_Interlace;
153  if (interlaced)
154  rate *= 2.0;
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 (interlaced)
160  {
161  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Ignoring interlaced mode %1x%2 %3i")
162  .arg(width).arg(height).arg(rate, 2, 'f', 2, '0'));
163  continue;
164  }
165 
166  QSize resolution(width, height);
167  QSize physical(mmwidth, mmheight);
168  uint64_t key = MythDisplayMode::CalcKey(resolution, 0.0);
169  if (screenmap.find(key) == screenmap.end())
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.begin(); screenmap.end() != it; ++it)
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 
187 bool MythDisplayX11::SwitchToVideoMode(QSize Size, double DesiredRate)
188 {
189  if (!m_crtc)
190  (void)GetVideoModes();
191  if (!m_crtc)
192  return false;
193 
194  auto rate = static_cast<double>(NAN);
195  QSize dummy(0, 0);
196  MythDisplayMode desired(Size, dummy, -1.0, DesiredRate);
197  int idx = MythDisplayMode::FindBestMatch(m_videoModes, desired, rate);
198 
199  if (idx < 0)
200  {
201  LOG(VB_GENERAL, LOG_ERR, LOC + "Desired resolution and frame rate not found.");
202  return false;
203  }
204 
205  auto mode = MythDisplayMode::CalcKey(Size, rate);
206  if (!m_modeMap.contains(mode))
207  {
208  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find mode");
209  return false;
210  }
211 
213  if (!display)
214  return false;
215 
216  Status status = RRSetConfigFailed;
217  XRRScreenResources* res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot());
218  if (res)
219  {
220  XRRCrtcInfo *currentcrtc = XRRGetCrtcInfo(display->GetDisplay(), res, m_crtc);
221  if (currentcrtc)
222  {
223  status = XRRSetCrtcConfig(display->GetDisplay(), res, m_crtc, CurrentTime,
224  currentcrtc->x, currentcrtc->y, m_modeMap.value(mode),
225  currentcrtc->rotation, currentcrtc->outputs,
226  currentcrtc->noutput);
227  XRRFreeCrtcInfo(currentcrtc);
228  XRRScreenConfiguration *config = XRRGetScreenInfo(display->GetDisplay(), display->GetRoot());
229  if (config)
230  XRRFreeScreenConfigInfo(config);
231  }
232  XRRFreeScreenResources(res);
233  }
234  delete display;
235 
236  if (RRSetConfigSuccess != status)
237  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set video mode");
238  return RRSetConfigSuccess == status;
239 }
240 
241 XRROutputInfo* MythDisplayX11::GetOutput(XRRScreenResources* Resources,
242  MythXDisplay* mDisplay,
243  QScreen* qScreen, RROutput *Output)
244 {
245  if (!(Resources && mDisplay && qScreen))
246  return nullptr;
247 
248  XRROutputInfo *result = nullptr;
249  for (int i = 0; i < Resources->noutput; ++i)
250  {
251  if (result)
252  {
253  XRRFreeOutputInfo(result);
254  result = nullptr;
255  }
256 
257  result = XRRGetOutputInfo(mDisplay->GetDisplay(), Resources, Resources->outputs[i]);
258  if (!result || result->nameLen < 1)
259  continue;
260  if (result->connection != RR_Connected)
261  {
262  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Output '%1' is disconnected")
263  .arg(result->name));
264  continue;
265  }
266 
267  QString name(result->name);
268  if (name == qScreen->name())
269  {
270  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched '%1' to output %2")
271  .arg(qScreen->name()).arg(Resources->outputs[i]));
272  if (Output)
273  *Output = Resources->outputs[i];
274  return result;
275  }
276  }
277  XRRFreeOutputInfo(result);
278  return nullptr;
279 }
280 #endif // USING_XRANDR
281 
283 {
284 #ifdef USING_XRANDR
285  if (!mDisplay)
286  {
287  m_edid = MythEDID();
288  return;
289  }
290 
291  XRRScreenResources* res = XRRGetScreenResourcesCurrent(mDisplay->GetDisplay(), mDisplay->GetRoot());
292  RROutput rroutput = 0;
293  XRROutputInfo *output = GetOutput(res, mDisplay, m_screen, &rroutput);
294 
295  while (rroutput)
296  {
297  Atom edidproperty = XInternAtom(mDisplay->GetDisplay(), RR_PROPERTY_RANDR_EDID, false);
298  if (!edidproperty)
299  break;
300 
301  int propertycount = 0;
302  Atom* properties = XRRListOutputProperties(mDisplay->GetDisplay(), rroutput, &propertycount);
303  if (!properties)
304  break;
305 
306  bool found = false;
307  for (int i = 0; i < propertycount; ++i)
308  {
309  if (properties[i] == edidproperty)
310  {
311  found = true;
312  break;
313  }
314  }
315  XFree(properties);
316  if (!found)
317  break;
318 
319  Atom actualtype = 0;
320  int actualformat = 0;;
321  unsigned long bytesafter = 0;
322  unsigned long nitems = 0;
323  unsigned char* data = nullptr;
324  if (XRRGetOutputProperty(mDisplay->GetDisplay(), rroutput, edidproperty,
325  0, 128, false, false, AnyPropertyType, &actualtype,
326  &actualformat, &nitems, &bytesafter, &data) == Success)
327  {
328  if (actualtype == XA_INTEGER && actualformat == 8)
329  m_edid = MythEDID(reinterpret_cast<const char*>(data),
330  static_cast<int>(nitems));
331  }
332  break;
333  }
334  XRRFreeOutputInfo(output);
335  XRRFreeScreenResources(res);
336 #else
337  (void)mDisplay;
338  m_edid = MythEDID();
339 #endif
340 }
double GetRefreshRate(void)
Display * GetDisplay(void)
Definition: mythxdisplay.h:28
virtual void UpdateCurrentMode(void)
Retrieve screen details.
QScreen * m_screen
Definition: mythdisplay.h:91
map< uint64_t, MythDisplayMode > DisplayModeMap
vector< MythDisplayMode > m_videoModes
Definition: mythdisplay.h:92
static uint64_t CalcKey(QSize Size, double Rate)
MythEDID m_edid
Definition: mythdisplay.h:88
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static MythXDisplay * OpenMythXDisplay(bool Warn=true)
QSize m_resolution
Definition: mythdisplay.h:86
#define LOC
void Initialise(void)
QList< Resource * > Resources
bool Open(void)
bool m_modeComplete
Definition: mythdisplay.h:83
MythEDID & GetEDID(void)
void AddRefreshRate(double Rate)
static int FindBestMatch(const vector< MythDisplayMode > &Modes, const MythDisplayMode &Mode, double &TargetRate)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool UsingVideoModes(void) override
bool GetBoolSetting(const QString &key, bool defaultval=false)
const std::vector< MythDisplayMode > & GetVideoModes(void) override
void UpdateCurrentMode(void) override final
Retrieve details for the current video mode.
QMap< uint64_t, unsigned long > m_modeMap
void DebugModes(void) const
static XRROutputInfo * GetOutput(XRRScreenResources *Resources, MythXDisplay *mDisplay, QScreen *qScreen, RROutput *Output=nullptr)
unsigned long m_crtc
QSize m_physicalSize
Definition: mythdisplay.h:87
bool SwitchToVideoMode(QSize Size, double DesiredRate) override
double m_refreshRate
Definition: mythdisplay.h:84
static bool IsAvailable(void)
Window GetRoot(void) const
Definition: mythxdisplay.h:34
#define output