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 }