diff options
Diffstat (limited to 'src/x.cpp')
-rw-r--r-- | src/x.cpp | 1062 |
1 files changed, 585 insertions, 477 deletions
@@ -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); -//// } -/////////////////////////////////////////////////////////////////////////////////// |