File indexing completed on 2024-05-05 17:39:39

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include "xrandr11.h"
0008 
0009 #include "../xcbeventlistener.h"
0010 #include "../xcbwrapper.h"
0011 
0012 #include "config.h"
0013 #include "mode.h"
0014 #include "output.h"
0015 #include "screen.h"
0016 
0017 #include <xcb/randr.h>
0018 
0019 #include <QDebug>
0020 #include <QString>
0021 
0022 Q_LOGGING_CATEGORY(KSCREEN_XRANDR11, "kscreen.xrandr11")
0023 
0024 XRandR11::XRandR11()
0025     : KScreen::AbstractBackend()
0026     , m_valid(false)
0027     , m_x11Helper(nullptr)
0028     , m_currentConfig(new KScreen::Config)
0029     , m_currentTimestamp(0)
0030 {
0031     xcb_generic_error_t *error = nullptr;
0032     xcb_randr_query_version_reply_t *version;
0033     version = xcb_randr_query_version_reply(XCB::connection(), //
0034                                             xcb_randr_query_version(XCB::connection(), XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION),
0035                                             &error);
0036 
0037     if (!version || error) {
0038         free(error);
0039         XCB::closeConnection();
0040         qCDebug(KSCREEN_XRANDR11) << "Can't get XRandR version";
0041         return;
0042     }
0043     if (version->major_version != 1 || version->minor_version != 1) {
0044         XCB::closeConnection();
0045         qCDebug(KSCREEN_XRANDR11) << "This backend is only for XRandR 1.1, your version is: " << version->major_version << "." << version->minor_version;
0046         return;
0047     }
0048 
0049     m_x11Helper = new XCBEventListener();
0050 
0051     connect(m_x11Helper, &XCBEventListener::outputsChanged, this, &XRandR11::updateConfig);
0052 
0053     m_valid = true;
0054 }
0055 
0056 XRandR11::~XRandR11()
0057 {
0058     XCB::closeConnection();
0059     delete m_x11Helper;
0060 }
0061 
0062 QString XRandR11::name() const
0063 {
0064     return QStringLiteral("XRandR 1.1");
0065 }
0066 
0067 QString XRandR11::serviceName() const
0068 {
0069     return QStringLiteral("org.kde.KScreen.Backend.XRandR11");
0070 }
0071 
0072 bool XRandR11::isValid() const
0073 {
0074     return m_valid;
0075 }
0076 
0077 KScreen::ConfigPtr XRandR11::config() const
0078 {
0079     KScreen::ConfigPtr config(new KScreen::Config);
0080     auto features = KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay;
0081     config->setSupportedFeatures(features);
0082 
0083     const int screenId = QX11Info::appScreen();
0084     xcb_screen_t *xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId);
0085     const XCB::ScreenInfo info(xcbScreen->root);
0086     const XCB::ScreenSize size(xcbScreen->root);
0087 
0088     if (info->config_timestamp == m_currentTimestamp) {
0089         return m_currentConfig;
0090     }
0091 
0092     KScreen::ScreenPtr screen(new KScreen::Screen);
0093     screen->setId(screenId);
0094     screen->setCurrentSize(QSize(xcbScreen->width_in_pixels, xcbScreen->height_in_pixels));
0095     if (size) { // RRGetScreenSize may file on VNC/RDP connections
0096         screen->setMaxSize(QSize(size->max_width, size->max_height));
0097         screen->setMinSize(QSize(size->min_width, size->min_height));
0098     } else {
0099         screen->setMaxSize(screen->currentSize());
0100         screen->setMinSize(screen->currentSize());
0101     }
0102     screen->setMaxActiveOutputsCount(1);
0103 
0104     config->setScreen(screen);
0105 
0106     KScreen::OutputList outputs;
0107     KScreen::OutputPtr output(new KScreen::Output);
0108     output->setId(1);
0109 
0110     output->setConnected(true);
0111     output->setEnabled(true);
0112     output->setName(QStringLiteral("Default"));
0113     output->setPos(QPoint(0, 0));
0114     output->setPriority(1);
0115     output->setRotation((KScreen::Output::Rotation)info->rotation);
0116     output->setSizeMm(QSize(xcbScreen->width_in_millimeters, xcbScreen->height_in_millimeters));
0117 
0118     outputs.insert(1, output);
0119     config->setOutputs(outputs);
0120 
0121     KScreen::ModePtr mode;
0122     KScreen::ModeList modes;
0123 
0124     auto iter = xcb_randr_get_screen_info_rates_iterator(info);
0125     xcb_randr_screen_size_t *sizes = xcb_randr_get_screen_info_sizes(info);
0126     for (int x = 0; x < info->nSizes; x++) {
0127         const xcb_randr_screen_size_t size = sizes[x];
0128         const uint16_t *rates = xcb_randr_refresh_rates_rates(iter.data);
0129         const int nrates = xcb_randr_refresh_rates_rates_length(iter.data);
0130 
0131         for (int j = 0; j < nrates; j++) {
0132             float rate = rates[j];
0133             mode = KScreen::ModePtr(new KScreen::Mode);
0134             mode->setId(QStringLiteral("%1-%2").arg(x).arg(j));
0135             mode->setSize(QSize(size.width, size.height));
0136             mode->setRefreshRate(rate);
0137             mode->setName(QStringLiteral("%1x%2").arg(size.width).arg(size.height));
0138 
0139             if (x == info->sizeID && rate == info->rate) {
0140                 output->setCurrentModeId(mode->id());
0141                 output->setSize(mode->size());
0142             }
0143             modes.insert(mode->id(), mode);
0144         }
0145 
0146         xcb_randr_refresh_rates_next(&iter);
0147     }
0148 
0149     output->setModes(modes);
0150     return config;
0151 }
0152 
0153 void XRandR11::setConfig(const KScreen::ConfigPtr &config)
0154 {
0155     const KScreen::OutputPtr output = config->outputs().take(1);
0156     const KScreen::ModePtr mode = output->currentMode();
0157 
0158     const int screenId = QX11Info::appScreen();
0159     xcb_screen_t *xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId);
0160 
0161     const XCB::ScreenInfo info(xcbScreen->root);
0162     xcb_generic_error_t *err;
0163     const int sizeId = mode->id().split(QLatin1Char('-')).first().toInt();
0164     auto cookie = xcb_randr_set_screen_config(XCB::connection(),
0165                                               xcbScreen->root,
0166                                               XCB_CURRENT_TIME,
0167                                               info->config_timestamp,
0168                                               sizeId,
0169                                               (short)output->rotation(),
0170                                               mode->refreshRate());
0171     XCB::ScopedPointer<xcb_randr_set_screen_config_reply_t> reply(xcb_randr_set_screen_config_reply(XCB::connection(), cookie, &err));
0172     if (err) {
0173         free(err);
0174     }
0175 }
0176 
0177 void XRandR11::updateConfig()
0178 {
0179     m_currentConfig = config();
0180     Q_EMIT configChanged(m_currentConfig);
0181 }