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 1/2] 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
|
|
12 | 12 | |
13 | 13 | #include <X11/extensions/Xrandr.h> // this has to be after util-x11.h (Qt bug) |
14 | 14 | |
15 | | static XRRScreenConfiguration *GetScreenConfig(MythXDisplay*& display); |
| 15 | static XRRScreenResources *GetScreenResources(MythXDisplay*& display); |
| 16 | static XRROutputInfo *GetOutputInfo(MythXDisplay*& display, XRRScreenResources*& rsc); |
| 17 | RRCrtc crtc; |
| 18 | /* Width x Height x Resolution = X mode ID. */ |
| 19 | std::map<std::tuple<int, int, double>, RRMode> modeIdMatch; |
16 | 20 | |
17 | 21 | DisplayResX::DisplayResX(void) |
18 | 22 | { |
… |
… |
bool DisplayResX::SwitchToVideoMode(int width, int height, double desired_rate) |
49 | 53 | |
50 | 54 | if (idx >= 0) |
51 | 55 | { |
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()) |
67 | 63 | { |
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; |
81 | 70 | } |
82 | 71 | |
83 | | Window root = display->GetRoot(); |
| 72 | MythXDisplay *display = NULL; |
84 | 73 | |
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; |
88 | 77 | |
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); |
90 | 93 | |
91 | 94 | // Force refresh of xf86VidMode current modeline |
92 | | cfg = XRRGetScreenInfo(display->GetDisplay(), root); |
| 95 | XRRScreenConfiguration *cfg = XRRGetScreenInfo(display->GetDisplay(), display->GetRoot()); |
93 | 96 | if (cfg) |
94 | 97 | { |
95 | 98 | XRRFreeScreenConfigInfo(cfg); |
… |
… |
bool DisplayResX::SwitchToVideoMode(int width, int height, double desired_rate) |
99 | 102 | |
100 | 103 | if (RRSetConfigSuccess != status) |
101 | 104 | LOG(VB_GENERAL, LOG_ERR, |
102 | | "XRRSetScreenConfigAndRate() call failed."); |
| 105 | "XRRSetCrtcConfig() call failed."); |
103 | 106 | |
104 | 107 | return RRSetConfigSuccess == status; |
105 | 108 | } |
… |
… |
const DisplayResVector& DisplayResX::GetVideoModes(void) const |
116 | 119 | |
117 | 120 | MythXDisplay *display = NULL; |
118 | 121 | |
119 | | XRRScreenConfiguration *cfg = GetScreenConfig(display); |
120 | | |
121 | | if (!cfg) |
| 122 | /* All screen resources for all monitors. */ |
| 123 | XRRScreenResources *rsc = GetScreenResources(display); |
| 124 | if (!rsc) |
122 | 125 | return m_videoModes; |
123 | 126 | |
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(); |
129 | 132 | |
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++) |
131 | 136 | { |
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 | } |
140 | 150 | |
141 | | t_screenrate screenmap; |
| 151 | /* Shouldn't happen if XRandr is consistent. */ |
| 152 | if (!found) |
| 153 | { |
| 154 | continue; |
| 155 | } |
142 | 156 | |
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 | } |
144 | 170 | |
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) |
146 | 174 | { |
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++) |
150 | 179 | { |
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) |
163 | 183 | { |
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; |
178 | 185 | } |
179 | 186 | |
180 | | if (found) |
| 187 | if (widthHeightMode.modeFlags & RR_Interlace) |
181 | 188 | { |
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; |
187 | 200 | } |
188 | 201 | } |
| 202 | |
| 203 | DisplayResScreen scr(widthHeight.first, widthHeight.second, |
| 204 | output->mm_width, output->mm_height, |
| 205 | rates); |
| 206 | m_videoModes.push_back(scr); |
189 | 207 | } |
190 | 208 | |
191 | 209 | m_videoModesUnsorted = m_videoModes; |
192 | 210 | |
193 | 211 | std::sort(m_videoModes.begin(), m_videoModes.end()); |
194 | | XRRFreeScreenConfigInfo(cfg); |
| 212 | XRRFreeOutputInfo(output); |
| 213 | XRRFreeScreenResources(rsc); |
195 | 214 | delete display; |
196 | 215 | |
197 | 216 | return m_videoModes; |
198 | 217 | } |
199 | 218 | |
200 | | static XRRScreenConfiguration *GetScreenConfig(MythXDisplay*& display) |
| 219 | static XRRScreenResources *GetScreenResources(MythXDisplay*& display) |
201 | 220 | { |
202 | 221 | display = OpenMythXDisplay(); |
203 | 222 | |
204 | 223 | if (!display) |
205 | 224 | { |
206 | | LOG(VB_GENERAL, LOG_ERR, "DisplaResX: MythXOpenDisplay call failed"); |
| 225 | LOG(VB_GENERAL, LOG_ERR, "DisplayResX: MythXOpenDisplay call failed"); |
207 | 226 | return NULL; |
208 | 227 | } |
209 | 228 | |
210 | 229 | Window root = RootWindow(display->GetDisplay(), display->GetScreen()); |
211 | 230 | |
212 | | XRRScreenConfiguration *cfg = NULL; |
| 231 | XRRScreenResources *rsc = NULL; |
213 | 232 | int event_basep = 0, error_basep = 0; |
214 | 233 | |
215 | 234 | if (XRRQueryExtension(display->GetDisplay(), &event_basep, &error_basep)) |
216 | | cfg = XRRGetScreenInfo(display->GetDisplay(), root); |
| 235 | rsc = XRRGetScreenResourcesCurrent(display->GetDisplay(), root); |
217 | 236 | |
218 | | if (!cfg) |
| 237 | if (!rsc) |
219 | 238 | { |
220 | 239 | delete display; |
221 | 240 | display = NULL; |
222 | | LOG(VB_GENERAL, LOG_ERR, "DisplaResX: Unable to XRRgetScreenInfo"); |
| 241 | LOG(VB_GENERAL, LOG_ERR, "DisplayResX: Unable to XRRGetScreenResourcesCurrent"); |
223 | 242 | } |
224 | 243 | |
225 | | return cfg; |
| 244 | return rsc; |
| 245 | } |
| 246 | |
| 247 | static 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); |
226 | 252 | } |
--
2.11.0
From 50ee71aaab90242689c5e9331bc686326a46f8ac Mon Sep 17 00:00:00 2001
From: Alex Halovanic <halovanic@gmail.com>
Date: Mon, 14 Aug 2017 18:13:37 -0400
Subject: [PATCH 2/2] Handle scenarios without primary monitor
---
mythtv/libs/libmythui/DisplayResX.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/mythtv/libs/libmythui/DisplayResX.cpp b/mythtv/libs/libmythui/DisplayResX.cpp
index f40d548f90..b2c8728eb3 100644
a
|
b
|
const DisplayResVector& DisplayResX::GetVideoModes(void) const |
126 | 126 | |
127 | 127 | /* The primary monitor alone. */ |
128 | 128 | XRROutputInfo *output = GetOutputInfo(display, rsc); |
| 129 | if (!output) |
| 130 | return m_videoModes; |
| 131 | |
129 | 132 | /* Needed to set the modeline later. */ |
130 | 133 | crtc = output->crtc; |
131 | 134 | modeIdMatch.clear(); |
… |
… |
static XRROutputInfo *GetOutputInfo(MythXDisplay*& display, XRRScreenResources*& |
248 | 251 | { |
249 | 252 | Window root = RootWindow(display->GetDisplay(), display->GetScreen()); |
250 | 253 | RROutput primary = XRRGetOutputPrimary(display->GetDisplay(), root); |
| 254 | if (primary == 0 && rsc->noutput > 0) { |
| 255 | /* If primary is not set (and it usually is not for single displays), |
| 256 | * pick the first monitor. */ |
| 257 | primary = rsc->outputs[0]; |
| 258 | } |
| 259 | |
251 | 260 | return XRRGetOutputInfo(display->GetDisplay(), rsc, primary); |
252 | 261 | } |