File indexing completed on 2024-04-21 15:08:51

0001 /***************************************************************************
0002  *   Copyright (C) 2012-2016 by Daniel Nicoletti <dantti12@gmail.com>      *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  *                                                                         *
0009  *   This program is distributed in the hope that it will be useful,       *
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0012  *   GNU General Public License for more details.                          *
0013  *                                                                         *
0014  *   You should have received a copy of the GNU General Public License     *
0015  *   along with this program; see the file COPYING. If not, write to       *
0016  *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,  *
0017  *   Boston, MA 02110-1301, USA.                                           *
0018  ***************************************************************************/
0019 
0020 #include "Output.h"
0021 
0022 #include <QGuiApplication>
0023 #include <QLoggingCategory>
0024 
0025 #define RR_CONNECTOR_TYPE_PANEL "Panel"
0026 
0027 Q_DECLARE_LOGGING_CATEGORY(COLORD)
0028 
0029 Output::Output(RROutput output, XRRScreenResources *resources)
0030     : m_output(output)
0031     , m_resources(resources)
0032 {
0033     XRROutputInfo *info;
0034     info = XRRGetOutputInfo(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), m_resources, m_output);
0035     if (!info) {
0036         return;
0037     }
0038 
0039     // store if RROutput is connected and active
0040     m_active = info->connection == RR_Connected && info->crtc != None;
0041 
0042     // store output name
0043     m_name = info->name;
0044 
0045     // store the crtc
0046     m_crtc = info->crtc;
0047 
0048     XRRFreeOutputInfo(info);
0049 
0050     // The ConnectorType property is present in RANDR 1.3 and greater
0051     if (connectorType() == QLatin1String(RR_CONNECTOR_TYPE_PANEL)) {
0052         m_isLaptop = true;
0053     } else if (m_name.contains(QLatin1String("lvds"), Qt::CaseInsensitive) ||
0054                // Most drivers use an "LVDS" prefix...
0055                m_name.contains(QLatin1String("LCD"), Qt::CaseInsensitive) ||
0056                // ... but fglrx uses "LCD" in some versions.  Shoot me now, kthxbye.
0057                m_name.contains(QLatin1String("eDP"), Qt::CaseInsensitive)
0058                /* eDP is for internal laptop panel connections */) {
0059         // Older versions of RANDR - this is a best guess,
0060         // as @#$% RANDR doesn't have standard output names,
0061         // so drivers can use whatever they like.
0062         m_isLaptop = true;
0063     }
0064 }
0065 
0066 Output::~Output()
0067 {
0068     delete m_interface;
0069 }
0070 
0071 bool Output::isActive() const
0072 {
0073     return m_active;
0074 }
0075 
0076 bool Output::isLaptop() const
0077 {
0078     return m_isLaptop;
0079 }
0080 
0081 bool Output::isPrimary(bool hasXRandR13, Window root) const
0082 {
0083     if (hasXRandR13) {
0084         RROutput primary = XRRGetOutputPrimary(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), root);
0085         return primary == m_output;
0086     }
0087     return false;
0088 }
0089 
0090 QString Output::name() const
0091 {
0092     return m_name;
0093 }
0094 
0095 QString Output::id() const
0096 {
0097     return m_id;
0098 }
0099 
0100 void Output::setPath(const QDBusObjectPath &path)
0101 {
0102     if (m_interface && m_interface->path() == path.path()) {
0103         return;
0104     }
0105     m_path = path;
0106 
0107     delete m_interface;
0108     m_interface = new CdDeviceInterface(QStringLiteral("org.freedesktop.ColorManager"), path.path(), QDBusConnection::systemBus());
0109     if (!m_interface->isValid()) {
0110         qCWarning(COLORD) << "Invalid interface" << path.path() << m_interface->lastError().message();
0111         delete m_interface;
0112         m_interface = nullptr;
0113     }
0114 }
0115 
0116 QDBusObjectPath Output::path() const
0117 {
0118     return m_path;
0119 }
0120 
0121 CdDeviceInterface *Output::interface()
0122 {
0123     return m_interface;
0124 }
0125 
0126 RRCrtc Output::crtc() const
0127 {
0128     return m_crtc;
0129 }
0130 
0131 RROutput Output::output() const
0132 {
0133     return m_output;
0134 }
0135 
0136 int Output::getGammaSize() const
0137 {
0138     // The gama size of this output
0139     return XRRGetCrtcGammaSize(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), m_crtc);
0140 }
0141 
0142 void Output::setGamma(XRRCrtcGamma *gamma)
0143 {
0144     XRRSetCrtcGamma(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), m_crtc, gamma);
0145 }
0146 
0147 Edid Output::readEdidData()
0148 {
0149     // get the EDID
0150     size_t size;
0151     const quint8 *data;
0152     data = readEdidData(size);
0153     if (data == nullptr) {
0154         qCWarning(COLORD) << "Unable to get EDID for output" << name();
0155         Edid ret;
0156         m_id = ret.deviceId(name());
0157         return ret;
0158     }
0159 
0160     // With EDID data we can parse our info
0161     Edid edid(data, size);
0162     m_edidHash = edid.hash();
0163     m_id = edid.deviceId(name());
0164     delete[] data;
0165 
0166     return edid;
0167 }
0168 
0169 QString Output::edidHash() const
0170 {
0171     return m_edidHash;
0172 }
0173 
0174 QString Output::connectorType() const
0175 {
0176     unsigned char *prop;
0177     int actual_format;
0178     unsigned long nitems, bytes_after;
0179     Atom actual_type;
0180     Atom connector_type;
0181     Atom connector_type_atom = XInternAtom(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), "ConnectorType", false);
0182     char *connector_type_str;
0183     QString result;
0184 
0185     XRRGetOutputProperty(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(),
0186                          m_output,
0187                          connector_type_atom,
0188                          0,
0189                          100,
0190                          false,
0191                          false,
0192                          AnyPropertyType,
0193                          &actual_type,
0194                          &actual_format,
0195                          &nitems,
0196                          &bytes_after,
0197                          &prop);
0198     if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1)) {
0199         XFree(prop);
0200         return result;
0201     }
0202 
0203     connector_type = *((Atom *)prop);
0204 
0205     connector_type_str = XGetAtomName(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), connector_type);
0206     if (connector_type_str) {
0207         result = connector_type_str;
0208         XFree(connector_type_str);
0209     }
0210 
0211     XFree(prop);
0212 
0213     return result;
0214 }
0215 
0216 /* This is what gnome-desktop does */
0217 static quint8 *getProperty(Display *dpy, RROutput output, Atom atom, size_t &len)
0218 {
0219     unsigned char *prop;
0220     int actual_format;
0221     unsigned long nitems, bytes_after;
0222     Atom actual_type;
0223     quint8 *result;
0224 
0225     XRRGetOutputProperty(dpy, output, atom, 0, 100, false, false, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
0226     if (actual_type == XA_INTEGER && actual_format == 8) {
0227         result = new quint8[nitems];
0228         memcpy(result, prop, nitems);
0229         len = nitems;
0230     } else {
0231         result = nullptr;
0232     }
0233 
0234     XFree(prop);
0235     return result;
0236 }
0237 
0238 quint8 *Output::readEdidData(size_t &len)
0239 {
0240     Atom edid_atom;
0241     quint8 *result;
0242 
0243     edid_atom = XInternAtom(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), RR_PROPERTY_RANDR_EDID, false);
0244     result = getProperty(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), m_output, edid_atom, len);
0245 
0246     if (result) {
0247         if (len % 128 == 0) {
0248             return result;
0249         } else {
0250             len = 0;
0251             delete[] result;
0252         }
0253     }
0254 
0255     return nullptr;
0256 }
0257 
0258 bool Output::operator==(const Output &output) const
0259 {
0260     return m_output == output.output();
0261 }
0262 
0263 #include "moc_Output.cpp"