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