Ticket #13100: 0001-Handle-non-integer-refresh-rates-in-XRandr.patch

File 0001-Handle-non-integer-refresh-rates-in-XRandr.patch, 11.4 KB (added by halovanic@…, 2 years ago)

Patch attempt #1

  • mythtv/libs/libmythui/DisplayResX.cpp

    From d0e5521d63a7c5ad9cf20e4a68766131e2e5cca7 Mon Sep 17 00:00:00 2001
    From: Alex Halovanic <halovanic@gmail.com>
    Date: Sun, 13 Aug 2017 21:14:50 -0400
    Subject: [PATCH] Handle non-integer refresh rates in XRandr
    
    ---
     mythtv/libs/libmythui/DisplayResX.cpp | 224 +++++++++++++++++++---------------
     1 file changed, 125 insertions(+), 99 deletions(-)
    
    diff --git a/mythtv/libs/libmythui/DisplayResX.cpp b/mythtv/libs/libmythui/DisplayResX.cpp
    index b5e9fd90cd..f40d548f90 100644
    a b  
    1212
    1313#include <X11/extensions/Xrandr.h> // this has to be after util-x11.h (Qt bug)
    1414
    15 static XRRScreenConfiguration *GetScreenConfig(MythXDisplay*& display);
     15static XRRScreenResources *GetScreenResources(MythXDisplay*& display);
     16static XRROutputInfo *GetOutputInfo(MythXDisplay*& display, XRRScreenResources*& rsc);
     17RRCrtc crtc;
     18/* Width x Height x Resolution = X mode ID. */
     19std::map<std::tuple<int, int, double>, RRMode> modeIdMatch;
    1620
    1721DisplayResX::DisplayResX(void)
    1822{
    bool DisplayResX::SwitchToVideoMode(int width, int height, double desired_rate) 
    4953
    5054    if (idx >= 0)
    5155    {
    52         short finalrate;
    53         MythXDisplay *display = NULL;
    54         XRRScreenConfiguration *cfg = GetScreenConfig(display);
    55 
    56         if (!cfg)
    57             return false;
    58 
    59         Rotation rot;
    60 
    61         XRRConfigCurrentConfiguration(cfg, &rot);
    62 
    63         // Search real xrandr rate for desired_rate
    64         finalrate = (short) rate;
    65 
    66         for (uint i = 0; i < m_videoModes.size(); i++)
     56        RRMode desiredMode;
     57        std::tuple<int, int, double> widthHeightRate = std::make_tuple(
     58            m_videoModesUnsorted[idx].Width(),
     59            m_videoModesUnsorted[idx].Height(),
     60            rate);
     61        std::map<std::tuple<int, int, double>, RRMode>::iterator modeIdIter = modeIdMatch.find(widthHeightRate);
     62        if (modeIdIter != modeIdMatch.end())
    6763        {
    68             if ((m_videoModes[i].Width() == width) &&
    69                     (m_videoModes[i].Height() == height))
    70             {
    71                 if (m_videoModes[i].Custom())
    72                 {
    73                     finalrate = m_videoModes[i].realRates[rate];
    74                     LOG(VB_PLAYBACK, LOG_INFO,
    75                         QString("Dynamic TwinView rate found, set %1Hz as "
    76                                 "XRandR %2") .arg(rate) .arg(finalrate));
    77                 }
    78 
    79                 break;
    80             }
     64            desiredMode = modeIdIter->second;
     65        }
     66        else
     67        {
     68            LOG(VB_GENERAL, LOG_ERR, "Desired modeline for Resolution and FrameRate not found.");
     69            return false;
    8170        }
    8271
    83         Window root = display->GetRoot();
     72        MythXDisplay *display = NULL;
    8473
    85         Status status = XRRSetScreenConfigAndRate(display->GetDisplay(), cfg,
    86                         root, idx, rot, finalrate,
    87                         CurrentTime);
     74        XRRScreenResources *rsc = GetScreenResources(display);
     75        if (!rsc)
     76            return false;
    8877
    89         XRRFreeScreenConfigInfo(cfg);
     78        XRRCrtcInfo *origCrtcInfo = XRRGetCrtcInfo(display->GetDisplay(), rsc, crtc);
     79        Status status = XRRSetCrtcConfig(
     80            display->GetDisplay(),
     81            rsc,
     82            crtc,
     83            CurrentTime,
     84            origCrtcInfo->x,
     85            origCrtcInfo->y,
     86            desiredMode,
     87            origCrtcInfo->rotation,
     88            origCrtcInfo->outputs,
     89            origCrtcInfo->noutput);
     90
     91        XRRFreeCrtcInfo(origCrtcInfo);
     92        XRRFreeScreenResources(rsc);
    9093
    9194        // Force refresh of xf86VidMode current modeline
    92         cfg = XRRGetScreenInfo(display->GetDisplay(), root);
     95        XRRScreenConfiguration *cfg = XRRGetScreenInfo(display->GetDisplay(), display->GetRoot());
    9396        if (cfg)
    9497        {
    9598            XRRFreeScreenConfigInfo(cfg);
    bool DisplayResX::SwitchToVideoMode(int width, int height, double desired_rate) 
    99102
    100103        if (RRSetConfigSuccess != status)
    101104            LOG(VB_GENERAL, LOG_ERR,
    102                 "XRRSetScreenConfigAndRate() call failed.");
     105                "XRRSetCrtcConfig() call failed.");
    103106
    104107        return RRSetConfigSuccess == status;
    105108    }
    const DisplayResVector& DisplayResX::GetVideoModes(void) const 
    116119
    117120    MythXDisplay *display = NULL;
    118121
    119     XRRScreenConfiguration *cfg = GetScreenConfig(display);
    120 
    121     if (!cfg)
     122    /* All screen resources for all monitors. */
     123    XRRScreenResources *rsc = GetScreenResources(display);
     124    if (!rsc)
    122125        return m_videoModes;
    123126
    124     int num_sizes, num_rates;
    125 
    126     XRRScreenSize *sizes = NULL;
    127 
    128     sizes = XRRConfigSizes(cfg, &num_sizes);
     127    /* The primary monitor alone. */
     128    XRROutputInfo *output = GetOutputInfo(display, rsc);
     129    /* Needed to set the modeline later. */
     130    crtc = output->crtc;
     131    modeIdMatch.clear();
    129132
    130     for (int i = 0; i < num_sizes; ++i)
     133    /* Sort these out by screen size */
     134    std::map<std::pair<int, int>, std::vector<XRRModeInfo>> modesBySize;
     135    for (int i = 0; i < output->nmode; i++)
    131136    {
    132         short *rates = NULL;
    133         rates = XRRRates(display->GetDisplay(), display->GetScreen(),
    134                          i, &num_rates);
    135         DisplayResScreen scr(sizes[i].width, sizes[i].height,
    136                              sizes[i].mwidth, sizes[i].mheight,
    137                              rates, num_rates);
    138         m_videoModes.push_back(scr);
    139     }
     137        /* ID of a valid mode for this monitor. */
     138        RRMode id = output->modes[i];
     139        XRRModeInfo mode;
     140        bool found = false;
     141        for (int j=0; j < rsc->nmode; j++)
     142        {
     143            mode = rsc->modes[j];
     144            if(id == mode.id)
     145            {
     146                found = true;
     147                break;
     148            }
     149        }
    140150
    141     t_screenrate screenmap;
     151        /* Shouldn't happen if XRandr is consistent. */
     152        if (!found)
     153        {
     154            continue;
     155        }
    142156
    143     int nvidiarate = GetNvidiaRates(screenmap);
     157        std::pair<int, int> widthHeight = std::make_pair(mode.width, mode.height);
     158        std::map<std::pair<int, int>, std::vector<XRRModeInfo>>::iterator heightWidthIter = modesBySize.find(widthHeight);
     159        if (heightWidthIter == modesBySize.end())
     160        {
     161            std::vector<XRRModeInfo> modesForSize;
     162            modesForSize.push_back(mode);
     163            modesBySize[widthHeight] = modesForSize;
     164        }
     165        else
     166        {
     167            heightWidthIter->second.push_back(mode);
     168        }
     169    }
    144170
    145     if (nvidiarate > 0)
     171    /* Generate the screen sizes with a list of refresh rates for each. */
     172    map<std::pair<int, int>, std::vector<XRRModeInfo>>::iterator screenSizesIter = modesBySize.begin();
     173    for (screenSizesIter = modesBySize.begin(); screenSizesIter != modesBySize.end(); ++screenSizesIter)
    146174    {
    147         // Update existing DisplayResScreen vector, and update it with
    148         // new frequencies
    149         for (uint i = 0; i < m_videoModes.size(); i++)
     175        std::pair<int, int> widthHeight = screenSizesIter->first;
     176        std::vector<XRRModeInfo> widthHeightModes = screenSizesIter->second;
     177        std::vector<double> rates;
     178        for (uint i = 0; i < widthHeightModes.size(); i++)
    150179        {
    151             DisplayResScreen scr = m_videoModes[i];
    152             int w = scr.Width();
    153             int h = scr.Height();
    154             int mw = scr.Width_mm();
    155             int mh = scr.Height_mm();
    156             std::vector<double> newrates;
    157             std::map<double, short> realRates;
    158             const std::vector<double>& rates = scr.RefreshRates();
    159             bool found = false;
    160 
    161             for (std::vector<double>::const_iterator it = rates.begin();
    162                     it !=  rates.end(); ++it)
     180            XRRModeInfo widthHeightMode = widthHeightModes[i];
     181            double vTotal = widthHeightMode.vTotal;
     182            if (widthHeightMode.modeFlags & RR_DoubleScan)
    163183            {
    164                 uint64_t key = DisplayResScreen::CalcKey(w, h, *it);
    165 
    166                 if (screenmap.find(key) != screenmap.end())
    167                 {
    168                     // Rate is defined in NV-CONTROL extension, use it
    169                     newrates.push_back(screenmap[key]);
    170                     realRates[screenmap[key]] = (int) round(*it);
    171                     found = true;
    172 #if 1
    173                     LOG(VB_PLAYBACK, LOG_DEBUG,
    174                         QString("CustomRate Found, set %1x%2@%3 as %4Hz")
    175                         .arg(w) .arg(h) .arg(*it) .arg(screenmap[key]));
    176 #endif
    177                 }
     184                vTotal *= 2;
    178185            }
    179186
    180             if (found)
     187            if (widthHeightMode.modeFlags & RR_Interlace)
    181188            {
    182                 m_videoModes.erase(m_videoModes.begin() + i);
    183                 std::sort(newrates.begin(), newrates.end());
    184                 m_videoModes.insert(m_videoModes.begin() + i,
    185                                     DisplayResScreen(w, h, mw, mh, newrates,
    186                                                      realRates));
     189                vTotal /= 2;
     190            }
     191
     192            /* Some "auto" modes may not have such info. */
     193            if (widthHeightMode.hTotal && vTotal)
     194            {
     195                double rate = ((double) widthHeightMode.dotClock /
     196                    ((double) widthHeightMode.hTotal * (double) vTotal));
     197                rates.push_back(rate);
     198                std::tuple<int, int, double> widthHeightRate = std::make_tuple(widthHeight.first, widthHeight.second, rate);
     199                modeIdMatch[widthHeightRate] = widthHeightMode.id;
    187200            }
    188201        }
     202
     203        DisplayResScreen scr(widthHeight.first, widthHeight.second,
     204                             output->mm_width, output->mm_height,
     205                             rates);
     206        m_videoModes.push_back(scr);
    189207    }
    190208
    191209    m_videoModesUnsorted = m_videoModes;
    192210
    193211    std::sort(m_videoModes.begin(), m_videoModes.end());
    194     XRRFreeScreenConfigInfo(cfg);
     212    XRRFreeOutputInfo(output);
     213    XRRFreeScreenResources(rsc);
    195214    delete display;
    196215
    197216    return m_videoModes;
    198217}
    199218
    200 static XRRScreenConfiguration *GetScreenConfig(MythXDisplay*& display)
     219static XRRScreenResources *GetScreenResources(MythXDisplay*& display)
    201220{
    202221    display = OpenMythXDisplay();
    203222
    204223    if (!display)
    205224    {
    206         LOG(VB_GENERAL, LOG_ERR, "DisplaResX: MythXOpenDisplay call failed");
     225        LOG(VB_GENERAL, LOG_ERR, "DisplayResX: MythXOpenDisplay call failed");
    207226        return NULL;
    208227    }
    209228
    210229    Window root = RootWindow(display->GetDisplay(), display->GetScreen());
    211230
    212     XRRScreenConfiguration *cfg = NULL;
     231    XRRScreenResources *rsc = NULL;
    213232    int event_basep = 0, error_basep = 0;
    214233
    215234    if (XRRQueryExtension(display->GetDisplay(), &event_basep, &error_basep))
    216         cfg = XRRGetScreenInfo(display->GetDisplay(), root);
     235        rsc = XRRGetScreenResourcesCurrent(display->GetDisplay(), root);
    217236
    218     if (!cfg)
     237    if (!rsc)
    219238    {
    220239        delete display;
    221240        display = NULL;
    222         LOG(VB_GENERAL, LOG_ERR, "DisplaResX: Unable to XRRgetScreenInfo");
     241        LOG(VB_GENERAL, LOG_ERR, "DisplayResX: Unable to XRRGetScreenResourcesCurrent");
    223242    }
    224243
    225     return cfg;
     244    return rsc;
     245}
     246
     247static XRROutputInfo *GetOutputInfo(MythXDisplay*& display, XRRScreenResources*& rsc)
     248{
     249    Window root = RootWindow(display->GetDisplay(), display->GetScreen());
     250    RROutput primary = XRRGetOutputPrimary(display->GetDisplay(), root);
     251    return XRRGetOutputInfo(display->GetDisplay(), rsc, primary);
    226252}