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