diff options
Diffstat (limited to 'src/xx.cpp')
-rw-r--r-- | src/xx.cpp | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/src/xx.cpp b/src/xx.cpp new file mode 100644 index 0000000..2a8cdc5 --- /dev/null +++ b/src/xx.cpp @@ -0,0 +1,348 @@ +#include "xx.h" +#include "xprivate.h" +#include "cvt.h" +#include <QDebug> + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +static ScreenInfo initScreenInfo(const OutputInfo *oi, const ModeMap &om) +{ + ScreenInfo si; + si.position = oi->position; + si.name = oi->modelName; + si.output = oi->outputName; + si.isProjector = (oi->outputType == Projector::Yes); + if (oi->mode != nullptr) { + si.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)); + if (!si.modes.contains(size)) { + si.modes.append(size); + } + if (i == 0 && oi->output->npreferred > 0) { + si.preferredResolution = size; + } + } + } + return si; +} + +ScreenSetup * ScreenSetup::_instance = nullptr; + +ScreenSetup::ScreenSetup() : a(new XPrivate()) +{ + /* Get informations about Xserver */ + updateScreenResources(); + +} + +static double toVertRefresh(const XRRModeInfo *mode) +{ + if (mode->hTotal > 0 && mode->vTotal > 0) + return (double(mode->dotClock) / (double(mode->hTotal) * double(mode->vTotal))); + return 0; +} + +//___________________________________________________________________________ +void ScreenSetup::updateScreenResources() +{ + a->updateScreenResources(); +} + +QMap<QString, ScreenInfo> ScreenSetup::getScreenPositions() const +{ + QMap<QString, ScreenInfo> ret; + for (auto oi : a->_outputMap) { + ret.insert(oi->outputName, initScreenInfo(oi, a->_modeMap)); + } + return ret; +} + +/** + * Create common modes and add them to all outputs. + * Make sure every output has some variant of the most commonly + * used modes. + */ +void ScreenSetup::initModes() +{ + // First copy typical resolutions to all outputs +#define RES(x,y) (((y) << 16) | (x)) + QSet<quint32> wanted; + wanted << RES(1280, 720) << RES(1280, 800) << RES(1920, 1080); + 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)); + // Make sure all outputs got it + for (OutputInfo *info : a->_outputMap) { + if (a->getOutputModeForResolution(info->output, mode->width, mode->height)) + continue; + XRRAddOutputMode(a->_display, info->id, mode->id); + } + } +#undef RES + // Create those that no output supported + for (auto res : wanted) { + unsigned int x = res & 0xffff; + unsigned int y = res >> 16; + createMode(x, y, 60, QString::asprintf("%ux%u", x, y)); + } + if (!wanted.isEmpty()) { + updateScreenResources(); + } + // Finally copy all those the projector supports to other outputs + for (auto key : a->_outputMap.keys()) { + OutputInfo *oi = a->_outputMap[key]; + if (oi->outputType == Projector::Yes) { + a->copyModesToAll(key, oi->output->nmode); + } + } + updateScreenResources(); +} + +static QSize getTotalSizeHorz(const QList<QSize> &list) +{ + QSize ret(0, 0); + for (auto e : list) { + ret.rwidth() += e.width(); + ret.rheight() = qMax(ret.height(), e.height()); + } + return ret; +} + +ScreenMode ScreenSetup::getCurrentMode() +{ + bool notAtOrigin = false; + for (auto oi : a->_outputMap) { + if (oi->mode != nullptr) { + if (oi->crtc->x != 0 || oi->crtc->y != 0) { + notAtOrigin = true; + } + } + } + if (a->_outputMap.size() == 1) + return ScreenMode::Single; + if (a->_outputMap.size() > 2) + return ScreenMode::Advanced; + if (notAtOrigin) + return ScreenMode::Dual; + return ScreenMode::Clone; +} + +ScreenMode ScreenSetup::setDefaultMode(bool dryRun) +{ + if (a->_outputMap.size() == 1) // Only one output exists, do nothing + return ScreenMode::Single; + QMap<QString, OutputInfo*> screenMap; + QMap<QString, OutputInfo*> projectorMap; + for (auto o : a->_outputMap) { + qDebug() << o->outputName << quint32(o->outputType); + if (o->outputType == Projector::Yes) { + projectorMap.insert(o->outputName, o); + } else { + screenMap.insert(o->outputName, o); + } + } + auto projectors = projectorMap.values(); + auto screens = screenMap.values(); + qDebug() << projectors.size() << "projectors," << screens.size() << "screens."; + QList<QSize> outputSizes = a->getTotalSize(projectors, screens); + if (outputSizes.isEmpty()) + return ScreenMode::Advanced; // Dunno lol + QSize screenSize = getTotalSizeHorz(outputSizes); + if (!dryRun) { + XGrabServer(a->_display); + a->disconnectAllCrtcs(); + // Set new screen size + a->setScreenSize(screenSize); + } + + qDebug() << "Virtual screen size:" << screenSize << "with" << outputSizes.size() << "different screens."; + + int offset = 0; + for (int i = 0; i < outputSizes.size(); ++i) { + const QSize &size = outputSizes.at(i); + if (i < projectors.size()) { + a->setOutputResolution(projectors.at(i), offset, 0, size, dryRun); + } + if (i < screens.size()) { + a->setOutputResolution(screens.at(i), offset, 0, size, dryRun); + } + offset += size.width(); + } + if (!dryRun) { + XUngrabServer(a->_display); + XSync(a->_display, False); + } + updateScreenResources(); // Re-Read + if (outputSizes.size() == 1) // One output size, at least 2 outputs in total -- clone mode + return ScreenMode::Clone; + if (outputSizes.size() == 2 && a->_outputMap.size() == 2) // Two outputs, two sizes -- extended + return ScreenMode::Dual; + return ScreenMode::Advanced; // Must be more than 2 outputs -> something more involved +} + +//___________________________________________________________________________ +bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh, QString name) +{ + QByteArray ba = name.toLocal8Bit(); + mode *mode = vert_refresh(int(resX), int(resY), refresh, 0, 0, 0); + if (mode == nullptr) + return false; + XRRModeInfo m; + m.width = static_cast<unsigned int>(mode->hr); + m.height = static_cast<unsigned int>(mode->vr); + m.dotClock = static_cast<unsigned long>(mode->pclk) * 1000ul * 1000ul; + m.hSyncStart= static_cast<unsigned int>(mode->hss); + m.hSyncEnd = static_cast<unsigned int>(mode->hse); + m.hTotal = static_cast<unsigned int>(mode->hfl); + m.hSkew = 0; + m.vSyncStart= static_cast<unsigned int>(mode->vss); + m.vSyncEnd = static_cast<unsigned int>(mode->vse); + m.vTotal = static_cast<unsigned int>(mode->vfl); + m.id = 0; + m.name = ba.data(); + m.nameLength = static_cast<unsigned int>(ba.length()); + free(mode); + + for (XRRModeInfo *mode : a->_modeMap) { + if (mode->width == m.width && mode->height == m.height && mode->dotClock == m.dotClock) + return true; // Already exists, return true? + } + + RRMode xid = XRRCreateMode(a->_display, DefaultRootWindow(a->_display), &m); + qDebug() << "Return value of create was" << xid; + // Immediately add to all screens + for (OutputInfo *info : a->_outputMap) { + XRRAddOutputMode(a->_display, info->id, xid); + } + return true; +} + +//___________________________________________________________________________ +ScreenSetup::~ScreenSetup() +{ + delete a; +} + +bool ScreenSetup::setClone(const QSize &resolution) +{ + a->createCrtcBackup(); + XGrabServer(a->_display); + a->disconnectAllCrtcs(); + a->setScreenSize(resolution); + bool ok = false; + for (auto oi : a->_outputMap) { + ok = a->setOutputResolution(oi, 0, 0, resolution) || ok; + } + XUngrabServer(a->_display); + XSync(a->_display, False); + return ok; +} + +bool ScreenSetup::setCustom(const QList<QPair<QSize, QList<QString>>> &list) +{ + QList<QSize> sizes; + for (auto e : list) { + if (e.second.isEmpty()) + continue; + sizes.append(e.first); + } + if (sizes.isEmpty()) + return false; + a->createCrtcBackup(); + auto screenSize = getTotalSizeHorz(sizes); + if (screenSize.isEmpty()) + return false; + XGrabServer(a->_display); + a->disconnectAllCrtcs(); + a->setScreenSize(screenSize); + int x = 0; + bool ok = false; + for (auto e : list) { + if (e.second.isEmpty()) + continue; + const QSize &res = e.first; + for (auto outputName : e.second) { + for (auto oi : a->_outputMap) { + if (oi->outputName != outputName) + continue; + ok = a->setOutputResolution(oi, x, 0, res) || ok; + } + } + x += res.width(); + } + XUngrabServer(a->_display); + XSync(a->_display, False); + return ok; +} + +void ScreenSetup::revertChanges() +{ + if (a->_crtcBackup.isEmpty()) + return; + qDebug() << "Starting revert"; + XGrabServer(a->_display); + a->disconnectAllCrtcs(); + QSize screenSize; + for (auto e : a->_crtcBackup) { + if (e->mode == None || !a->_modeMap.contains(e->mode)) + continue; + screenSize = screenSize.expandedTo(QSize(e->x + int(a->_modeMap[e->mode]->width), e->y + int(a->_modeMap[e->mode]->height))); + } + for (CrtcMap::iterator it = a->_crtcBackup.begin(); it != a->_crtcBackup.end(); ++it) { + auto e = it.value(); + if (e->mode == None) + continue; + XRRSetCrtcConfig(a->_display, a->_screenResources, it.key(), CurrentTime, + e->x, e->y, e->mode, e->rotation, e->outputs, e->noutput); + } + XUngrabServer(a->_display); + XSync(a->_display, False); +} + +static bool modeBiggerThan(const QSize &a, const QSize &b) +{ + if (a.width() > b.width()) + return true; + return a.width() == b.width() && a.height() > b.height(); +} + +ResolutionVector ScreenSetup::getCommonModes() const +{ + QHash<QPair<quint32, quint32>, QSet<RROutput>> matches; + for (auto oi : a->_outputMap) { + for (int i = 0; i < oi->output->nmode; ++i) { + if (!a->_modeMap.contains(oi->output->modes[i])) + continue; + const auto mode = a->_modeMap[oi->output->modes[i]]; + const QPair<quint32, quint32> pair = qMakePair(mode->width, mode->height); + matches[pair].insert(oi->id); + } + } + ResolutionVector ret; + for (auto it = matches.begin(); it != matches.end(); ++it) { + if (it.value().size() == a->_outputMap.size()) { + ret.append(QSize(int(it.key().first), int(it.key().second))); + } + } + qSort(ret.begin(), ret.end(), modeBiggerThan); + return ret; +} + +int ScreenSetup::getOutputCount() const +{ + return a->_outputMap.size(); +} + +const ResolutionVector &ScreenSetup::getVirtualResolutions() const +{ + return a->_resolutions; +} |