diff options
-rw-r--r-- | src/xprivate.cpp | 222 | ||||
-rw-r--r-- | src/xprivate.h | 5 | ||||
-rw-r--r-- | src/xx.cpp | 12 | ||||
-rw-r--r-- | src/xx.h | 1 |
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); @@ -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 @@ -61,6 +61,7 @@ class ScreenSetup : public QObject { Q_OBJECT public: + void addMissingEdidResolutions(); void updateScreenResources(); void initModes(); ScreenMode getCurrentMode(); |