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