summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2018-12-31 15:29:13 +0100
committerSimon Rettberg2018-12-31 15:29:13 +0100
commit15426a68638be35c98af921e033bc825250678e3 (patch)
treea9e2ebfdaf5adc04695632b125f0a8452b4d0840
parentFix bug (diff)
downloadbeamergui-15426a68638be35c98af921e033bc825250678e3.tar.gz
beamergui-15426a68638be35c98af921e033bc825250678e3.tar.xz
beamergui-15426a68638be35c98af921e033bc825250678e3.zip
Parse EDID for missing modes (ultrawide issue)
For some reason X ignores ultrawide resolutions in the EDID data... Additionally (at least on LG models) the EDID data wrongly says 1080p would be the preferred resolution of the screen, so we now take the physical dimensions of the screen into account and override that decision, if applicable. This assumes square pixels, but that shouldn't be too crazy of an assumption.
-rw-r--r--src/xprivate.cpp222
-rw-r--r--src/xprivate.h5
-rw-r--r--src/xx.cpp12
-rw-r--r--src/xx.h1
4 files changed, 193 insertions, 47 deletions
diff --git a/src/xprivate.cpp b/src/xprivate.cpp
index 7667b9e..4be7098 100644
--- a/src/xprivate.cpp
+++ b/src/xprivate.cpp
@@ -37,6 +37,21 @@ static bool xRectLessThan(const OutputInfo* const &a, const OutputInfo* const &b
return a->crtc->x < b->crtc->x;
}
+static int modeEqual(const XRRModeInfo *a, const XRRModeInfo *b)
+{
+ return (a->width == b->width &&
+ a->height == b->height &&
+ a->dotClock / 10 == b->dotClock / 10 &&
+ a->hSyncStart == b->hSyncStart &&
+ a->hSyncEnd == b->hSyncEnd &&
+ a->hTotal == b->hTotal &&
+ a->hSkew == b->hSkew &&
+ a->vSyncStart == b->vSyncStart &&
+ a->vSyncEnd == b->vSyncEnd &&
+ a->vTotal == b->vTotal &&
+ a->modeFlags == b->modeFlags);
+}
+
/*
* Class members
*/
@@ -224,51 +239,172 @@ void XPrivate::updateScreenResources()
qDebug() << "Loaded.";
}
+void XPrivate::addMissingEdidResolutions()
+{
+ XRRScreenResources *resptr = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
+ for (int i = 0; i < resptr->noutput; ++i) {
+ unsigned char prop[512];
+ unsigned long edidLen = 512;
+ if (!getEdid(resptr->outputs[i], prop, &edidLen))
+ continue;
+ if (edidLen < 256)
+ continue;
+ for (unsigned int j = 128; j < edidLen - 127; j += 128) {
+ addMissingModesFromEdid(resptr, resptr->outputs[i], prop + j);
+ }
+ }
+ XRRFreeScreenResources(resptr);
+}
+
+bool XPrivate::getEdid(RROutput outputId, unsigned char *buffer, unsigned long *size)
+{
+ int numProps = 0;
+ bool found = false;
+ Atom* properties = XRRListOutputProperties(_display, outputId, &numProps);
+ for (int i = 0; i < numProps; ++i) {
+ if (properties[i] == _EDID_ATOM) {
+ found = true;
+ break;
+ }
+ }
+ XFree(properties);
+ if (!found)
+ return false;
+
+ unsigned long nitems, bytes_after;
+ unsigned char *prop = nullptr;
+ int actual_format;
+ Atom actual_type;
+ bool valid;
+
+ XRRGetOutputProperty(_display, outputId, _EDID_ATOM,
+ 0, long(size), False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop);
+ valid = (actual_format == 8 && nitems >= 128 && prop != nullptr);
+ if (valid) {
+ memcpy(buffer, prop, qMin(*size, nitems));
+ *size = qMin(nitems, *size);
+ } else {
+ *size = 0;
+ }
+ if (prop != nullptr) {
+ XFree(prop);
+ }
+ return valid;
+}
+
bool XPrivate::readEdid(OutputInfo* output)
{
- int numProps = 0;
- bool found = false;
- Atom* properties = XRRListOutputProperties(_display, output->id, &numProps);
- for (int i = 0; i < numProps; ++i) {
- if (properties[i] == _EDID_ATOM) {
- found = true;
- break;
- }
- }
- XFree(properties);
- if (!found)
- return false;
-
- unsigned long nitems, bytes_after;
- unsigned char *prop;
- int actual_format;
- Atom actual_type;
- bool valid;
-
- XRRGetOutputProperty(_display, output->id, _EDID_ATOM,
- 0, 128, False, False,
- AnyPropertyType,
- &actual_type, &actual_format,
- &nitems, &bytes_after, &prop);
- valid = (actual_format == 8 && nitems >= 128);
-
- if (valid) {
- int idx;
- for (unsigned char *byte = prop + 54; byte < prop + 126; byte += 18) {
- if (byte[0] != 0 || byte[1] != 0 || byte[2] != 0)
- continue; // Not a text block
- if (byte[3] == 0xfc) { // Serial number
- output->modelName = QString::fromLatin1(reinterpret_cast<const char*>(byte) + 5, 13); // It's actually CP-437 but meh
- if ((idx = output->modelName.indexOf('\r')) != -1) {
- output->modelName.truncate(idx);
- }
- output->modelName = output->modelName.trimmed();
- qDebug() << "Display name:" << output->modelName;
- }
- }
- }
- XFree(prop);
- return valid;
+ unsigned char prop[512];
+ unsigned long edidLen = 512;
+
+ if (!getEdid(output->id, prop, &edidLen))
+ return false;
+
+ int idx;
+ for (unsigned char *byte = prop + 54; byte < prop + 126; byte += 18) {
+ if (byte[0] != 0 || byte[1] != 0 || byte[2] != 0)
+ continue; // Not a text block
+ if (byte[3] == 0xfc) { // Serial number
+ output->modelName = QString::fromLatin1(reinterpret_cast<const char*>(byte) + 5, 13); // It's actually CP-437 but meh
+ if ((idx = output->modelName.indexOf('\r')) != -1) {
+ output->modelName.truncate(idx);
+ }
+ output->modelName = output->modelName.trimmed();
+ qDebug() << "Display name:" << output->modelName;
+ }
+ }
+ return true;
+}
+
+void XPrivate::addMissingModesFromEdid(XRRScreenResources *res, RROutput outputId, unsigned char *data)
+{
+ if (data[0] != 2) // Not type 2, skip
+ return;
+ if (data[2] == 0) // Empty block
+ return;
+ int dtdOffset = data[2];
+ if (dtdOffset < 4) // No DTD blocks
+ return;
+ if (dtdOffset > 128) {
+ dtdOffset = 128;
+ }
+
+ int hactive, vactive, pixclk, hsyncoff, hsyncwidth, hblank, vsyncoff, vsyncwidth, vblank;
+ for (int i = dtdOffset; i < 128 - 17; i += 18) {
+ if (data[i] == 0 && data[i + 1] == 0)
+ continue;
+ hactive = data[i+2] + ((data[i+4] & 0xf0) << 4);
+ hblank = data[i+3] + ((data[i+4] & 0x0f) << 8);
+ vactive = data[i+5] + ((data[i+7] & 0xf0) << 4);
+ vblank = data[i+6] + ((data[i+7] & 0x0f) << 8);
+ pixclk = (data[i+1] << 8) | (data[i]); // 10kHz
+ hsyncoff = data[i+8] | ((data[i+11] & 0xC0) << 2);
+ hsyncwidth = data[i+9] | ((data[i+11] & 0x30) << 4);
+ vsyncoff = ((data[i+10] & 0xf0) >> 4) | ((data[i+11] & 0x0C) << 2);
+ vsyncwidth = (data[i+10] & 0x0f) | ((data[i+11] & 0x03) << 4);
+ char buf[100];
+ XRRModeInfo m;
+ memset(&m, 0, sizeof(m));
+ m.width = static_cast<unsigned int>(hactive);
+ m.height = static_cast<unsigned int>(vactive);
+ m.dotClock = static_cast<unsigned long>(pixclk) * 10ul * 1000ul;
+ m.hSyncStart= static_cast<unsigned int>(hactive + hsyncoff);
+ m.hSyncEnd = static_cast<unsigned int>(hactive + hsyncoff + hsyncwidth);
+ m.hTotal = static_cast<unsigned int>(hactive + hblank);
+ m.hSkew = 0;
+ m.vSyncStart= static_cast<unsigned int>(vactive + vsyncoff);
+ m.vSyncEnd = static_cast<unsigned int>(vactive + vsyncoff + vsyncwidth);
+ m.vTotal = static_cast<unsigned int>(vactive + vblank);
+ m.id = 0;
+ m.name = buf;
+ m.nameLength = static_cast<unsigned int>(snprintf(buf, sizeof(buf), "%dx%d_%d", hactive, vactive, static_cast<int>(m.dotClock / (m.hTotal * m.vTotal))));
+ if (m.nameLength > sizeof(buf)) {
+ m.nameLength = sizeof(buf);
+ }
+ if ((data[i+17] & 0x18) == 0x18) {
+ // Digital Separate
+ if (data[i+17] & 0x04) {
+ m.modeFlags |= RR_VSyncPositive;
+ } else {
+ m.modeFlags |= RR_VSyncNegative;
+ }
+ if (data[i+17] & 0x02) {
+ m.modeFlags |= RR_HSyncPositive;
+ } else {
+ m.modeFlags |= RR_HSyncNegative;
+ }
+ } else if ((data[i+17] & 0x18) == 0x10) {
+ // Digital Composite
+ if (data[i+17] & 0x02) {
+ m.modeFlags |= RR_HSyncPositive;
+ } else {
+ m.modeFlags |= RR_HSyncNegative;
+ }
+ m.modeFlags |= RR_VSyncNegative; // Default? Clarify...
+ } else {
+ m.modeFlags |= RR_VSyncNegative | RR_HSyncNegative; // ...
+ }
+ if (data[i+17] & 0x80) {
+ m.modeFlags |= RR_Interlace;
+ }
+ // See if we should add this mode
+ RRMode modeId = 0;
+ for (int mode = 0; mode < res->nmode; ++mode) {
+ if (modeEqual(&res->modes[mode], &m)) {
+ modeId = res->modes[mode].id;
+ break;
+ }
+ }
+ if (modeId == 0) {
+ modeId = XRRCreateMode(_display, DefaultRootWindow(_display), &m);
+ }
+ if (modeId != 0) {
+ XRRAddOutputMode(_display, outputId, modeId);
+ }
+ }
}
void XPrivate::setScreenSize(const QSize &size)
diff --git a/src/xprivate.h b/src/xprivate.h
index d3e9129..28234ac 100644
--- a/src/xprivate.h
+++ b/src/xprivate.h
@@ -44,7 +44,10 @@ public:
void freeResources();
void updateScreenResources();
- bool readEdid(OutputInfo* output);
+ void addMissingEdidResolutions();
+ bool getEdid(RROutput outputId, unsigned char *buffer, unsigned long *size);
+ bool readEdid(OutputInfo* output);
+ void addMissingModesFromEdid(XRRScreenResources *res, RROutput outputId, unsigned char *data);
void disconnectAllCrtcs();
XRRModeInfo* getPreferredMode(OutputInfo *oi, XRRModeInfo *fallback = nullptr) const;
void setScreenSize(const QSize &size);
diff --git a/src/xx.cpp b/src/xx.cpp
index d0b6a3d..80cb1b1 100644
--- a/src/xx.cpp
+++ b/src/xx.cpp
@@ -225,6 +225,11 @@ static double toVertRefresh(const XRRModeInfo *mode)
return 0;
}
+void ScreenSetup::addMissingEdidResolutions()
+{
+ a->addMissingEdidResolutions();
+}
+
//___________________________________________________________________________
void ScreenSetup::updateScreenResources()
{
@@ -254,10 +259,11 @@ void ScreenSetup::initModes()
for (XRRModeInfo *mode : a->_modeMap) {
if (toVertRefresh(mode) < 58 || toVertRefresh(mode) > 61)
continue; // Play it safe and consider only those for copying that are 60Hz
- wanted.remove(RES(mode->width, mode->height));
+ if (!wanted.remove(RES(mode->width, mode->height)))
+ continue; // Is not a wanted resolution
// Make sure all outputs got it
for (OutputInfo *info : a->_outputMap) {
- if (a->getOutputModeForResolution(info->output, mode->width, mode->height))
+ if (a->getOutputModeForResolution(info->output, mode->width, mode->height) != None)
continue;
XRRAddOutputMode(a->_display, info->id, mode->id);
}
@@ -269,7 +275,7 @@ void ScreenSetup::initModes()
unsigned int y = res >> 16;
createMode(x, y, 60, QString::asprintf("%ux%u", x, y));
}
- if (!wanted.isEmpty()) {
+ if (!wanted.isEmpty()) { // Modes were added, update for final loop below
updateScreenResources();
}
// Finally copy all those the projector supports to other outputs
diff --git a/src/xx.h b/src/xx.h
index de52db3..7346c4a 100644
--- a/src/xx.h
+++ b/src/xx.h
@@ -61,6 +61,7 @@ class ScreenSetup : public QObject
{
Q_OBJECT
public:
+ void addMissingEdidResolutions();
void updateScreenResources();
void initModes();
ScreenMode getCurrentMode();