diff options
Diffstat (limited to 'src/xprivate.cpp')
-rw-r--r-- | src/xprivate.cpp | 222 |
1 files changed, 179 insertions, 43 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) |