summaryrefslogtreecommitdiffstats
path: root/src/x.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/x.cpp')
-rw-r--r--src/x.cpp1062
1 files changed, 585 insertions, 477 deletions
diff --git a/src/x.cpp b/src/x.cpp
index e0aa12f..74020e0 100644
--- a/src/x.cpp
+++ b/src/x.cpp
@@ -1,513 +1,621 @@
#include "x.h"
+#include "cvt.h"
#include <QDebug>
-namespace X
-{
+
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
- Screen * Screen::_instance = NULL;
-
- Screen::Screen()
- {
- // Get initial data (to be freed)
- _display = XOpenDisplay(NULL);
- _screenResources = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
-
- /* Get informations about Xserver */
- updateScreenResources();
-
- }
-
- //___________________________________________________________________________
- void Screen::updateScreenResources()
- {
- // Create the modemap
- _modeMap.clear();
- for (int i = 0; i < _screenResources->nmode; ++i)
- {
- _modeMap.insert(
- _screenResources->modes[i].id,
- Mode(&_screenResources->modes[i])
- );
- }
-
- // Create crtcMap
- _crtcMap.clear();
- for (int i = 0; i < _screenResources->ncrtc; ++i)
- {
- _crtcMap.insert(
- _screenResources->crtcs[i],
- Crtc(_screenResources->crtcs[i],this)
- );
- }
-
- // Create outputMap
- _outputMap.clear();
- for (int i = 0; i < _screenResources->noutput; ++i)
- {
- _outputMap.insert(
- _screenResources->outputs[i],
- new Output(_screenResources->outputs[i], this)
- );
- }
-
- // Create connectedOutputMap
- _connectedOutputList.clear();
- for (OutputMap::iterator it = _outputMap.begin();
- it != _outputMap.end(); ++it)
- if ( (*it)->isConnected() )
- _connectedOutputList.push_back((*it)->_id);
- }
-
- //___________________________________________________________________________
- void Screen::createMode(
- unsigned int resX,
- unsigned int resY,
- unsigned long dotClock,
- unsigned int hSyncStart,
- unsigned int hSyncEnd,
- unsigned int hTotal,
- unsigned int vSyncStart,
- unsigned int vSyncEnd,
- unsigned int vTotal,
- QString name)
- {
- XRRModeInfo m;
- m.width = resX;
- m.height = resY;
- m.dotClock = dotClock;
- m.hSyncStart= hSyncStart;
- m.hSyncEnd = hSyncEnd;
- m.hTotal = hTotal;
- m.hSkew = 0;
- m.vSyncStart= vSyncStart;
- m.vSyncEnd = vSyncEnd;
- m.vTotal = vTotal;
- QByteArray ba = name.toLocal8Bit();
- m.name = ba.data();
-
- XRRCreateMode(const_cast<Display*>(X::Screen::inst()->display()), DefaultRootWindow(X::Screen::inst()->display()), &m);
- //XRRAddOutputMode();
- }
-
- //___________________________________________________________________________
- Screen::~Screen()
- {
- XCloseDisplay(_display);
- XRRFreeScreenResources(_screenResources);
- for (int i = 0; i < _outputMap.size(); ++i)
- delete _outputMap[i];
- }
-
-
-// //_________________________________________________________________________
-//// int Screen::applyChanges()
-//// {
-//// // First make backup to restore in case of an error or user interaction
-
-//// // Calculate screensize
-//// QRect screenSize;
-//// for (CrtcMap::iterator i = _crtcMap.begin(); i != _crtcMap.end(); ++i)
-//// screenSize = screenSize.united(i->getRect());
-
-//// /* values from xrandr */
-//// float dpi = (25.4 * DisplayHeight(_display, 0)) /
-//// DisplayHeightMM(_display, 0);
-//// int widthMM = (int) ((25.4 * screenSize.width()) / dpi);
-//// int heightMM = (int) ((25.4 * screenSize.height()) / dpi);
-
-//// // Set screensize
-//// XRRSetScreenSize(_display, DefaultRootWindow(_display),
-//// screenSize.width(),
-//// screenSize.height(),
-//// widthMM, heightMM);
-
-//// // Apply changes of each crtc
-//// // Stupid applying here, sanitychecks have to be done before
-//// for (CrtcMap::iterator i = _crtcMap.begin(); i != _crtcMap.end(); ++i)
-//// {
-//// if ( i->applyChanges() )
-//// {
-//// revertChanges();
-//// return EXIT_FAILURE;
-//// }
-//// }
-//// return EXIT_SUCCESS;
-//// }
-
-
-//// //_________________________________________________________________________
-//// void Screen::revertChanges()
-//// {
-//// // TODO
-//// }
-
-//// OutputList Screen::getConnectedOutputs() const
-//// {
-//// OutputList result;
-//// for (OutputMap::const_iterator it = _outputMap.begin();
-//// it != _outputMap.end(); ++it)
-//// if (it->isConnected())
-//// result.push_back(it->_id);
-//// return result;
-//// }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- XElement::XElement(XID xid)
- : _id(xid), _validity(false) {}
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Mode::Mode(XRRModeInfo *info)
- {
- if ( info == NULL )
- return;
- _id = info->id;
- _dotClock = info->dotClock;
- _hSyncStart = info->hSyncStart;
- _hSyncEnd = info->hSyncEnd;
- _hTotal = info->hTotal;
- _hSkew = info->hSkew;
- _vSyncStart = info->vSyncStart;
- _vSyncEnd = info->vSyncEnd;
- _vTotal = info->vTotal;
- _name = QString(info->name);
- _modeFlags = info->modeFlags;
- _resolution.setWidth(info->width);
- _resolution.setHeight(info->height);
- _validity = true;
- // rate = ((float) info->dotClock / ((float) info->hTotal * (float) info->vTotal));
-
- qDebug() << "Mode: " << _id << _resolution.width() << _resolution.height();
- }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Crtc::Crtc(XID id, Screen * parent)
- : XElement(id), _parent(parent)
- {
- // Get the information from XRROutputInfo
- XRRCrtcInfo * info = XRRGetCrtcInfo(
- const_cast<Display*>(_parent->display()),
- const_cast<XRRScreenResources*>(_parent->screenResources()),
- id);
-
- // Leave invalid if XID not existent
- if ( !info ){
- return;
- }
-
- _timestamp = info->timestamp;
- _crtcRect = QRect(
- info->x,
- info->y,
- info->width,
- info->height);
- _mode = info->mode;
-
- for (int i = 0; i < info->noutput; ++i)
- _outputs.append(info->outputs[i]);
-
- for (int i = 0; i < info->npossible; ++i)
- _possible.append(info->possible[i]);
-
- _rotation = info->rotation;
- _rotations = info->rotations;
- _validity = true;
- XRRFreeCrtcInfo(info);
-
- qDebug() << "Crtc: " << _id << _mode << _outputs
- << _crtcRect;
- }
-
-//// int Crtc::applyChanges()
-//// {
-//// return XRRSetCrtcConfig(_parent->display,
-//// _parent->screenResources,
-//// _id,
-//// _timestamp,
-//// _crtcRect.x(),
-//// _crtcRect.y(),
-//// _mode,
-//// _rotation,
-//// _outputs.toVector().data(),
-//// _outputs.size()
-//// );
-//// }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Output::Output(XID id, Screen * parent)
- : XElement(id), _parent(parent), _hasReliableEDID(false)
- {
- // Get the information from XRROutputInfo
- XRROutputInfo* info = XRRGetOutputInfo(
- const_cast<Display*>(_parent->display()),
- const_cast<XRRScreenResources*>(_parent->screenResources()),
- id);
-
- // Leave invalid if XID not existent
- if ( !info ){
- return;
- }
-
- _timestamp = info->timestamp;
- _crtc = info->crtc;
- _name = QString(info->name);
- _metricDimension.setWidth(info->mm_width);
- _metricDimension.setHeight(info->mm_height);
- _connection = (State)info->connection;
- //subpixel_order = info->subpixel_order;
- for (int i = 0; i < info->ncrtc; ++i)
- _crtcs.append(info->crtcs[i]);
- for (int i = 0; i < info->nclone; ++i)
- _clones.append(info->clones[i]);
-
- // List the supported modes and extract preferred
- // This is the point where creating the modemap before the outputmap
- // gets a contition.
- for (int i = 0; i < info->nmode; ++i){
- _modes.insert(info->modes[i]);
- if (i < info->npreferred)
- _preferred = i;
- }
- XRRFreeOutputInfo(info);
-
- // Check if this is a beamer
- _isProjector = _metricDimension.isEmpty();
-
- // Maybe obsolete, since no preferred mode means no EDID.
- // // EDID = ?;
- int nprop;
- Atom *props = XRRListOutputProperties(
- const_cast<Display*>(_parent->display()),
- _id,
- &nprop);
-
- for (int i = 0; i < nprop; ++i)
- {
- char *atom_name = XGetAtomName (
- const_cast<Display*>(_parent->display()),
- props[i]);
- if ( strcmp (atom_name, "EDID") == 0)
- {
-// // Print Stuff
-// unsigned long nitems, bytes_after;
-// unsigned char *prop;
-// int actual_format;
-// Atom actual_type;
-// int bytes_per_item;
-
-// XRRGetOutputProperty (dpy, ID, props[i],
-// 0, 100, False, False,
-// AnyPropertyType,
-// &actual_type, &actual_format,
-// &nitems, &bytes_after, &prop);
-
-// XRRPropertyInfo *propinfo = XRRQueryOutputProperty(dpy, ID, props[i]);
-// bytes_per_item = actual_format / 8;
-
-// fprintf (stderr, "\t%s: ", atom_name);
-// for (unsigned int k = 0; k < nitems; k++)
-// {
-// if (k != 0)
-// {
-// if ((k % 16) == 0)
-// {
-// fprintf (stderr, "\n\t\t");
-// }
-// }
-// const uint8_t *val = prop + (k * bytes_per_item);
-// fprintf (stderr, "%d02", *val);
-// }
-// free(propinfo);
- _hasReliableEDID = true;
- }
- }
- free(props);
-
-
- qDebug() << "Output: " << _id << _name << _crtc
- << _metricDimension << _clones << _modes;
- }
-
-// //_________________________________________________________________________
-//// int Screen::Output::changeMode(XID mode)
-//// {
-//// // Check if this mode is supported
-//// if ( ! _modes.contains(mode) )
-//// return 1;
-
-//// // Check if this output is connected
-//// if ( _connection != Output::Connected )
-//// return 2;
-
-//// // If this output is NOT conected to a crtc
-//// if ( ! isActive() )
-//// {
-//// // Try to find a unconnected crtc which
-//// // this output can be connected to
-//// for (CrtcList::iterator i = _crtcs.begin(); ; ++i)
-//// {
-//// // If this search reches end no appropriate
-//// // crtc has been found
-//// if ( i == _crtcs.end() )
-//// return 3;
-
-//// // If a free crtc was found connect and apply mode
-//// if ( _parent->_crtcMap[*i].getConnectedOutputs().empty() )
-//// {
-//// _parent->_crtcMap[*i].connect(this, mode);
-//// break;
-//// }
-//// }
-//// }
-//// //If it is already connected apply mode to the crtc
-//// // TODO(Manuel):continue
-//// return 0;
-//// }
-
-//// //_________________________________________________________________________
-//// int Output::changePos()
-//// {
-//// return 1;
-//// }
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-}
-
-
-
-
-
-
+enum class Projector {
+ No,
+ Yes,
+ Maybe
+};
+
+struct OutputInfo {
+ OutputInfo(RROutput id, XRROutputInfo *output, XRRCrtcInfo *crtc, XRRModeInfo *mode)
+ : output(output), crtc(crtc), mode(mode), id(id), position(-1), isProjector(Projector::No) {}
+ ~OutputInfo() {
+ XRRFreeOutputInfo(output);
+ }
+ XRROutputInfo* output;
+ XRRCrtcInfo* crtc;
+ XRRModeInfo* mode;
+ RROutput id;
+ QString modelName;
+ QString outputName;
+ int position;
+ Projector isProjector;
+};
+
+ScreenSetup * ScreenSetup::_instance = nullptr;
+
+ScreenSetup::ScreenSetup() : _screenResources(nullptr)
+{
+ // Get initial data (to be freed)
+ _display = XOpenDisplay(nullptr);
+ if (_display == nullptr) {
+ qFatal("Cannot open display");
+ ::exit(1);
+ }
+ _EDID_ATOM = XInternAtom(_display, RR_PROPERTY_RANDR_EDID, False);
+ /* Get informations about Xserver */
+ updateScreenResources();
+}
+void ScreenSetup::freeResources()
+{
+ // Clear the modemap (nothing to be freed, stored in screenResources)
+ _modeMap.clear();
+ _resolutions.clear();
+
+ // Clear output info
+ for (OutputInfo *output : _outputMap.values()) {
+ delete output;
+ }
+ _outputMap.clear();
+
+ // Clear crtc info
+ for (XRRCrtcInfo *info : _crtcMap.values()) {
+ XRRFreeCrtcInfo(info);
+ }
+ _crtcMap.clear();
+
+ // Release resources
+ if (_screenResources != nullptr) {
+ XRRFreeScreenResources(_screenResources);
+ _screenResources = nullptr;
+ }
+ XRRFreeScreenResources(_screenResources);
+}
+static bool xRectLessThan(const OutputInfo* const &a, const OutputInfo* const &b)
+{
+ return a->crtc->x < b->crtc->x;
+}
+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;
+}
+/**
+ * Check list of known model names that falsely report a screen size or similar
+ */
+static QStringList initProjectorList()
+{
+ QStringList list;
+ list << "AT-HDVS-RX"; // Switchbox
+ // TODO: Load from file
+ return list;
+}
+static bool isProjectorName(const QString &name)
+{
+ static QStringList projectors = initProjectorList();
+ return projectors.contains(name);
+}
+//___________________________________________________________________________
+void ScreenSetup::updateScreenResources()
+{
+ freeResources();
+ _screenResources = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
+ // Create the modemap
+ qDebug() << "Modes";
+ for (int i = 0; i < _screenResources->nmode; ++i) {
+ _modeMap.insert(
+ _screenResources->modes[i].id,
+ &_screenResources->modes[i]);
+ qDebug() << _screenResources->modes[i].id << "\t"
+ << _screenResources->modes[i].width << "x"
+ << _screenResources->modes[i].height;
+ }
+
+ /*
+ int num = 0;
+ XRRMonitorInfo *mi = XRRGetMonitors(_display, DefaultRootWindow(_display), True, &num);
+ for (int i = 0; i < num; ++i) {
+ char *name = XGetAtomName(_display, mi [i].name);
+ if (name == nullptr)
+ continue;
+ qDebug() << "some monitor with name" << name << mi[i].primary;
+ XFree(name);
+ }
+ */
+
+ // Create crtcMap
+ qDebug() << "CRTCs";
+ for (int i = 0; i < _screenResources->ncrtc; ++i) {
+ XRRCrtcInfo * info = XRRGetCrtcInfo(
+ _display,
+ _screenResources,
+ _screenResources->crtcs[i]);
+ if (info == nullptr) {
+ qDebug() << "Error getting CRTC info for crtc" << i;
+ continue;
+ }
+ qDebug() << _screenResources->crtcs[i] << "-- Outputs:" << info->noutput << "Possible:" << info->npossible;
+ _crtcMap.insert(_screenResources->crtcs[i], info);
+ }
+
+ // Create outputmap and connectedOutputMap
+ qDebug() << "Outputs";
+ QHash<RROutput, QString> tempMap;
+ QMap<Projector, int> typeCount;
+ for (int i = 0; i < _screenResources->noutput; ++i) {
+ XRROutputInfo* info = XRRGetOutputInfo(
+ _display,
+ _screenResources,
+ _screenResources->outputs[i]);
+ if (info == nullptr) {
+ qDebug() << "Error getting info for output" << i;
+ continue;
+ }
+ const QString outputName = QString::fromLocal8Bit(info->name, info->nameLen);
+ tempMap.insert(_screenResources->outputs[i], outputName);
+ if (info->connection != RR_Connected) {
+ qDebug() << "Ignoring disconnected output" << outputName;
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ if (!_crtcMap.contains(info->crtc)) {
+ qDebug() << "Have output" << outputName << "with no known crtc";
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ XRRCrtcInfo *crtc = _crtcMap.value(info->crtc);
+ if (!_modeMap.contains(crtc->mode)) {
+ qDebug() << "Have output" << outputName << " with crtc with no known mode";
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ 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
+ oi->isProjector = Projector::Maybe;
+ } else if ((info->mm_height == 0 && info->mm_width == 0) || isProjectorName(oi->modelName)) {
+ oi->isProjector = Projector::Yes; // Screens with size 0x0 are projectors by convention
+ } else if (info->mm_width > 500 && info->mm_height > 500) { // Big screen - probably should be handled like a projector
+ oi->isProjector = Projector::Yes;
+ } else if (info->mm_width > 350 && info->mm_height > 350) { // Somewhere in-between
+ oi->isProjector = Projector::Maybe;
+ }
+ typeCount[oi->isProjector]++;
+ _outputMap.insert(_screenResources->outputs[i], oi);
+ qDebug() << "Connected" << outputName << "-- Clones:" << info->nclone << "Modes:" << info->nmode << "Crtcs:" << info->ncrtc << "Preferred:" << info->npreferred;
+ }
+ // Final checks for projector
+ if (typeCount[Projector::Yes] == 0) {
+ // No definitive projector
+ if (typeCount[Projector::Maybe] > 0 && typeCount[Projector::No] > 0) {
+ // Potential projector(s) and normal screen, promote
+ for (OutputInfo *info : _outputMap) {
+ if (info->isProjector == Projector::Maybe) {
+ info->isProjector = Projector::Yes;
+ break;
+ }
+ }
+ }
+ }
+ // Print mappings
+ for (XRRCrtcInfo *info : _crtcMap) {
+ qDebug() << info << ":";
+ for (int i = 0; i < info->npossible; ++i) {
+ qDebug() << "Possible:" << tempMap[info->possible[i]];
+ }
+ }
+ // Determine each screen's position
+ QList<OutputInfo*> screens = _outputMap.values();
+ // Nothing connected?
+ if (screens.isEmpty())
+ return;
+ qSort(screens.begin(), screens.end(), xRectLessThan);
+ int endX = -0xffff;
+ qDebug() << "From left to right";
+ for (OutputInfo* output : screens) {
+ if (output->crtc->x >= endX) {
+ QSize res(0, 0);
+ if (_modeMap.contains(output->crtc->mode)) {
+ auto mode = _modeMap.value(output->crtc->mode);
+ res = QSize(int(mode->width), int(mode->height));
+ }
+ _resolutions.append(res);
+ endX = -0xffff; // Reset
+ }
+ output->position = _resolutions.size() - 1;
+ qDebug() << "Screen (" << output->crtc->x << "," << output->crtc->y << ") @" << output->crtc->width << "x" << output->crtc->height << "as screen" << output->position;
+ if (output->crtc->x + int(output->crtc->width) > endX) {
+ endX = output->crtc->x + int(output->crtc->width);
+ }
+ }
+ qDebug() << "Loaded.";
+}
+QHash<QString, int> ScreenSetup::getScreenPositions() const
+{
+ QHash<QString, int> ret;
+ for (auto oi : _outputMap) {
+ ret.insert(oi->outputName, oi->position);
+ }
+ return ret;
+}
+bool ScreenSetup::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;
+}
+/**
+ * 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 : _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 : _outputMap) {
+ if (getOutputModeForResolution(info->output, mode->width, mode->height))
+ continue;
+ XRRAddOutputMode(_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 : _outputMap.keys()) {
+ OutputInfo *oi = _outputMap[key];
+ if (oi->isProjector == Projector::Yes) {
+ copyModesToAll(key, oi->output->nmode);
+ }
+ }
+ updateScreenResources();
+}
+XRRModeInfo* ScreenSetup::getPreferredMode(OutputInfo *oi) const
+{
+ if (oi->output->nmode == 0) {
+ qDebug() << "getPreferredMode: Output" << oi->outputName << "has no modes!?";
+ return nullptr; // WTF!?
+ }
+ RRMode mode;
+ if (oi->output->npreferred > 0) {
+ mode = oi->output->modes[0];
+ } else {
+ mode = getOutputModeForResolution(oi->output, 1920, 1080);
+ if (mode == None) {
+ mode = getOutputModeForResolution(oi->output, 1280, 720);
+ }
+ if (mode == None) {
+ mode = getOutputModeForResolution(oi->output, 1280, 800);
+ }
+ if (mode == None) {
+ mode = oi->output->modes[0];
+ }
+ }
+ if (!_modeMap.contains(mode))
+ return nullptr;
+ return _modeMap[mode];
+}
+QList<QSize> ScreenSetup::getTotalSize(const QList<OutputInfo*> &projectors, const QList<OutputInfo*> &screens) const
+{
+ const int max = qMax(screens.size(), projectors.size());
+ QList<QSize> modes;
+ for (int i = 0; i < max; ++i) {
+ XRRModeInfo *mode = nullptr;
+ if (i < projectors.size()) {
+ mode = getPreferredMode(projectors.at(i));
+ }
+ if (mode == nullptr && i < screens.size()) {
+ mode = getPreferredMode(screens.at(i));
+ }
+ if (mode != nullptr) {
+ modes.append(QSize(int(mode->width), int(mode->height)));
+ }
+ }
+ return modes;
+}
+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;
+}
+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()));
+ if (mode == None) {
+ if (oi->output->nmode == 0)
+ return;
+ qDebug() << oi->outputName << "doesn't support" << size << "- falling back to its default";
+ mode = oi->output->modes[0];
+ }
+ if (!dryRun) {
+ XRRSetCrtcConfig(_display,
+ _screenResources,
+ oi->output->crtc,
+ CurrentTime,
+ x, y, mode,
+ RR_Rotate_0,
+ &oi->id, 1);
+ }
+ qDebug() << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y;
+}
-//// qDebug() << XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[1],
-//// CurrentTime,
-//// 0, 0,
-//// 597,
-//// RR_Rotate_0,
-//// &(screenResources->outputs[3]),
-//// 1);
-//// qDebug() << XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[0],
-//// CurrentTime,
-//// 1920, 0,
-//// 586,
-//// RR_Rotate_0,
-//// &(screenResources->outputs[1]),
-//// 1);
-//// HOLY!!
-//// XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[1],
-//// CurrentTime,
-//// 0, 0,
-//// 587,
-//// RR_Rotate_0,
-//// &screenResources->outputs[1],
-//// 1);
+ScreenMode ScreenSetup::getCurrentMode()
+{
+ if (_outputMap.size() == 1)
+ return ScreenMode::Single;
+ if (_outputMap.size() > 2)
+ return ScreenMode::Advanced;
+ for (auto oi : _outputMap) {
+ if (oi->crtc->x != 0 || oi->crtc->y != 0)
+ return ScreenMode::Dual;
+ }
+ return ScreenMode::Clone;
+}
+ScreenMode ScreenSetup::setDefaultMode(bool dryRun)
+{
+ if (_outputMap.size() == 1) // Only one output exists, do nothing
+ return ScreenMode::Single;
+ QMap<QString, OutputInfo*> screenMap;
+ QMap<QString, OutputInfo*> projectorMap;
+ for (auto o : _outputMap) {
+ qDebug() << o->outputName << quint32(o->isProjector);
+ if (o->isProjector == 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 = getTotalSize(projectors, screens);
+ if (outputSizes.isEmpty())
+ 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);
+ }
+ // 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
+ }
+
+ qDebug() << "Virtual 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()) {
+ setOutputResolution(projectors.at(i), offset, 0, size, dryRun);
+ }
+ if (i < screens.size()) {
+ setOutputResolution(screens.at(i), offset, 0, size, dryRun);
+ }
+ offset += size.width();
+ }
+ XSync(_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 && _outputMap.size() == 2) // Two outputs, two sizes -- extended
+ return ScreenMode::Dual;
+ return ScreenMode::Advanced; // Must be more than 2 outputs -> something more involved
+}
+/**
+ * Copy first "num" modes from output to all other outputs if they
+ * dont have a suitable mode yet.
+ */
+void ScreenSetup::copyModesToAll(RROutput sourceId, int num)
+{
+ const XRROutputInfo *outputInfo = _outputMap[sourceId]->output;
+ for (int i = 0; i < num; ++i) {
+ if (!_modeMap.contains(outputInfo->modes[i])) {
+ qDebug() << "BUG: Mode" << outputInfo->modes[i] << "not found in copyModesToAll";
+ continue;
+ }
+ const XRRModeInfo *mode = _modeMap[outputInfo->modes[i]];
+ for (auto other : _outputMap.keys()) {
+ if (other == sourceId)
+ continue;
+ const XRROutputInfo *otherInfo = _outputMap[other]->output;
+ if (getOutputModeForResolution(otherInfo, mode->width, mode->height) != None)
+ continue;
+ XRRAddOutputMode(_display, other, outputInfo->modes[i]);
+ }
+ }
+}
+/**
+ * Get the RRMode for the specified output matching the given resolution.
+ * If multiple modes match the given resolution, the function will
+ * return the first matching preferred mode. If no preferred
+ * mode matches, the non-preferred mode with the highest refresh rate
+ * will be returned.
+ * Otherwise, None will be returned.
+ */
+RRMode ScreenSetup::getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const
+{
+ XRRModeInfo *retval = nullptr;
+ for (int i = 0; i < output->nmode; ++i) {
+ if (!_modeMap.contains(output->modes[i]))
+ continue;
+ XRRModeInfo *info = _modeMap[output->modes[i]];
+ if (info->width == width && info->height == height) {
+ if (i < output->npreferred)
+ return output->modes[i];
+ if (retval == nullptr || retval->dotClock < info->dotClock) {
+ retval = info;
+ }
+ }
+ }
+ return retval == nullptr ? None : retval->id;
+}
+//___________________________________________________________________________
+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 : _modeMap) {
+ if (mode->width == m.width && mode->height == m.height && mode->dotClock == m.dotClock)
+ return true; // Already exists, return true?
+ }
+
+ RRMode xid = XRRCreateMode(_display, DefaultRootWindow(_display), &m);
+ qDebug() << "Return value of create was" << xid;
+ // Immediately add to all screens
+ for (OutputInfo *info : _outputMap) {
+ XRRAddOutputMode(_display, info->id, xid);
+ }
+ return true;
+}
+//___________________________________________________________________________
+ScreenSetup::~ScreenSetup()
+{
+ freeResources();
+ XCloseDisplay(_display);
+}
+bool ScreenSetup::applyChanges()
+{
+ return true;
+}
+void ScreenSetup::revertChanges()
+{
+}
-/////////////////////////////// DBEUG //// DEBUG //////////////////////////////////
-//// //SCREEN
-//// qDebug() << "ScreenResources: ";
-//// qDebug() << "Count of crtcs: " << screenResources->ncrtc;
-//// qDebug() << "Count of outputs: " << screenResources->noutput;
-//// qDebug() << "Count of modes: " << screenResources->nmode;
+static bool modeBiggerThan(const QPair<quint32, quint32> &a, const QPair<quint32, quint32> &b)
+{
+ if (a.first > b.first)
+ return true;
+ if (a.first == b.first)
+ return a.second > b.second;
+ return false;
+}
-//// //MODES
-//// for (int i = 0; i < screenResources->nmode; ++i)
-//// {
-//// qDebug() << screenResources->modes[i].id
-//// << screenResources->modes[i].width
-//// << screenResources->modes[i].height
-////// << screenResources->modes[i].dotClock
-////// << screenResources->modes[i].hSyncStart
-////// << screenResources->modes[i].hSyncEnd
-////// << screenResources->modes[i].hTotal
-////// << screenResources->modes[i].hSkew
-////// << screenResources->modes[i].vSyncStart
-////// << screenResources->modes[i].vSyncEnd
-////// << screenResources->modes[i].vTotal
-////// << screenResources->modes[i].nameLength
-//// << screenResources->modes[i].name;
-//// }
+QList<QPair<quint32, quint32>> ScreenSetup::getCommonModes() const
+{
+ QHash<QPair<quint32, quint32>, QSet<RROutput>> matches;
+ for (auto oi : _outputMap) {
+ for (int i = 0; i < oi->output->nmode; ++i) {
+ if (!_modeMap.contains(oi->output->modes[i]))
+ continue;
+ const auto mode = _modeMap[oi->output->modes[i]];
+ const QPair<quint32, quint32> pair = qMakePair(mode->width, mode->height);
+ matches[pair].insert(oi->id);
+ }
+ }
+ QList<QPair<quint32, quint32>> ret;
+ for (auto it = matches.begin(); it != matches.end(); ++it) {
+ if (it.value().size() == _outputMap.size()) {
+ ret.append(it.key());
+ }
+ }
+ qSort(ret.begin(), ret.end(), modeBiggerThan);
+ return ret;
+}
-//// //CRTCS
-//// for (int j = 0; j < screenResources->ncrtc; ++j)
-//// {
-//// XRRCrtcInfo *CrtcInfo = XRRGetCrtcInfo(display, screenResources, screenResources->crtcs[j]);
-//// qDebug() << "\n-------- CrtcInfo";
-//// qDebug() << "timestamp: " << CrtcInfo->timestamp;
-//// qDebug() << "x: " << CrtcInfo->x;
-//// qDebug() << "y: " << CrtcInfo->y;
-//// qDebug() << "width: " << CrtcInfo->width;
-//// qDebug() << "height: " << CrtcInfo->height;
-//// qDebug() << "rotation: " << CrtcInfo->rotation;
-//// qDebug() << "noutput: " << CrtcInfo->noutput;
-//// qDebug() << "rotations: " << CrtcInfo->rotations;
-//// qDebug() << "npossible: " << CrtcInfo->npossible;
-//// XRRFreeCrtcInfo(CrtcInfo);
-//// }
-//// // OUTPUTS
-//// for (int nOut = 0; nOut < screenResources->noutput; ++nOut)
-//// {
-//// XRROutputInfo *OutputInfo = XRRGetOutputInfo (display, screenResources, screenResources->outputs[nOut]);
-////// if (OutputInfo->connection == RR_Connected) {
-//// qDebug() << "\n--- Output " << nOut;
-//// qDebug() << "name " << OutputInfo->name;
-//// qDebug() << "mm_width " << OutputInfo->mm_width;
-//// qDebug() << "mm_height " << OutputInfo->mm_height;
-//// qDebug() << "ncrtc " << OutputInfo->ncrtc;
-//// qDebug() << "nclone " << OutputInfo->nclone;
-//// qDebug() << "nmode " << OutputInfo->nmode;
-//// qDebug() << "npreferred " << OutputInfo->npreferred;
-
-//// for (int j = 0; j < OutputInfo->nmode; j++)
-//// {
-//// qDebug() << "mode" << j << ": " << OutputInfo->modes[j];
-//// }
-////// }
-//// XRRFreeOutputInfo (OutputInfo);
-//// }
-///////////////////////////////////////////////////////////////////////////////////