diff options
Diffstat (limited to 'src/x.cpp')
-rw-r--r-- | src/x.cpp | 199 |
1 files changed, 170 insertions, 29 deletions
@@ -29,6 +29,25 @@ struct OutputInfo { Projector isProjector; }; +ScreenInfo::ScreenInfo(const OutputInfo *oi, const ModeMap &om) + : position(oi->position), name(oi->modelName), output(oi->outputName), + isProjector(oi->isProjector == Projector::Yes) +{ + if (oi->mode != nullptr) { + currentResolution = QSize(QSize(int(oi->mode->width), int(oi->mode->height))); + } + for (int i = 0; i < oi->output->nmode; ++i) { + if (om.contains(oi->output->modes[i])) { + auto m = om.value(oi->output->modes[i]); + const QSize size(int(m->width), int(m->height)); + modes.append(size); + if (i == 0 && oi->output->npreferred > 0) { + preferredResolution = size; + } + } + } +} + ScreenSetup * ScreenSetup::_instance = nullptr; ScreenSetup::ScreenSetup() : _screenResources(nullptr) @@ -47,6 +66,7 @@ ScreenSetup::ScreenSetup() : _screenResources(nullptr) void ScreenSetup::freeResources() { + freeCrtcBackup(); // Clear the modemap (nothing to be freed, stored in screenResources) _modeMap.clear(); _resolutions.clear(); @@ -163,18 +183,21 @@ void ScreenSetup::updateScreenResources() XRRFreeOutputInfo(info); continue; } + if (info->crtc == None) { + info->crtc = getFreeCrtc(info); + } if (!_crtcMap.contains(info->crtc)) { qDebug() << "Have output" << outputName << "with no known crtc"; XRRFreeOutputInfo(info); continue; } XRRCrtcInfo *crtc = _crtcMap.value(info->crtc); + XRRModeInfo *mode = nullptr; if (!_modeMap.contains(crtc->mode)) { - qDebug() << "Have output" << outputName << " with crtc with no known mode"; - XRRFreeOutputInfo(info); - continue; + qDebug() << "Have output" << outputName << " with crtc with no known mode -- offline?"; + } else { + mode = _modeMap.value(crtc->mode); } - XRRModeInfo *mode = _modeMap.value(crtc->mode); OutputInfo *oi = new OutputInfo(_screenResources->outputs[i], info, crtc, mode); oi->outputName = outputName; if (!this->readEdid(oi)) { // We have no EDID - take it as an indicator that there's a dumb output split/switch box @@ -237,11 +260,11 @@ void ScreenSetup::updateScreenResources() qDebug() << "Loaded."; } -QHash<QString, int> ScreenSetup::getScreenPositions() const +QMap<QString, ScreenInfo> ScreenSetup::getScreenPositions() const { - QHash<QString, int> ret; + QMap<QString, ScreenInfo> ret; for (auto oi : _outputMap) { - ret.insert(oi->outputName, oi->position); + ret.insert(oi->outputName, ScreenInfo(oi, _modeMap)); } return ret; } @@ -394,8 +417,7 @@ static QSize getTotalSizeHorz(const QList<QSize> &list) void ScreenSetup::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun) { - RRMode mode = getOutputModeForResolution(oi->output, - static_cast<unsigned int>(size.width()), static_cast<unsigned int>(size.height())); + RRMode mode = getOutputModeForResolution(oi->output, size); if (mode == None) { if (oi->output->nmode == 0) return; @@ -449,16 +471,9 @@ ScreenMode ScreenSetup::setDefaultMode(bool dryRun) return ScreenMode::Advanced; // Dunno lol QSize screenSize = getTotalSizeHorz(outputSizes); if (!dryRun) { - // Disconnect everything - for (auto crtc : _crtcMap.keys()) { - XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime, - 0, 0, None, RR_Rotate_0, nullptr, 0); - } + disconnectAllCrtcs(); // Set new screen size - XRRSetScreenSize(_display, DefaultRootWindow(_display), - screenSize.width(), screenSize.height(), - int(25.4 * screenSize.width() / 96.0), // standard dpi that X uses - int(25.4 * screenSize.height() / 96.0)); // standard dpi that X uses + setScreenSize(screenSize); } qDebug() << "Virtual size:" << screenSize << "with" << outputSizes.size() << "different screens."; @@ -533,6 +548,25 @@ RRMode ScreenSetup::getOutputModeForResolution(const XRROutputInfo *output, unsi return retval == nullptr ? None : retval->id; } +RRMode ScreenSetup::getOutputModeForResolution(const XRROutputInfo *output, const QSize &resolution) const +{ + return getOutputModeForResolution(output, static_cast<unsigned int>(resolution.width()), static_cast<unsigned int>(resolution.height())); +} + +RRCrtc ScreenSetup::getFreeCrtc(const XRROutputInfo* output) const +{ + for (int i = 0; i < output->ncrtc; ++i) { + RRCrtc c = output->crtcs[i]; + for (auto oi : _outputMap) { + if (oi->output->crtc == c) + goto next; + } + return c; +next:; + } + return None; +} + //___________________________________________________________________________ bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh, QString name) { @@ -577,26 +611,133 @@ ScreenSetup::~ScreenSetup() XCloseDisplay(_display); } -bool ScreenSetup::applyChanges() +void ScreenSetup::setScreenSize(const QSize &size) { - return true; + XRRSetScreenSize(_display, DefaultRootWindow(_display), + size.width(), size.height(), + int(25.4 * size.width() / 96.0), // standard dpi that X uses + int(25.4 * size.height() / 96.0)); // standard dpi that X uses } -void ScreenSetup::revertChanges() +void ScreenSetup::disconnectAllCrtcs() +{ + // Disconnect everything + for (auto crtc : _crtcMap.keys()) { + XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime, + 0, 0, None, RR_Rotate_0, nullptr, 0); + } +} + +void ScreenSetup::freeCrtcBackup() +{ + for (auto entry : _crtcBackup) { + free(entry->outputs); + free(entry); + } + _crtcBackup.clear(); + qDebug() << "CRTC freed"; +} + +void ScreenSetup::createCrtcBackup() { + freeCrtcBackup(); + for (CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) { + const auto src = it.value(); + XRRCrtcInfo *copy = static_cast<XRRCrtcInfo*>(calloc(1, sizeof(XRRCrtcInfo))); + copy->outputs = static_cast<RROutput*>(calloc(size_t(src->noutput), sizeof(RROutput))); + copy->x = src->x; + copy->y = src->y; + copy->mode = src->mode; + copy->rotation = src->rotation; + copy->noutput = src->noutput; + for (int i = 0; i < src->noutput; ++i) { + copy->outputs[i] = src->outputs[i]; + } + _crtcBackup[it.key()] = copy; + } + qDebug() << "Created CRTC backup with entries:" << _crtcBackup.size(); +} +bool ScreenSetup::setClone(const QSize &resolution) +{ + createCrtcBackup(); + disconnectAllCrtcs(); + setScreenSize(resolution); + bool ok = false; + for (auto oi : _outputMap) { + RRMode mode = getOutputModeForResolution(oi->output, resolution); + if (mode != None) { + XRRSetCrtcConfig(_display, _screenResources, oi->output->crtc, CurrentTime, 0, 0, mode, RR_Rotate_0, &oi->id, 1); + ok = true; + } + } + XSync(_display, False); + return ok; +} + +bool ScreenSetup::setCustom(const QList<QPair<QSize, QList<QString>>> &list) +{ + createCrtcBackup(); + QList<QSize> sizes; + for (auto e : list) { + sizes.append(e.first); + } + auto screenSize = getTotalSizeHorz(sizes); + if (screenSize.isEmpty()) + return false; + disconnectAllCrtcs(); + setScreenSize(screenSize); + int x = 0; + bool ok = false; + for (auto e : list) { + const QSize &res = e.first; + for (auto outputName : e.second) { + for (auto oi : _outputMap) { + if (oi->outputName != outputName) + continue; + RRMode mode = getOutputModeForResolution(oi->output, res); + if (mode == None) + continue; + XRRSetCrtcConfig(_display, _screenResources, oi->output->crtc, CurrentTime, x, 0, mode, RR_Rotate_0, &oi->id, 1); + ok = true; + } + } + x += res.width(); + } + XSync(_display, False); + return ok; +} + +void ScreenSetup::revertChanges() +{ + if (_crtcBackup.isEmpty()) + return; + qDebug() << "Starting revert"; + disconnectAllCrtcs(); + QSize screenSize; + for (auto e : _crtcBackup) { + if (e->mode == None || !_modeMap.contains(e->mode)) + continue; + screenSize = screenSize.expandedTo(QSize(e->x + int(_modeMap[e->mode]->width), e->y + int(_modeMap[e->mode]->height))); + } + for (CrtcMap::iterator it = _crtcBackup.begin(); it != _crtcBackup.end(); ++it) { + auto e = it.value(); + if (e->mode == None) + continue; + XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime, + e->x, e->y, e->mode, e->rotation, e->outputs, e->noutput); + } + XSync (_display, False); } -static bool modeBiggerThan(const QPair<quint32, quint32> &a, const QPair<quint32, quint32> &b) +static bool modeBiggerThan(const QSize &a, const QSize &b) { - if (a.first > b.first) + if (a.width() > b.width()) return true; - if (a.first == b.first) - return a.second > b.second; - return false; + return a.width() == b.width() && a.height() > b.height(); } -QList<QPair<quint32, quint32>> ScreenSetup::getCommonModes() const +ResolutionVector ScreenSetup::getCommonModes() const { QHash<QPair<quint32, quint32>, QSet<RROutput>> matches; for (auto oi : _outputMap) { @@ -608,10 +749,10 @@ QList<QPair<quint32, quint32>> ScreenSetup::getCommonModes() const matches[pair].insert(oi->id); } } - QList<QPair<quint32, quint32>> ret; + ResolutionVector ret; for (auto it = matches.begin(); it != matches.end(); ++it) { if (it.value().size() == _outputMap.size()) { - ret.append(it.key()); + ret.append(QSize(int(it.key().first), int(it.key().second))); } } qSort(ret.begin(), ret.end(), modeBiggerThan); |