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, minor;
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  Display *dpy;
97  bool ret;
98  int screen, display_devices, mask, major, minor, len, j;
99  char *str, *start;
100  int nDisplayDevice;
101 
102  char *pMetaModes, *pModeLines[8], *tmp, *modeString;
103  char *modeLine, *modeName;
104  int MetaModeLen, ModeLineLen[8];
105  int thisMask;
106  int id;
107  int twinview = 0;
108  map<int, map<int,bool> > maprate;
109 
110  memset(pModeLines, 0, sizeof(pModeLines));
111  memset(ModeLineLen, 0, sizeof(ModeLineLen));
112 
113  /*
114  * Open a display connection, and make sure the NV-CONTROL X
115  * extension is present on the screen we want to use.
116  */
117 
118  dpy = d->GetDisplay();
119  screen = d->GetScreen();
120 
121  if (!XNVCTRLIsNvScreen(dpy, screen))
122  {
123  LOG(VB_GUI, LOG_INFO,
124  QString("The NV-CONTROL X extension is not available on screen %1 "
125  "of '%2'.")
126  .arg(screen) .arg(XDisplayName(nullptr)));
127  delete d;
128  return -1;
129  }
130 
131  ret = (XNVCTRLQueryVersion(dpy, &major, &minor) != 0);
132  if (!ret)
133  {
134  LOG(VB_GUI, LOG_INFO,
135  QString("The NV-CONTROL X extension does not exist on '%1'.")
136  .arg(XDisplayName(nullptr)));
137  delete d;
138  return -1;
139  }
140 
141  ret = (XNVCTRLQueryAttribute(dpy, screen, 0, NV_CTRL_DYNAMIC_TWINVIEW, &twinview) != 0);
142 
143  if (!ret)
144  {
145  LOG(VB_GUI, LOG_ERR,
146  "Failed to query if Dynamic Twinview is enabled");
147  XCloseDisplay(dpy);
148  return -1;
149  }
150  if (!twinview)
151  {
152  LOG(VB_GUI, LOG_ERR, "Dynamic Twinview not enabled, ignoring");
153  delete d;
154  return 0;
155  }
156 
157  /*
158  * query the connected display devices on this X screen and print
159  * basic information about each X screen
160  */
161 
162  ret = (XNVCTRLQueryAttribute(dpy, screen, 0,
163  NV_CTRL_CONNECTED_DISPLAYS, &display_devices) != 0);
164 
165  if (!ret)
166  {
167  LOG(VB_GUI, LOG_ERR,
168  "Failed to query the enabled Display Devices.");
169  delete d;
170  return -1;
171  }
172 
173  /* first, we query the MetaModes on this X screen */
174 
175  ret = (XNVCTRLQueryBinaryData(dpy, screen, 0, // n/a
176  NV_CTRL_BINARY_DATA_METAMODES,
177  (unsigned char **)&pMetaModes, &MetaModeLen) != 0);
178  if (!ret)
179  {
180  LOG(VB_GUI, LOG_ERR,
181  "Failed to query the metamode on selected display device.");
182  delete d;
183  return -1;
184  }
185 
186  /*
187  * then, we query the ModeLines for each display device on
188  * this X screen; we'll need these later
189  */
190 
191  nDisplayDevice = 0;
192 
193  for (mask = 1; mask < (1 << 24); mask <<= 1)
194  {
195  if (!(display_devices & mask)) continue;
196 
197  ret = (XNVCTRLQueryBinaryData(dpy, screen, mask,
198  NV_CTRL_BINARY_DATA_MODELINES,
199  (unsigned char **)&str, &len) != 0);
200  if (!ret)
201  {
202  LOG(VB_GUI, LOG_ERR,
203  "Unknown error. Failed to query the enabled Display Devices.");
204  // Free Memory currently allocated
205  for (j=0; j < nDisplayDevice; ++j)
206  {
207  free(pModeLines[j]);
208  }
209  delete d;
210  return -1;
211  }
212 
213  pModeLines[nDisplayDevice] = str;
214  ModeLineLen[nDisplayDevice] = len;
215 
216  nDisplayDevice++;
217  }
218 
219  /* now, parse each MetaMode */
220  str = start = pMetaModes;
221 
222  for (j = 0; j < MetaModeLen - 1; ++j)
223  {
224  /*
225  * if we found the end of a line, treat the string from
226  * start to str[j] as a MetaMode
227  */
228 
229  if ((str[j] == '\0') && (str[j+1] != '\0'))
230  {
231  id = extract_id_string(start);
232  /*
233  * the MetaMode may be preceded with "token=value"
234  * pairs, separated by the main MetaMode with "::"; if
235  * "::" exists in the string, skip past it
236  */
237 
238  tmp = strstr(start, "::");
239  if (tmp)
240  {
241  tmp += 2;
242  }
243  else
244  {
245  tmp = start;
246  }
247 
248  /* split the MetaMode string by comma */
249 
250  char *strtok_state = nullptr;
251  for (modeString = strtok_r(tmp, ",", &strtok_state);
252  modeString;
253  modeString = strtok_r(nullptr, ",", &strtok_state))
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 (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  modeLine = find_modeline(modeName,
275  pModeLines[nDisplayDevice],
276  ModeLineLen[nDisplayDevice]);
277 
278  if (modeLine && !modeline_is_interlaced(modeLine))
279  {
280  int w, h, vfl, hfl, i, irate;
281  double dcl, r;
282  char *buf[9] = {nullptr};
283  uint64_t key, key2;
284 
285  // skip name
286  tmp = strchr(modeLine, '"');
287  tmp = strchr(tmp+1, '"') +1 ;
288  while (*tmp == ' ')
289  tmp++;
290  i = 0;
291  for (modeString = strtok_r(tmp, " ", &strtok_state);
292  modeString;
293  modeString = strtok_r(nullptr, " ", &strtok_state))
294  {
295  buf[i++] = modeString;
296 
297  if (i == 9) // We're only interested in the first 9 tokens [0 ... 8]
298  break;
299  }
300 
301  if (i < 9)
302  {
303  LOG(VB_GUI, LOG_WARNING, QString("Invalid modeline string "
304  "received, skipping: %1").arg(modeLine));
305  continue;
306  }
307 
308  w = strtol(buf[1], nullptr, 10);
309  h = strtol(buf[5], nullptr, 10);
310  vfl = strtol(buf[8], nullptr, 10);
311  hfl = strtol(buf[4], nullptr, 10);
312  istringstream istr(buf[0]);
313  istr.imbue(locale("C"));
314  istr >> dcl;
315  r = (dcl * 1000000.0) / (vfl * hfl);
316  irate = (int) round(r * 1000.0);
317  key = DisplayResScreen::CalcKey(w, h, (double) id);
318  key2 = DisplayResScreen::CalcKey(w, h, 0.0);
319  // We need to eliminate duplicates, giving priority to the first entries found
320  if (maprate.find(key2) == maprate.end())
321  {
322  // First time we see this resolution, create a map for it
323  maprate[key2] = map<int, bool>();
324  }
325  if ((maprate[key2].find(irate) == maprate[key2].end()) &&
326  (screenmap.find(key) == screenmap.end()))
327  {
328  screenmap[key] = r;
329  maprate[key2][irate] = true;
330  }
331  }
332  free(modeName);
333  }
334 
335  /* move to the next MetaMode */
336  start = &str[j+1];
337  }
338  }
339  // Free Memory
340  for (j=0; j < nDisplayDevice; ++j)
341  {
342  free(pModeLines[j]);
343  }
344 
345  delete d;
346  return 1;
347 #else // USING_XRANDR
348  return -1;
349 #endif
350 }
351 
352 #ifdef USING_XRANDR
353 /*
354  * display_device_mask() - given a display device name, translate to
355  * the display device mask
356  */
357 
358 static unsigned int display_device_mask(char *str)
359 {
360  if (strcmp(str, "CRT-0") == 0) return (1 << 0);
361  if (strcmp(str, "CRT-1") == 0) return (1 << 1);
362  if (strcmp(str, "CRT-2") == 0) return (1 << 2);
363  if (strcmp(str, "CRT-3") == 0) return (1 << 3);
364  if (strcmp(str, "CRT-4") == 0) return (1 << 4);
365  if (strcmp(str, "CRT-5") == 0) return (1 << 5);
366  if (strcmp(str, "CRT-6") == 0) return (1 << 6);
367  if (strcmp(str, "CRT-7") == 0) return (1 << 7);
368 
369  if (strcmp(str, "TV-0") == 0) return (1 << 8);
370  if (strcmp(str, "TV-1") == 0) return (1 << 9);
371  if (strcmp(str, "TV-2") == 0) return (1 << 10);
372  if (strcmp(str, "TV-3") == 0) return (1 << 11);
373  if (strcmp(str, "TV-4") == 0) return (1 << 12);
374  if (strcmp(str, "TV-5") == 0) return (1 << 13);
375  if (strcmp(str, "TV-6") == 0) return (1 << 14);
376  if (strcmp(str, "TV-7") == 0) return (1 << 15);
377 
378  if (strcmp(str, "DFP-0") == 0) return (1 << 16);
379  if (strcmp(str, "DFP-1") == 0) return (1 << 17);
380  if (strcmp(str, "DFP-2") == 0) return (1 << 18);
381  if (strcmp(str, "DFP-3") == 0) return (1 << 19);
382  if (strcmp(str, "DFP-4") == 0) return (1 << 20);
383  if (strcmp(str, "DFP-5") == 0) return (1 << 21);
384  if (strcmp(str, "DFP-6") == 0) return (1 << 22);
385  if (strcmp(str, "DFP-7") == 0) return (1 << 23);
386 
387  return 0;
388 
389 }
390 
391 
392 
393 /*
394  * parse_mode_string() - extract the modeName and the display device
395  * mask for the per-display device MetaMode string in 'modeString'
396  */
397 
398 static void parse_mode_string(char *modeString, char **modeName, int *mask)
399 {
400  char *colon, *s_end, tmp;
401 
402  // Skip space
403  while (*modeString == ' ')
404  {
405  modeString++;
406  }
407  colon = strchr(modeString, ':');
408 
409  if (colon)
410  {
411  *colon = '\0';
412  *mask = display_device_mask(modeString);
413  *colon = ':';
414  modeString = colon + 1;
415  }
416  else
417  {
418  *mask = 0;
419  }
420 
421  // Skip space
422  while (*modeString == ' ')
423  {
424  modeString++;
425  }
426 
427  /*
428  * find the modename; stop at the last ' @' (would be easier with regex)
429  */
430 
431  s_end = strchr(modeString, '\0');
432 
433  for (char *s = modeString; *s; s++)
434  {
435  if (*s == ' ' && *(s+1) == '@')
436  s_end = s;
437  }
438 
439  tmp = *s_end;
440  *s_end = '\0';
441  *modeName = strdup(modeString);
442  *s_end = tmp;
443 }
444 
445 
446 /*
447  * find_modeline() - search the pModeLines list of ModeLines for the
448  * mode named 'modeName'; return a pointer to the matching ModeLine,
449  * or nullptr if no match is found
450  */
451 
452 static char *find_modeline(char *modeName, char *pModeLines, int ModeLineLen)
453 {
454  char *start, *beginQuote, *endQuote;
455  int j;
456  bool match = false;
457 
458  start = pModeLines;
459 
460  for (j = 0; j < ModeLineLen; j++)
461  {
462  if (pModeLines[j] == '\0')
463  {
464  /*
465  * the modeline will contain the modeName in quotes; find
466  * the begin and end of the quoted modeName, so that we
467  * can compare it to modeName
468  */
469 
470  beginQuote = strchr(start, '"');
471  endQuote = beginQuote ? strchr(beginQuote+1, '"') : nullptr;
472 
473  if (beginQuote && endQuote)
474  {
475  *endQuote = '\0';
476  match = (strcmp(modeName, beginQuote+1) == 0);
477  *endQuote = '"';
478 
479  /*
480  * if we found a match, return a pointer to the start
481  * of this modeLine
482  */
483 
484  if (match)
485  {
486  char *tmp = strstr(start, "::");
487  if (tmp)
488  {
489  tmp += 2;
490  }
491  else
492  {
493  tmp = start;
494  }
495  return tmp;
496  }
497  }
498  start = &pModeLines[j+1];
499  }
500  }
501 
502  return nullptr;
503 
504 }
505 
506 /*
507  * extract_id_string() - Extract the xrandr twinview id from modestr and return it as an int.
508  */
509 static int extract_id_string(char *str)
510 {
511  char *begin, *end;
512  int id;
513 
514  begin = strstr(str, "id=");
515  if (!begin)
516  {
517  return -1;
518  }
519  if (!(begin = end = strdup(begin + 3)))
520  {
521  return -1;
522  }
523  while (isdigit(*end))
524  {
525  end++;
526  }
527  *end = '\0';
528  id = atoi(begin);
529  free(begin);
530  return id;
531 }
532 
533 /*
534  * modeline_is_interlaced() - return true if the Modeline is Interlaced, false otherwise.
535  */
536 static bool modeline_is_interlaced(char *modeLine)
537 {
538  return (strstr(modeLine, "Interlace") != nullptr);
539 }
540 
541 #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