summaryrefslogtreecommitdiffstats
path: root/src/xx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xx.cpp')
-rw-r--r--src/xx.cpp348
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;
+}