summaryrefslogtreecommitdiffstats
path: root/src/x.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/x.cpp')
-rw-r--r--src/x.cpp199
1 files changed, 170 insertions, 29 deletions
diff --git a/src/x.cpp b/src/x.cpp
index 74020e0..df257bb 100644
--- a/src/x.cpp
+++ b/src/x.cpp
@@ -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);