MythTV  master
util-nvctrl.cpp
Go to the documentation of this file.
1 #include "config.h"
2 #include "util-nvctrl.h"
3 
4 #include "mythlogging.h"
5 #include "mythdb.h"
6 
7 #include <cctype>
8 #include <cmath>
9 #include <cstdio>
10 #include <cstring>
11 #include <locale>
12 #include <sstream>
13 
14 #include "mythxdisplay.h"
15 
16 #if CONFIG_XNVCTRL_EXTERNAL
17 #include "NVCtrl/NVCtrl.h"
18 #include "NVCtrl/NVCtrlLib.h"
19 #else
20 #include "NVCtrl.h"
21 #include "NVCtrlLib.h"
22 #endif
23 
24 #ifdef USING_XRANDR
25 #include "DisplayResX.h"
26 #endif
27 
28 #ifdef USING_XRANDR
29 static unsigned int display_device_mask(char *str);
30 static void parse_mode_string(char *modeString, char **modeName, int *mask);
31 static char *find_modeline(char *modeName, char *pModeLines,
32  int ModeLineLen);
33 static int extract_id_string(char *str);
34 static bool modeline_is_interlaced(char *modeLine);
35 #endif
36 
37 #define LOC QString("NVCtrl: ")
38 
40 {
42  if (!d)
43  return -1;
44 
45  Display *dpy = d->GetDisplay();
46  int screen = d->GetScreen();
47 
48  if (!XNVCTRLIsNvScreen(dpy, screen))
49  {
50  delete d;
51  return -1;
52  }
53 
54  int major = 0, minor = 0;
55  if (!XNVCTRLQueryVersion(dpy, &major, &minor))
56  return -1;
57 
58  int sync = NV_CTRL_SYNC_TO_VBLANK_OFF;
59  if (!XNVCTRLQueryAttribute(dpy, screen, 0, NV_CTRL_SYNC_TO_VBLANK, &sync))
60  {
61  delete d;
62  return -1;
63  }
64 
65  if (!sync)
66  {
67  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL Sync to VBlank is disabled.");
68  LOG(VB_GENERAL, LOG_WARNING, LOC + "For best results enable this in NVidia settings or try running:");
69  LOG(VB_GENERAL, LOG_WARNING, LOC + "nvidia-settings -a \"SyncToVBlank=1\"");
70  }
71 
72  if (!sync && getenv("__GL_SYNC_TO_VBLANK"))
73  {
74  LOG(VB_GENERAL, LOG_INFO, LOC +
75  "OpenGL Sync to VBlank enabled via __GL_SYNC_TO_VBLANK.");
76  sync = 1;
77  }
78  else if (!sync)
79  {
80  LOG(VB_GENERAL, LOG_WARNING, LOC +
81  "Alternatively try setting the '__GL_SYNC_TO_VBLANK' environment variable.");
82  }
83 
84  return sync;
85 }
86 
88 {
89 #ifdef USING_XRANDR
90 
92  if (!d)
93  {
94  return -1;
95  }
96  int display_devices = 0, major = 0, minor = 0;
97 
98  char *pMetaModes = nullptr, *pModeLines[8], *tmp = nullptr;
99  int MetaModeLen = 0, ModeLineLen[8];
100  map<int, map<int,bool> > maprate;
101 
102  memset(pModeLines, 0, sizeof(pModeLines));
103  memset(ModeLineLen, 0, sizeof(ModeLineLen));
104 
105  /*
106  * Open a display connection, and make sure the NV-CONTROL X
107  * extension is present on the screen we want to use.
108  */
109 
110  Display *dpy = d->GetDisplay();
111  int screen = d->GetScreen();
112 
113  if (!XNVCTRLIsNvScreen(dpy, screen))
114  {
115  LOG(VB_GUI, LOG_INFO,
116  QString("The NV-CONTROL X extension is not available on screen %1 "
117  "of '%2'.")
118  .arg(screen) .arg(XDisplayName(nullptr)));
119  delete d;
120  return -1;
121  }
122 
123  int ret = (XNVCTRLQueryVersion(dpy, &major, &minor) != 0);
124  if (!ret)
125  {
126  LOG(VB_GUI, LOG_INFO,
127  QString("The NV-CONTROL X extension does not exist on '%1'.")
128  .arg(XDisplayName(nullptr)));
129  delete d;
130  return -1;
131  }
132 
133  int twinview = 0;
134  ret = (XNVCTRLQueryAttribute(dpy, screen, 0, NV_CTRL_DYNAMIC_TWINVIEW, &twinview) != 0);
135 
136  if (!ret)
137  {
138  LOG(VB_GUI, LOG_ERR,
139  "Failed to query if Dynamic Twinview is enabled");
140  XCloseDisplay(dpy);
141  return -1;
142  }
143  if (!twinview)
144  {
145  LOG(VB_GUI, LOG_ERR, "Dynamic Twinview not enabled, ignoring");
146  delete d;
147  return 0;
148  }
149 
150  /*
151  * query the connected display devices on this X screen and print
152  * basic information about each X screen
153  */
154 
155  ret = (XNVCTRLQueryAttribute(dpy, screen, 0,
156  NV_CTRL_CONNECTED_DISPLAYS, &display_devices) != 0);
157 
158  if (!ret)
159  {
160  LOG(VB_GUI, LOG_ERR,
161  "Failed to query the enabled Display Devices.");
162  delete d;
163  return -1;
164  }
165 
166  /* first, we query the MetaModes on this X screen */
167 
168  ret = (XNVCTRLQueryBinaryData(dpy, screen, 0, // n/a
169  NV_CTRL_BINARY_DATA_METAMODES,
170  (unsigned char **)&pMetaModes, &MetaModeLen) != 0);
171  if (!ret)
172  {
173  LOG(VB_GUI, LOG_ERR,
174  "Failed to query the metamode on selected display device.");
175  delete d;
176  return -1;
177  }
178 
179  /*
180  * then, we query the ModeLines for each display device on
181  * this X screen; we'll need these later
182  */
183 
184  int nDisplayDevice = 0;
185 
186  for (int mask = 1; mask < (1 << 24); mask <<= 1)
187  {
188  int len = 0;
189 
190  if (!(display_devices & mask)) continue;
191 
192  char *str = nullptr;
193  ret = (XNVCTRLQueryBinaryData(dpy, screen, mask,
194  NV_CTRL_BINARY_DATA_MODELINES,
195  (unsigned char **)&str, &len) != 0);
196  if (!ret)
197  {
198  LOG(VB_GUI, LOG_ERR,
199  "Unknown error. Failed to query the enabled Display Devices.");
200  // Free Memory currently allocated
201  for (int j=0; j < nDisplayDevice; ++j)
202  {
203  free(pModeLines[j]);
204  }
205  delete d;
206  return -1;
207  }
208 
209  pModeLines[nDisplayDevice] = str;
210  ModeLineLen[nDisplayDevice] = len;
211 
212  nDisplayDevice++;
213  }
214 
215  /* now, parse each MetaMode */
216  char *str = pMetaModes;
217  char *start = pMetaModes;
218 
219  for (int j = 0; j < MetaModeLen - 1; ++j)
220  {
221  /*
222  * if we found the end of a line, treat the string from
223  * start to str[j] as a MetaMode
224  */
225 
226  if ((str[j] == '\0') && (str[j+1] != '\0'))
227  {
228  int id = extract_id_string(start);
229  /*
230  * the MetaMode may be preceded with "token=value"
231  * pairs, separated by the main MetaMode with "::"; if
232  * "::" exists in the string, skip past it
233  */
234 
235  tmp = strstr(start, "::");
236  if (tmp)
237  {
238  tmp += 2;
239  }
240  else
241  {
242  tmp = start;
243  }
244 
245  /* split the MetaMode string by comma */
246 
247  char *strtok_state = nullptr;
248  for (char *modeString = strtok_r(tmp, ",", &strtok_state);
249  modeString;
250  modeString = strtok_r(nullptr, ",", &strtok_state))
251  {
252  char *modeName = nullptr;
253  int thisMask = 0;
254 
255  /*
256  * retrieve the modeName and display device mask
257  * for this segment of the Metamode
258  */
259 
260  parse_mode_string(modeString, &modeName, &thisMask);
261 
262  /* lookup the modeline that matches */
263  nDisplayDevice = 0;
264  if (thisMask)
265  {
266  for (int mask = 1; mask < (1 << 24); mask <<= 1)
267  {
268  if (!(display_devices & mask)) continue;
269  if (thisMask & mask) break;
270  nDisplayDevice++;
271  }
272  }
273 
274  char *modeLine = find_modeline(modeName,
275  pModeLines[nDisplayDevice],
276  ModeLineLen[nDisplayDevice]);
277 
278  if (modeLine && !modeline_is_interlaced(modeLine))
279  {
280  char *buf[9] = {nullptr};
281 
282  // skip name
283  tmp = strchr(modeLine, '"');
284  tmp = strchr(tmp+1, '"') +1 ;
285  while (*tmp == ' ')
286  tmp++;
287  int i = 0;
288  for (char *modeString2 = strtok_r(tmp, " ", &strtok_state);
289  modeString2;
290  modeString2 = strtok_r(nullptr, " ", &strtok_state))
291  {
292  buf[i++] = modeString2;
293 
294  if (i == 9) // We're only interested in the first 9 tokens [0 ... 8]
295  break;
296  }
297 
298  if (i < 9)
299  {
300  LOG(VB_GUI, LOG_WARNING, QString("Invalid modeline string "
301  "received, skipping: %1").arg(modeLine));
302  continue;
303  }
304 
305  int w = strtol(buf[1], nullptr, 10);
306  int h = strtol(buf[5], nullptr, 10);
307  int vfl = strtol(buf[8], nullptr, 10);
308  int hfl = strtol(buf[4], nullptr, 10);
309  istringstream istr(buf[0]);
310  istr.imbue(locale("C"));
311  double dcl = NAN;
312  istr >> dcl;
313  double r = (dcl * 1000000.0) / (vfl * hfl);
314  int irate = (int) round(r * 1000.0);
315  uint64_t key = DisplayResScreen::CalcKey(w, h, (double) id);
316  uint64_t key2 = DisplayResScreen::CalcKey(w, h, 0.0);
317  // We need to eliminate duplicates, giving priority to the first entries found
318  if (maprate.find(key2) == maprate.end())
319  {
320  // First time we see this resolution, create a map for it
321  maprate[key2] = map<int, bool>();
322  }
323  if ((maprate[key2].find(irate) == maprate[key2].end()) &&
324  (screenmap.find(key) == screenmap.end()))
325  {
326  screenmap[key] = r;
327  maprate[key2][irate] = true;
328  }
329  }
330  free(modeName);
331  }
332 
333  /* move to the next MetaMode */
334  start = &str[j+1];
335  }
336  }
337  // Free Memory
338  for (int j=0; j < nDisplayDevice; ++j)
339  {
340  free(pModeLines[j]);
341  }
342 
343  delete d;
344  return 1;
345 #else // USING_XRANDR
346  return -1;
347 #endif
348 }
349 
350 #ifdef USING_XRANDR
351 /*
352  * display_device_mask() - given a display device name, translate to
353  * the display device mask
354  */
355 
356 static unsigned int display_device_mask(char *str)
357 {
358  if (strcmp(str, "CRT-0") == 0) return (1 << 0);
359  if (strcmp(str, "CRT-1") == 0) return (1 << 1);
360  if (strcmp(str, "CRT-2") == 0) return (1 << 2);
361  if (strcmp(str, "CRT-3") == 0) return (1 << 3);
362  if (strcmp(str, "CRT-4") == 0) return (1 << 4);
363  if (strcmp(str, "CRT-5") == 0) return (1 << 5);
364  if (strcmp(str, "CRT-6") == 0) return (1 << 6);
365  if (strcmp(str, "CRT-7") == 0) return (1 << 7);
366 
367  if (strcmp(str, "TV-0") == 0) return (1 << 8);
368  if (strcmp(str, "TV-1") == 0) return (1 << 9);
369  if (strcmp(str, "TV-2") == 0) return (1 << 10);
370  if (strcmp(str, "TV-3") == 0) return (1 << 11);
371  if (strcmp(str, "TV-4") == 0) return (1 << 12);
372  if (strcmp(str, "TV-5") == 0) return (1 << 13);
373  if (strcmp(str, "TV-6") == 0) return (1 << 14);
374  if (strcmp(str, "TV-7") == 0) return (1 << 15);
375 
376  if (strcmp(str, "DFP-0") == 0) return (1 << 16);
377  if (strcmp(str, "DFP-1") == 0) return (1 << 17);
378  if (strcmp(str, "DFP-2") == 0) return (1 << 18);
379  if (strcmp(str, "DFP-3") == 0) return (1 << 19);
380  if (strcmp(str, "DFP-4") == 0) return (1 << 20);
381  if (strcmp(str, "DFP-5") == 0) return (1 << 21);
382  if (strcmp(str, "DFP-6") == 0) return (1 << 22);
383  if (strcmp(str, "DFP-7") == 0) return (1 << 23);
384 
385  return 0;
386 
387 }
388 
389 
390 
391 /*
392  * parse_mode_string() - extract the modeName and the display device
393  * mask for the per-display device MetaMode string in 'modeString'
394  */
395 
396 static void parse_mode_string(char *modeString, char **modeName, int *mask)
397 {
398  // Skip space
399  while (*modeString == ' ')
400  {
401  modeString++;
402  }
403  char *colon = strchr(modeString, ':');
404 
405  if (colon)
406  {
407  *colon = '\0';
408  *mask = display_device_mask(modeString);
409  *colon = ':';
410  modeString = colon + 1;
411  }
412  else
413  {
414  *mask = 0;
415  }
416 
417  // Skip space
418  while (*modeString == ' ')
419  {
420  modeString++;
421  }
422 
423  /*
424  * find the modename; stop at the last ' @' (would be easier with regex)
425  */
426 
427  char *s_end = strchr(modeString, '\0');
428 
429  for (char *s = modeString; *s; s++)
430  {
431  if (*s == ' ' && *(s+1) == '@')
432  s_end = s;
433  }
434 
435  char tmp = *s_end;
436  *s_end = '\0';
437  *modeName = strdup(modeString);
438  *s_end = tmp;
439 }
440 
441 
442 /*
443  * find_modeline() - search the pModeLines list of ModeLines for the
444  * mode named 'modeName'; return a pointer to the matching ModeLine,
445  * or nullptr if no match is found
446  */
447 
448 static char *find_modeline(char *modeName, char *pModeLines, int ModeLineLen)
449 {
450  char *start = pModeLines;
451 
452  for (int j = 0; j < ModeLineLen; j++)
453  {
454  if (pModeLines[j] == '\0')
455  {
456  /*
457  * the modeline will contain the modeName in quotes; find
458  * the begin and end of the quoted modeName, so that we
459  * can compare it to modeName
460  */
461 
462  char *beginQuote = strchr(start, '"');
463  char *endQuote = beginQuote ? strchr(beginQuote+1, '"') : nullptr;
464 
465  if (beginQuote && endQuote)
466  {
467  *endQuote = '\0';
468  bool match = (strcmp(modeName, beginQuote+1) == 0);
469  *endQuote = '"';
470 
471  /*
472  * if we found a match, return a pointer to the start
473  * of this modeLine
474  */
475 
476  if (match)
477  {
478  char *tmp = strstr(start, "::");
479  if (tmp)
480  {
481  tmp += 2;
482  }
483  else
484  {
485  tmp = start;
486  }
487  return tmp;
488  }
489  }
490  start = &pModeLines[j+1];
491  }
492  }
493 
494  return nullptr;
495 
496 }
497 
498 /*
499  * extract_id_string() - Extract the xrandr twinview id from modestr and return it as an int.
500  */
501 static int extract_id_string(char *str)
502 {
503  char *end = nullptr;
504  char *begin = strstr(str, "id=");
505  if (!begin)
506  {
507  return -1;
508  }
509  if (!(begin = end = strdup(begin + 3)))
510  {
511  return -1;
512  }
513  while (isdigit(*end))
514  {
515  end++;
516  }
517  *end = '\0';
518  int id = atoi(begin);
519  free(begin);
520  return id;
521 }
522 
523 /*
524  * modeline_is_interlaced() - return true if the Modeline is Interlaced, false otherwise.
525  */
526 static bool modeline_is_interlaced(char *modeLine)
527 {
528  return (strstr(modeLine, "Interlace") != nullptr);
529 }
530 
531 #endif
MythXDisplay * OpenMythXDisplay(void)
static void parse_mode_string(char *modeString, char **modeName, int *mask)
#define round(x)
Definition: mythplayer.cpp:66
static int extract_id_string(char *str)
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
This file is intended to hold X11 specific utility functions.
Definition: mythxdisplay.h:16
static guint32 * tmp
Definition: goom_core.c:35
static char * find_modeline(char *modeName, char *pModeLines, int ModeLineLen)
unsigned char r
Definition: ParseText.cpp:329
static unsigned int display_device_mask(char *str)
static const uint16_t * d
int CheckNVOpenGLSyncToVBlank(void)
Definition: util-nvctrl.cpp:39
#define minor(X)
Definition: compat.h:138
map< unsigned int, double > t_screenrate
Definition: util-nvctrl.h:9
static bool modeline_is_interlaced(char *modeLine)
static uint64_t CalcKey(int w, int h, double rate)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define LOC
Definition: util-nvctrl.cpp:37
int GetNvidiaRates(t_screenrate &screenmap)
Definition: util-nvctrl.cpp:87