00001
00002 #include "util-nvctrl.h"
00003
00004 #include "mythlogging.h"
00005 #include "mythdb.h"
00006
00007 #include <stdio.h>
00008 #include <string.h>
00009 #include <ctype.h>
00010 #include <cmath>
00011 #include <sstream>
00012 #include <locale>
00013
00014 #include "mythxdisplay.h"
00015
00016 #include "libmythnvctrl/NVCtrl.h"
00017 #include "libmythnvctrl/NVCtrlLib.h"
00018
00019 #ifdef USING_XRANDR
00020 #include "DisplayResX.h"
00021 #endif
00022
00023 #ifdef USING_XRANDR
00024 static unsigned int display_device_mask(char *str);
00025 static void parse_mode_string(char *modeString, char **modeName, int *mask);
00026 static char *find_modeline(char *modeString, char *pModeLines,
00027 int ModeLineLen);
00028 static int extract_id_string(char *str);
00029 static int modeline_is_interlaced(char *modeLine);
00030 #endif
00031
00032 #define LOC QString("NVCtrl: ")
00033
00034 int CheckNVOpenGLSyncToVBlank(void)
00035 {
00036 MythXDisplay *d = OpenMythXDisplay();
00037 if (!d)
00038 return -1;
00039
00040 Display *dpy = d->GetDisplay();
00041 int screen = d->GetScreen();
00042
00043 if (!XNVCTRLIsNvScreen(dpy, screen))
00044 {
00045 delete d;
00046 return -1;
00047 }
00048
00049 int major, minor;
00050 if (!XNVCTRLQueryVersion(dpy, &major, &minor))
00051 return -1;
00052
00053 int sync = NV_CTRL_SYNC_TO_VBLANK_OFF;
00054 if (!XNVCTRLQueryAttribute(dpy, screen, 0, NV_CTRL_SYNC_TO_VBLANK, &sync))
00055 {
00056 delete d;
00057 return -1;
00058 }
00059
00060 if (!sync)
00061 {
00062 LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL Sync to VBlank is disabled.");
00063 LOG(VB_GENERAL, LOG_WARNING, LOC + "For best results enable this in NVidia settings or try running:");
00064 LOG(VB_GENERAL, LOG_WARNING, LOC + "nvidia-settings -a \"SyncToVBlank=1\"");
00065 }
00066
00067 if (!sync && getenv("__GL_SYNC_TO_VBLANK"))
00068 {
00069 LOG(VB_GENERAL, LOG_INFO, LOC +
00070 "OpenGL Sync to VBlank enabled via __GL_SYNC_TO_VBLANK.");
00071 sync = 1;
00072 }
00073 else if (!sync)
00074 {
00075 LOG(VB_GENERAL, LOG_WARNING, LOC +
00076 "Alternatively try setting the '__GL_SYNC_TO_VBLANK' environment variable.");
00077 }
00078
00079 return sync;
00080 }
00081
00082 int GetNvidiaRates(t_screenrate& screenmap)
00083 {
00084 #ifdef USING_XRANDR
00085
00086 MythXDisplay *d = OpenMythXDisplay();
00087 if (!d)
00088 {
00089 return -1;
00090 }
00091 Display *dpy;
00092 bool ret;
00093 int screen, display_devices, mask, major, minor, len, j;
00094 char *str, *start;
00095 int nDisplayDevice;
00096
00097 char *pMetaModes, *pModeLines[8], *tmp, *modeString;
00098 char *modeLine, *modeName;
00099 int MetaModeLen, ModeLineLen[8];
00100 int thisMask;
00101 int id;
00102 int twinview = 0;
00103 map<int, map<int,bool> > maprate;
00104
00105 memset(pModeLines, 0, sizeof(pModeLines));
00106 memset(ModeLineLen, 0, sizeof(ModeLineLen));
00107
00108
00109
00110
00111
00112
00113 dpy = d->GetDisplay();
00114 screen = d->GetScreen();
00115
00116 if (!XNVCTRLIsNvScreen(dpy, screen))
00117 {
00118 LOG(VB_PLAYBACK, LOG_INFO,
00119 QString("The NV-CONTROL X extension is not available on screen %1 "
00120 "of '%2'.")
00121 .arg(screen) .arg(XDisplayName(NULL)));
00122 delete d;
00123 return -1;
00124 }
00125
00126 ret = XNVCTRLQueryVersion(dpy, &major, &minor);
00127 if (ret != True)
00128 {
00129 LOG(VB_PLAYBACK, LOG_INFO,
00130 QString("The NV-CONTROL X extension does not exist on '%1'.")
00131 .arg(XDisplayName(NULL)));
00132 delete d;
00133 return -1;
00134 }
00135
00136 ret = XNVCTRLQueryAttribute(dpy, screen, 0, NV_CTRL_DYNAMIC_TWINVIEW, &twinview);
00137
00138 if (!ret)
00139 {
00140 LOG(VB_PLAYBACK, LOG_ERR,
00141 "Failed to query if Dynamic Twinview is enabled");
00142 XCloseDisplay(dpy);
00143 return -1;
00144 }
00145 if (!twinview)
00146 {
00147 LOG(VB_PLAYBACK, LOG_ERR, "Dynamic Twinview not enabled, ignoring");
00148 delete d;
00149 return 0;
00150 }
00151
00152
00153
00154
00155
00156
00157 ret = XNVCTRLQueryAttribute(dpy, screen, 0,
00158 NV_CTRL_CONNECTED_DISPLAYS, &display_devices);
00159
00160 if (!ret)
00161 {
00162 LOG(VB_PLAYBACK, LOG_ERR,
00163 "Failed to query the enabled Display Devices.");
00164 delete d;
00165 return -1;
00166 }
00167
00168
00169
00170 ret = XNVCTRLQueryBinaryData(dpy, screen, 0,
00171 NV_CTRL_BINARY_DATA_METAMODES,
00172 (unsigned char **)&pMetaModes, &MetaModeLen);
00173 if (!ret)
00174 {
00175 LOG(VB_PLAYBACK, LOG_ERR,
00176 "Failed to query the metamode on selected display device.");
00177 delete d;
00178 return -1;
00179 }
00180
00181
00182
00183
00184
00185
00186 nDisplayDevice = 0;
00187
00188 for (mask = 1; mask < (1 << 24); mask <<= 1)
00189 {
00190 if (!(display_devices & mask)) continue;
00191
00192 ret = XNVCTRLQueryBinaryData(dpy, screen, mask,
00193 NV_CTRL_BINARY_DATA_MODELINES,
00194 (unsigned char **)&str, &len);
00195 if (!ret)
00196 {
00197 LOG(VB_PLAYBACK, LOG_ERR,
00198 "Unknown error. Failed to query the enabled Display Devices.");
00199
00200 for (j=0; j < nDisplayDevice; ++j)
00201 {
00202 free(pModeLines[j]);
00203 }
00204 delete d;
00205 return -1;
00206 }
00207
00208 pModeLines[nDisplayDevice] = str;
00209 ModeLineLen[nDisplayDevice] = len;
00210
00211 nDisplayDevice++;
00212 }
00213
00214
00215 str = start = pMetaModes;
00216
00217 for (j = 0; j < MetaModeLen - 1; ++j)
00218 {
00219
00220
00221
00222
00223
00224 if ((str[j] == '\0') && (str[j+1] != '\0'))
00225 {
00226 id = extract_id_string(start);
00227
00228
00229
00230
00231
00232
00233 tmp = strstr(start, "::");
00234 if (tmp)
00235 {
00236 tmp += 2;
00237 }
00238 else
00239 {
00240 tmp = start;
00241 }
00242
00243
00244
00245 char *strtok_state = NULL;
00246 for (modeString = strtok_r(tmp, ",", &strtok_state);
00247 modeString;
00248 modeString = strtok_r(NULL, ",", &strtok_state))
00249 {
00250
00251
00252
00253
00254
00255 parse_mode_string(modeString, &modeName, &thisMask);
00256
00257
00258 nDisplayDevice = 0;
00259 if (thisMask)
00260 {
00261 for (mask = 1; mask < (1 << 24); mask <<= 1)
00262 {
00263 if (!(display_devices & mask)) continue;
00264 if (thisMask & mask) break;
00265 nDisplayDevice++;
00266 }
00267 }
00268
00269 modeLine = find_modeline(modeName,
00270 pModeLines[nDisplayDevice],
00271 ModeLineLen[nDisplayDevice]);
00272
00273 if (modeLine && !modeline_is_interlaced(modeLine))
00274 {
00275 int w, h, vfl, hfl, i, irate;
00276 double dcl, r;
00277 char *buf[256];
00278 uint64_t key, key2;
00279
00280
00281 tmp = strchr(modeLine, '"');
00282 tmp = strchr(tmp+1, '"') +1 ;
00283 while (*tmp == ' ')
00284 tmp++;
00285 i = 0;
00286 for (modeString = strtok_r(tmp, " ", &strtok_state);
00287 modeString;
00288 modeString = strtok_r(NULL, " ", &strtok_state))
00289 {
00290 buf[i++] = modeString;
00291 }
00292 w = strtol(buf[1], NULL, 10);
00293 h = strtol(buf[5], NULL, 10);
00294 vfl = strtol(buf[8], NULL, 10);
00295 hfl = strtol(buf[4], NULL, 10);
00296 h = strtol(buf[5], NULL, 10);
00297 istringstream istr(buf[0]);
00298 istr.imbue(locale("C"));
00299 istr >> dcl;
00300 r = (dcl * 1000000.0) / (vfl * hfl);
00301 irate = (int) round(r * 1000.0);
00302 key = DisplayResScreen::CalcKey(w, h, (double) id);
00303 key2 = DisplayResScreen::CalcKey(w, h, 0.0);
00304
00305 if (maprate.find(key2) == maprate.end())
00306 {
00307
00308 maprate[key2] = map<int, bool>();
00309 }
00310 if ((maprate[key2].find(irate) == maprate[key2].end()) &&
00311 (screenmap.find(key) == screenmap.end()))
00312 {
00313 screenmap[key] = r;
00314 maprate[key2][irate] = true;
00315 }
00316 }
00317 free(modeName);
00318 }
00319
00320
00321 start = &str[j+1];
00322 }
00323 }
00324
00325 for (j=0; j < nDisplayDevice; ++j)
00326 {
00327 free(pModeLines[j]);
00328 }
00329
00330 delete d;
00331 return 1;
00332 #else // USING_XRANDR
00333 return -1;
00334 #endif
00335 }
00336
00337 #ifdef USING_XRANDR
00338
00339
00340
00341
00342
00343 static unsigned int display_device_mask(char *str)
00344 {
00345 if (strcmp(str, "CRT-0") == 0) return (1 << 0);
00346 if (strcmp(str, "CRT-1") == 0) return (1 << 1);
00347 if (strcmp(str, "CRT-2") == 0) return (1 << 2);
00348 if (strcmp(str, "CRT-3") == 0) return (1 << 3);
00349 if (strcmp(str, "CRT-4") == 0) return (1 << 4);
00350 if (strcmp(str, "CRT-5") == 0) return (1 << 5);
00351 if (strcmp(str, "CRT-6") == 0) return (1 << 6);
00352 if (strcmp(str, "CRT-7") == 0) return (1 << 7);
00353
00354 if (strcmp(str, "TV-0") == 0) return (1 << 8);
00355 if (strcmp(str, "TV-1") == 0) return (1 << 9);
00356 if (strcmp(str, "TV-2") == 0) return (1 << 10);
00357 if (strcmp(str, "TV-3") == 0) return (1 << 11);
00358 if (strcmp(str, "TV-4") == 0) return (1 << 12);
00359 if (strcmp(str, "TV-5") == 0) return (1 << 13);
00360 if (strcmp(str, "TV-6") == 0) return (1 << 14);
00361 if (strcmp(str, "TV-7") == 0) return (1 << 15);
00362
00363 if (strcmp(str, "DFP-0") == 0) return (1 << 16);
00364 if (strcmp(str, "DFP-1") == 0) return (1 << 17);
00365 if (strcmp(str, "DFP-2") == 0) return (1 << 18);
00366 if (strcmp(str, "DFP-3") == 0) return (1 << 19);
00367 if (strcmp(str, "DFP-4") == 0) return (1 << 20);
00368 if (strcmp(str, "DFP-5") == 0) return (1 << 21);
00369 if (strcmp(str, "DFP-6") == 0) return (1 << 22);
00370 if (strcmp(str, "DFP-7") == 0) return (1 << 23);
00371
00372 return 0;
00373
00374 }
00375
00376
00377
00378
00379
00380
00381
00382
00383 static void parse_mode_string(char *modeString, char **modeName, int *mask)
00384 {
00385 char *colon, *s_end, tmp;
00386
00387
00388 while (*modeString == ' ')
00389 {
00390 modeString++;
00391 }
00392 colon = strchr(modeString, ':');
00393
00394 if (colon)
00395 {
00396 *colon = '\0';
00397 *mask = display_device_mask(modeString);
00398 *colon = ':';
00399 modeString = colon + 1;
00400 }
00401 else
00402 {
00403 *mask = 0;
00404 }
00405
00406
00407 while (*modeString == ' ')
00408 {
00409 modeString++;
00410 }
00411
00412
00413
00414
00415
00416 s_end = strchr(modeString, '\0');
00417
00418 for (char *s = modeString; *s; s++)
00419 {
00420 if (*s == ' ' && *(s+1) == '@')
00421 s_end = s;
00422 }
00423
00424 tmp = *s_end;
00425 *s_end = '\0';
00426 *modeName = strdup(modeString);
00427 *s_end = tmp;
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437 static char *find_modeline(char *modeName, char *pModeLines, int ModeLineLen)
00438 {
00439 char *start, *beginQuote, *endQuote;
00440 int j, match = 0;
00441
00442 start = pModeLines;
00443
00444 for (j = 0; j < ModeLineLen; j++)
00445 {
00446 if (pModeLines[j] == '\0')
00447 {
00448
00449
00450
00451
00452
00453
00454 beginQuote = strchr(start, '"');
00455 endQuote = beginQuote ? strchr(beginQuote+1, '"') : NULL;
00456
00457 if (beginQuote && endQuote)
00458 {
00459 *endQuote = '\0';
00460 match = (strcmp(modeName, beginQuote+1) == 0);
00461 *endQuote = '"';
00462
00463
00464
00465
00466
00467
00468 if (match)
00469 {
00470 char *tmp = strstr(start, "::");
00471 if (tmp)
00472 {
00473 tmp += 2;
00474 }
00475 else
00476 {
00477 tmp = start;
00478 }
00479 return tmp;
00480 }
00481 }
00482 start = &pModeLines[j+1];
00483 }
00484 }
00485
00486 return NULL;
00487
00488 }
00489
00490
00491
00492
00493 static int extract_id_string(char *str)
00494 {
00495 char *begin, *end;
00496 int id;
00497
00498 begin = strstr(str, "id=");
00499 if (!begin)
00500 {
00501 return -1;
00502 }
00503 if (!(begin = end = strdup(begin + 3)))
00504 {
00505 return -1;
00506 }
00507 while (isdigit(*end))
00508 {
00509 end++;
00510 }
00511 *end = '\0';
00512 id = atoi(begin);
00513 free(begin);
00514 return id;
00515 }
00516
00517
00518
00519
00520 static int modeline_is_interlaced(char *modeLine)
00521 {
00522 return (strstr(modeLine, "Interlace") != NULL);
00523 }
00524
00525 #endif