GCC Code Coverage Report


Directory: ./
File: backends/xrandr/xcbeventlistener.cpp
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 0 124 0.0%
Branches: 0 95 0.0%

Line Branch Exec Source
1 /*************************************************************************************
2 * Copyright 2012, 2013 Daniel Vrátil <dvratil@redhat.com> *
3 * *
4 * This library is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with this library; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
17 *************************************************************************************/
18 #include "xcbeventlistener.h"
19
20 #include "xrandr_logging.h"
21
22 #include <QGuiApplication>
23 #include <QRect>
24 #include <QtGui/private/qtx11extras_p.h>
25
26 XCBEventListener::XCBEventListener()
27 : m_isRandrPresent(false)
28 , m_randrBase(0)
29 , m_randrErrorBase(0)
30 , m_majorOpcode(0)
31 , m_versionMajor(0)
32 , m_versionMinor(0)
33 , m_window(0)
34 {
35 xcb_connection_t* c = QX11Info::connection();
36 xcb_prefetch_extension_data(c, &xcb_randr_id);
37 auto cookie = xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION);
38 const auto* queryExtension = xcb_get_extension_data(c, &xcb_randr_id);
39 if (!queryExtension) {
40 qCDebug(DISMAN_XRANDR) << "Fail to query for xrandr extension";
41 return;
42 }
43 if (!queryExtension->present) {
44 qCDebug(DISMAN_XRANDR) << "XRandR extension is not present at all";
45 return;
46 }
47
48 m_isRandrPresent = queryExtension->present;
49 m_randrBase = queryExtension->first_event;
50 m_randrErrorBase = queryExtension->first_error;
51 m_majorOpcode = queryExtension->major_opcode;
52
53 xcb_generic_error_t* error = nullptr;
54 auto* versionReply = xcb_randr_query_version_reply(c, cookie, &error);
55 Q_ASSERT_X(versionReply, "xrandrxcbhelper", "Query to fetch xrandr version failed");
56 if (error) {
57 qFatal("Error while querying for xrandr version: %d", error->error_code);
58 }
59 m_versionMajor = versionReply->major_version;
60 m_versionMinor = versionReply->minor_version;
61 free(versionReply);
62
63 qCDebug(DISMAN_XRANDR).nospace()
64 << "Detected XRandR " << m_versionMajor << "." << m_versionMinor;
65 qCDebug(DISMAN_XRANDR) << "Event Base: " << m_randrBase;
66 qCDebug(DISMAN_XRANDR) << "Event Error: " << m_randrErrorBase;
67
68 uint32_t rWindow = QX11Info::appRootWindow();
69 m_window = xcb_generate_id(c);
70 xcb_create_window(c,
71 XCB_COPY_FROM_PARENT,
72 m_window,
73 rWindow,
74 0,
75 0,
76 1,
77 1,
78 0,
79 XCB_COPY_FROM_PARENT,
80 XCB_COPY_FROM_PARENT,
81 0,
82 nullptr);
83
84 xcb_randr_select_input(c,
85 m_window,
86 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE
87 | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE
88 | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
89
90 qApp->installNativeEventFilter(this);
91 }
92
93 XCBEventListener::~XCBEventListener()
94 {
95 if (m_window && QX11Info::connection()) {
96 xcb_destroy_window(QX11Info::connection(), m_window);
97 }
98 }
99
100 QString XCBEventListener::rotationToString(xcb_randr_rotation_t rotation)
101 {
102 switch (rotation) {
103 case XCB_RANDR_ROTATION_ROTATE_0:
104 return QStringLiteral("Rotate_0");
105 case XCB_RANDR_ROTATION_ROTATE_90:
106 return QStringLiteral("Rotate_90");
107 case XCB_RANDR_ROTATION_ROTATE_180:
108 return QStringLiteral("Rotate_180");
109 case XCB_RANDR_ROTATION_ROTATE_270:
110 return QStringLiteral("Rotate_270");
111 case XCB_RANDR_ROTATION_REFLECT_X:
112 return QStringLiteral("Reflect_X");
113 case XCB_RANDR_ROTATION_REFLECT_Y:
114 return QStringLiteral("Reflect_Y");
115 }
116
117 return QStringLiteral("invalid value (%1)").arg(rotation);
118 }
119
120 QString XCBEventListener::connectionToString(xcb_randr_connection_t connection)
121 {
122 switch (connection) {
123 case XCB_RANDR_CONNECTION_CONNECTED:
124 return QStringLiteral("Connected");
125 case XCB_RANDR_CONNECTION_DISCONNECTED:
126 return QStringLiteral("Disconnected");
127 case XCB_RANDR_CONNECTION_UNKNOWN:
128 return QStringLiteral("UnknownConnection");
129 }
130
131 return QStringLiteral("invalid value (%1)").arg(connection);
132 }
133
134 bool XCBEventListener::nativeEventFilter(const QByteArray& eventType,
135 void* message,
136 qintptr* /*result*/)
137 {
138 if (eventType != "xcb_generic_event_t") {
139 return false;
140 }
141
142 auto* e = static_cast<xcb_generic_event_t*>(message);
143 const uint8_t xEventType = e->response_type & ~0x80;
144
145 // If this event is not xcb_randr_notify, we don't want it
146 if (xEventType == m_randrBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
147 handleScreenChange(e);
148 }
149 if (xEventType == m_randrBase + XCB_RANDR_NOTIFY) {
150 handleXRandRNotify(e);
151 }
152
153 return false;
154 }
155
156 void XCBEventListener::handleScreenChange(xcb_generic_event_t* e)
157 {
158 auto* e2 = reinterpret_cast<xcb_randr_screen_change_notify_event_t*>(e);
159
160 // Only accept notifications for our window
161 if (e2->request_window != m_window) {
162 return;
163 }
164
165 qCDebug(DISMAN_XRANDR) << "RRScreenChangeNotify";
166 qCDebug(DISMAN_XRANDR) << "\tWindow:" << e2->request_window;
167 qCDebug(DISMAN_XRANDR) << "\tRoot:" << e2->root;
168 qCDebug(DISMAN_XRANDR) << "\tRotation: "
169 << rotationToString((xcb_randr_rotation_t)e2->rotation);
170 qCDebug(DISMAN_XRANDR) << "\tSize ID:" << e2->sizeID;
171 qCDebug(DISMAN_XRANDR) << "\tSize: " << e2->width << e2->height;
172 qCDebug(DISMAN_XRANDR) << "\tPhysical size: " << e2->mwidth << e2->mheight;
173
174 Q_EMIT screenChanged((xcb_randr_rotation_t)e2->rotation,
175 QSize(e2->width, e2->height),
176 QSize(e2->mwidth, e2->mheight));
177 }
178
179 void XCBEventListener::handleXRandRNotify(xcb_generic_event_t* e)
180 {
181 auto* randrEvent = reinterpret_cast<xcb_randr_notify_event_t*>(e);
182
183 if (randrEvent->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
184 xcb_randr_crtc_change_t crtc = randrEvent->u.cc;
185 qCDebug(DISMAN_XRANDR) << "RRNotify_CrtcChange";
186 qCDebug(DISMAN_XRANDR) << "\tCRTC: " << crtc.crtc;
187 qCDebug(DISMAN_XRANDR) << "\tMode: " << crtc.mode;
188 qCDebug(DISMAN_XRANDR) << "\tRotation: "
189 << rotationToString((xcb_randr_rotation_t)crtc.rotation);
190 qCDebug(DISMAN_XRANDR) << "\tGeometry: " << crtc.x << crtc.y << crtc.width << crtc.height;
191 Q_EMIT crtcChanged(crtc.crtc,
192 crtc.mode,
193 (xcb_randr_rotation_t)crtc.rotation,
194 QRect(crtc.x, crtc.y, crtc.width, crtc.height));
195
196 } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
197 xcb_randr_output_change_t output = randrEvent->u.oc;
198 qCDebug(DISMAN_XRANDR) << "RRNotify_OutputChange";
199 qCDebug(DISMAN_XRANDR) << "\tOutput: " << output.output;
200 qCDebug(DISMAN_XRANDR) << "\tCRTC: " << output.crtc;
201 qCDebug(DISMAN_XRANDR) << "\tMode: " << output.mode;
202 qCDebug(DISMAN_XRANDR) << "\tRotation: "
203 << rotationToString((xcb_randr_rotation_t)output.rotation);
204 qCDebug(DISMAN_XRANDR) << "\tConnection: "
205 << connectionToString((xcb_randr_connection_t)output.connection);
206 qCDebug(DISMAN_XRANDR) << "\tSubpixel Order: " << output.subpixel_order;
207 Q_EMIT outputChanged(
208 output.output, output.crtc, output.mode, (xcb_randr_connection_t)output.connection);
209
210 } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_PROPERTY) {
211 xcb_randr_output_property_t property = randrEvent->u.op;
212
213 XCB::ScopedPointer<xcb_get_atom_name_reply_t> reply(
214 xcb_get_atom_name_reply(QX11Info::connection(),
215 xcb_get_atom_name(QX11Info::connection(), property.atom),
216 nullptr));
217
218 qCDebug(DISMAN_XRANDR) << "RRNotify_OutputProperty (ignored)";
219 qCDebug(DISMAN_XRANDR) << "\tOutput: " << property.output;
220 qCDebug(DISMAN_XRANDR) << "\tProperty: " << xcb_get_atom_name_name(reply.data());
221 qCDebug(DISMAN_XRANDR) << "\tState (newValue, Deleted): " << property.status;
222 }
223 }
224