File indexing completed on 2024-04-21 16:17:31
0001 /* 0002 * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "xcbeventlistener.h" 0008 0009 #include "../xcbwrapper.h" 0010 0011 #include <QGuiApplication> 0012 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0013 #include <private/qtx11extras_p.h> 0014 #else 0015 #include <QX11Info> 0016 #endif 0017 0018 Q_LOGGING_CATEGORY(KSCREEN_XCB_HELPER, "kscreen.xcb.helper") 0019 0020 XCBEventListener::XCBEventListener() 0021 : m_isRandrPresent(false) 0022 , m_randrBase(0) 0023 , m_randrErrorBase(0) 0024 , m_majorOpcode(0) 0025 , m_versionMajor(0) 0026 , m_versionMinor(0) 0027 , m_window(0) 0028 { 0029 xcb_connection_t *c = QX11Info::connection(); 0030 xcb_prefetch_extension_data(c, &xcb_randr_id); 0031 auto cookie = xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); 0032 const auto *queryExtension = xcb_get_extension_data(c, &xcb_randr_id); 0033 if (!queryExtension) { 0034 qCDebug(KSCREEN_XCB_HELPER) << "Fail to query for xrandr extension"; 0035 return; 0036 } 0037 if (!queryExtension->present) { 0038 qCDebug(KSCREEN_XCB_HELPER) << "XRandR extension is not present at all"; 0039 return; 0040 } 0041 0042 m_isRandrPresent = queryExtension->present; 0043 m_randrBase = queryExtension->first_event; 0044 m_randrErrorBase = queryExtension->first_error; 0045 m_majorOpcode = queryExtension->major_opcode; 0046 0047 xcb_generic_error_t *error = nullptr; 0048 auto *versionReply = xcb_randr_query_version_reply(c, cookie, &error); 0049 Q_ASSERT_X(versionReply, "xrandrxcbhelper", "Query to fetch xrandr version failed"); 0050 if (error) { 0051 qFatal("Error while querying for xrandr version: %d", error->error_code); 0052 } 0053 m_versionMajor = versionReply->major_version; 0054 m_versionMinor = versionReply->minor_version; 0055 free(versionReply); 0056 0057 qCDebug(KSCREEN_XCB_HELPER).nospace() << "Detected XRandR " << m_versionMajor << "." << m_versionMinor; 0058 qCDebug(KSCREEN_XCB_HELPER) << "Event Base: " << m_randrBase; 0059 qCDebug(KSCREEN_XCB_HELPER) << "Event Error: " << m_randrErrorBase; 0060 0061 uint32_t rWindow = QX11Info::appRootWindow(); 0062 m_window = xcb_generate_id(c); 0063 xcb_create_window(c, XCB_COPY_FROM_PARENT, m_window, rWindow, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr); 0064 0065 xcb_randr_select_input(c, 0066 m_window, 0067 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE 0068 | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); 0069 0070 qApp->installNativeEventFilter(this); 0071 } 0072 0073 XCBEventListener::~XCBEventListener() 0074 { 0075 if (m_window && QX11Info::connection()) { 0076 xcb_destroy_window(QX11Info::connection(), m_window); 0077 } 0078 } 0079 0080 QString XCBEventListener::rotationToString(xcb_randr_rotation_t rotation) 0081 { 0082 switch (rotation) { 0083 case XCB_RANDR_ROTATION_ROTATE_0: 0084 return QStringLiteral("Rotate_0"); 0085 case XCB_RANDR_ROTATION_ROTATE_90: 0086 return QStringLiteral("Rotate_90"); 0087 case XCB_RANDR_ROTATION_ROTATE_180: 0088 return QStringLiteral("Rotate_180"); 0089 case XCB_RANDR_ROTATION_ROTATE_270: 0090 return QStringLiteral("Rotate_270"); 0091 case XCB_RANDR_ROTATION_REFLECT_X: 0092 return QStringLiteral("Reflect_X"); 0093 case XCB_RANDR_ROTATION_REFLECT_Y: 0094 return QStringLiteral("Reflect_Y"); 0095 } 0096 0097 return QStringLiteral("invalid value (%1)").arg(rotation); 0098 } 0099 0100 QString XCBEventListener::connectionToString(xcb_randr_connection_t connection) 0101 { 0102 switch (connection) { 0103 case XCB_RANDR_CONNECTION_CONNECTED: 0104 return QStringLiteral("Connected"); 0105 case XCB_RANDR_CONNECTION_DISCONNECTED: 0106 return QStringLiteral("Disconnected"); 0107 case XCB_RANDR_CONNECTION_UNKNOWN: 0108 return QStringLiteral("UnknownConnection"); 0109 } 0110 0111 return QStringLiteral("invalid value (%1)").arg(connection); 0112 } 0113 0114 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0115 bool XCBEventListener::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) 0116 #else 0117 bool XCBEventListener::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) 0118 #endif 0119 { 0120 Q_UNUSED(result); 0121 0122 if (eventType != "xcb_generic_event_t") { 0123 return false; 0124 } 0125 0126 auto *e = static_cast<xcb_generic_event_t *>(message); 0127 const uint8_t xEventType = e->response_type & ~0x80; 0128 0129 // If this event is not xcb_randr_notify, we don't want it 0130 if (xEventType == m_randrBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { 0131 handleScreenChange(e); 0132 } 0133 if (xEventType == m_randrBase + XCB_RANDR_NOTIFY) { 0134 handleXRandRNotify(e); 0135 } 0136 0137 return false; 0138 } 0139 0140 void XCBEventListener::handleScreenChange(xcb_generic_event_t *e) 0141 { 0142 auto *e2 = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(e); 0143 0144 // Only accept notifications for our window 0145 if (e2->request_window != m_window) { 0146 return; 0147 } 0148 0149 qCDebug(KSCREEN_XCB_HELPER) << "RRScreenChangeNotify"; 0150 qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << e2->timestamp; 0151 qCDebug(KSCREEN_XCB_HELPER) << "\tConfig_timestamp: " << e2->config_timestamp; 0152 qCDebug(KSCREEN_XCB_HELPER) << "\tWindow:" << e2->request_window; 0153 qCDebug(KSCREEN_XCB_HELPER) << "\tRoot:" << e2->root; 0154 qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)e2->rotation); 0155 qCDebug(KSCREEN_XCB_HELPER) << "\tSize ID:" << e2->sizeID; 0156 qCDebug(KSCREEN_XCB_HELPER) << "\tSize: " << e2->width << e2->height; 0157 qCDebug(KSCREEN_XCB_HELPER) << "\tSizeMM: " << e2->mwidth << e2->mheight; 0158 0159 Q_EMIT screenChanged((xcb_randr_rotation_t)e2->rotation, QSize(e2->width, e2->height), QSize(e2->mwidth, e2->mheight)); 0160 Q_EMIT outputsChanged(); 0161 } 0162 0163 void XCBEventListener::handleXRandRNotify(xcb_generic_event_t *e) 0164 { 0165 auto *randrEvent = reinterpret_cast<xcb_randr_notify_event_t *>(e); 0166 0167 if (randrEvent->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) { 0168 xcb_randr_crtc_change_t crtc = randrEvent->u.cc; 0169 qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_CrtcChange"; 0170 qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << crtc.timestamp; 0171 qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << crtc.crtc; 0172 qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << crtc.mode; 0173 qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)crtc.rotation); 0174 qCDebug(KSCREEN_XCB_HELPER) << "\tGeometry: " << crtc.x << crtc.y << crtc.width << crtc.height; 0175 Q_EMIT crtcChanged(crtc.crtc, crtc.mode, (xcb_randr_rotation_t)crtc.rotation, QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.timestamp); 0176 0177 } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) { 0178 xcb_randr_output_change_t output = randrEvent->u.oc; 0179 qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputChange"; 0180 qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << output.timestamp; 0181 qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << output.output; 0182 qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << output.crtc; 0183 qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << output.mode; 0184 qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)output.rotation); 0185 qCDebug(KSCREEN_XCB_HELPER) << "\tConnection: " << connectionToString((xcb_randr_connection_t)output.connection); 0186 qCDebug(KSCREEN_XCB_HELPER) << "\tSubpixel Order: " << output.subpixel_order; 0187 Q_EMIT outputChanged(output.output, output.crtc, output.mode, (xcb_randr_connection_t)output.connection); 0188 0189 } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_PROPERTY) { 0190 xcb_randr_output_property_t property = randrEvent->u.op; 0191 0192 XCB::ScopedPointer<xcb_get_atom_name_reply_t> reply( 0193 xcb_get_atom_name_reply(QX11Info::connection(), xcb_get_atom_name(QX11Info::connection(), property.atom), nullptr)); 0194 0195 qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputProperty (ignored)"; 0196 qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << property.timestamp; 0197 qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << property.output; 0198 qCDebug(KSCREEN_XCB_HELPER) << "\tProperty: " << xcb_get_atom_name_name(reply.data()); 0199 qCDebug(KSCREEN_XCB_HELPER) << "\tState (newValue, Deleted): " << property.status; 0200 } 0201 }