Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxcbconnection_basic.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include "qxcbbackingstore.h" // for createSystemVShmSegment()
7#include "private/qoffsetstringarray_p.h"
8
9#include <xcb/randr.h>
10#include <xcb/shm.h>
11#include <xcb/sync.h>
12#include <xcb/xfixes.h>
13#include <xcb/render.h>
14#include <xcb/xinput.h>
15#define explicit dont_use_cxx_explicit
16#include <xcb/xkb.h>
17#undef explicit
18
19#if QT_CONFIG(xcb_xlib)
20#define register /* C++17 deprecated register */
21#include <X11/Xlib.h>
22#include <X11/Xlib-xcb.h>
23#include <X11/Xlibint.h>
24#include <X11/Xutil.h>
25#undef register
26#endif
27
29
30Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb")
31
32#if QT_CONFIG(xcb_xlib)
33static constexpr auto xcbConnectionErrors = qOffsetStringArray(
34 "No error", /* Error 0 */
35 "I/O error", /* XCB_CONN_ERROR */
36 "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */
37 "Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */
38 "Maximum allowed requested length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */
39 "Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */
40 "No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */
41 "Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */
42);
43
44static int nullErrorHandler(Display *dpy, XErrorEvent *err)
45{
46#ifndef Q_XCB_DEBUG
47 Q_UNUSED(dpy);
48 Q_UNUSED(err);
49#else
50 const int buflen = 1024;
51 char buf[buflen];
52
53 XGetErrorText(dpy, err->error_code, buf, buflen);
54 fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf);
55#endif
56 return 0;
57}
58
59static int ioErrorHandler(Display *dpy)
60{
61 xcb_connection_t *conn = XGetXCBConnection(dpy);
62 if (conn != nullptr) {
63 /* Print a message with a textual description of the error */
64 int code = xcb_connection_has_error(conn);
65 const char *str = "Unknown error";
66 if (code >= 0 && code < xcbConnectionErrors.count())
67 str = xcbConnectionErrors[code];
68
69 qWarning("The X11 connection broke: %s (code %d)", str, code);
70 }
71 return _XDefaultIOError(dpy);
72}
73#endif
74
75QXcbBasicConnection::QXcbBasicConnection(const char *displayName)
76 : m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
77{
78#if QT_CONFIG(xcb_xlib)
79 Display *dpy = XOpenDisplay(m_displayName.constData());
80 if (dpy) {
81 m_primaryScreenNumber = DefaultScreen(dpy);
82 m_xcbConnection = XGetXCBConnection(dpy);
83 XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
84 XSetErrorHandler(nullErrorHandler);
85 XSetIOErrorHandler(ioErrorHandler);
86 m_xlibDisplay = dpy;
87 }
88#else
89 m_xcbConnection = xcb_connect(m_displayName.constData(), &m_primaryScreenNumber);
90#endif
91 if (Q_UNLIKELY(!isConnected())) {
92 qCWarning(lcQpaXcb, "could not connect to display %s", m_displayName.constData());
93 return;
94 }
95
96 m_setup = xcb_get_setup(m_xcbConnection);
97 m_xcbAtom.initialize(m_xcbConnection);
98 m_maximumRequestLength = xcb_get_maximum_request_length(m_xcbConnection);
99
100 xcb_extension_t *extensions[] = {
101 &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
102 &xcb_render_id, &xcb_xkb_id, &xcb_input_id, nullptr
103 };
104
105 for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it)
106 xcb_prefetch_extension_data (m_xcbConnection, *ext_it);
107
108 initializeXSync();
109 if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM"))
110 initializeShm();
111 if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR"))
112 initializeXRandr();
113 initializeXFixes();
114 initializeXRender();
115 if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2"))
116 initializeXInput2();
117 initializeXShape();
118 initializeXKB();
119}
120
121QXcbBasicConnection::~QXcbBasicConnection()
122{
123 if (isConnected()) {
124#if QT_CONFIG(xcb_xlib)
125 XCloseDisplay(static_cast<Display *>(m_xlibDisplay));
126#else
127 xcb_disconnect(m_xcbConnection);
128#endif
129 }
130}
131
132size_t QXcbBasicConnection::maxRequestDataBytes(size_t requestSize) const
133{
134 if (hasBigRequest())
135 requestSize += 4; // big-request encoding adds 4 bytes
136
137 return m_maximumRequestLength * 4 - requestSize;
138}
139
140xcb_atom_t QXcbBasicConnection::internAtom(const char *name)
141{
142 if (!name || *name == 0)
143 return XCB_NONE;
144
145 auto reply = Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name);
146 if (!reply) {
147 qCDebug(lcQpaXcb) << "failed to query intern atom: " << name;
148 return XCB_NONE;
149 }
150
151 return reply->atom;
152}
153
154QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom)
155{
156 if (!atom)
157 return QByteArray();
158
159 auto reply = Q_XCB_REPLY(xcb_get_atom_name, m_xcbConnection, atom);
160 if (reply)
161 return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
162
163 qCWarning(lcQpaXcb) << "atomName: bad atom" << atom;
164 return QByteArray();
165}
166
167bool QXcbBasicConnection::hasBigRequest() const
168{
169 return m_maximumRequestLength > m_setup->maximum_request_length;
170}
171
172// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
173// - "pad0" became "extension"
174// - "pad1" and "pad" became "pad0"
175// New and old version of this struct share the following fields:
176typedef struct qt_xcb_ge_event_t {
178 uint8_t extension;
179 uint16_t sequence;
180 uint32_t length;
181 uint16_t event_type;
182} qt_xcb_ge_event_t;
183
184bool QXcbBasicConnection::isXIEvent(xcb_generic_event_t *event) const
185{
186 qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
187 return e->extension == m_xiOpCode;
188}
189
190bool QXcbBasicConnection::isXIType(xcb_generic_event_t *event, uint16_t type) const
191{
192 if (!isXIEvent(event))
193 return false;
194
195 auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
196 return e->event_type == type;
197}
198
199bool QXcbBasicConnection::isXFixesType(uint responseType, int eventType) const
200{
201 return m_hasXFixes && responseType == m_xfixesFirstEvent + eventType;
202}
203
204bool QXcbBasicConnection::isXRandrType(uint responseType, int eventType) const
205{
206 return m_hasXRandr && responseType == m_xrandrFirstEvent + eventType;
207}
208
209bool QXcbBasicConnection::isXkbType(uint responseType) const
210{
211 return m_hasXkb && responseType == m_xkbFirstEvent;
212}
213
214void QXcbBasicConnection::initializeXSync()
215{
216 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_sync_id);
217 if (!reply || !reply->present)
218 return;
219
220 m_hasXSync = true;
221}
222
223void QXcbBasicConnection::initializeShm()
224{
225 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shm_id);
226 if (!reply || !reply->present) {
227 qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server");
228 return;
229 }
230
231 auto shmQuery = Q_XCB_REPLY(xcb_shm_query_version, m_xcbConnection);
232 if (!shmQuery) {
233 qCWarning(lcQpaXcb, "failed to request MIT-SHM version");
234 return;
235 }
236
237 m_hasShm = true;
238 m_hasShmFd = (shmQuery->major_version == 1 && shmQuery->minor_version >= 2) ||
239 shmQuery->major_version > 1;
240
241 qCDebug(lcQpaXcb) << "Has MIT-SHM :" << m_hasShm;
242 qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << m_hasShmFd;
243
244 // Temporary disable warnings (unless running in debug mode).
245 auto logging = const_cast<QLoggingCategory*>(&lcQpaXcb());
246 bool wasEnabled = logging->isEnabled(QtMsgType::QtWarningMsg);
247 if (!logging->isEnabled(QtMsgType::QtDebugMsg))
248 logging->setEnabled(QtMsgType::QtWarningMsg, false);
249 if (!QXcbBackingStore::createSystemVShmSegment(m_xcbConnection)) {
250 qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote "
251 "X11 connection?), disabling SHM");
252 m_hasShm = m_hasShmFd = false;
253 }
254 if (wasEnabled)
255 logging->setEnabled(QtMsgType::QtWarningMsg, true);
256}
257
258void QXcbBasicConnection::initializeXRender()
259{
260 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_render_id);
261 if (!reply || !reply->present) {
262 qCDebug(lcQpaXcb, "XRender extension not present on the X server");
263 return;
264 }
265
266 auto xrenderQuery = Q_XCB_REPLY(xcb_render_query_version, m_xcbConnection,
267 XCB_RENDER_MAJOR_VERSION,
268 XCB_RENDER_MINOR_VERSION);
269 if (!xrenderQuery) {
270 qCWarning(lcQpaXcb, "xcb_render_query_version failed");
271 return;
272 }
273
274 m_hasXRender = true;
275 m_xrenderVersion.first = xrenderQuery->major_version;
276 m_xrenderVersion.second = xrenderQuery->minor_version;
277}
278
279void QXcbBasicConnection::initializeXFixes()
280{
281 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xfixes_id);
282 if (!reply || !reply->present)
283 return;
284
285 auto xfixesQuery = Q_XCB_REPLY(xcb_xfixes_query_version, m_xcbConnection,
286 XCB_XFIXES_MAJOR_VERSION,
287 XCB_XFIXES_MINOR_VERSION);
288 if (!xfixesQuery || xfixesQuery->major_version < 2) {
289 qCWarning(lcQpaXcb, "failed to initialize XFixes");
290 return;
291 }
292
293 m_hasXFixes = true;
294 m_xfixesFirstEvent = reply->first_event;
295}
296
297void QXcbBasicConnection::initializeXRandr()
298{
299 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_randr_id);
300 if (!reply || !reply->present)
301 return;
302
303 auto xrandrQuery = Q_XCB_REPLY(xcb_randr_query_version, m_xcbConnection,
304 XCB_RANDR_MAJOR_VERSION,
305 XCB_RANDR_MINOR_VERSION);
306 if (!xrandrQuery || (xrandrQuery->major_version < 1 ||
307 (xrandrQuery->major_version == 1 && xrandrQuery->minor_version < 2))) {
308 qCWarning(lcQpaXcb, "failed to initialize XRandr 1.2");
309 return;
310 }
311
312 m_hasXRandr = true;
313
314 m_xrandr1Minor = xrandrQuery->minor_version;
315
316 m_xrandrFirstEvent = reply->first_event;
317}
318
319void QXcbBasicConnection::initializeXInput2()
320{
321 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_input_id);
322 if (!reply || !reply->present) {
323 qCDebug(lcQpaXcb, "XInput extension is not present on the X server");
324 return;
325 }
326
327 // depending on whether bundled xcb is used we may support different XCB protocol versions.
328 auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection,
329 2, XCB_INPUT_MINOR_VERSION);
330 if (!xinputQuery || xinputQuery->major_version != 2) {
331 qCWarning(lcQpaXcb, "X server does not support XInput 2");
332 return;
333 }
334
335 qCDebug(lcQpaXcb, "Using XInput version %d.%d",
336 xinputQuery->major_version, xinputQuery->minor_version);
337
338 m_xi2Enabled = true;
339 m_xiOpCode = reply->major_opcode;
340 m_xinputFirstEvent = reply->first_event;
341 m_xi2Minor = xinputQuery->minor_version;
342}
343
344void QXcbBasicConnection::initializeXShape()
345{
346 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shape_id);
347 if (!reply || !reply->present)
348 return;
349
350 m_hasXhape = true;
351
352 auto shapeQuery = Q_XCB_REPLY(xcb_shape_query_version, m_xcbConnection);
353 if (!shapeQuery) {
354 qCWarning(lcQpaXcb, "failed to initialize XShape extension");
355 return;
356 }
357
358 if (shapeQuery->major_version > 1 || (shapeQuery->major_version == 1 && shapeQuery->minor_version >= 1)) {
359 // The input shape is the only thing added in SHAPE 1.1
360 m_hasInputShape = true;
361 }
362}
363
364void QXcbBasicConnection::initializeXKB()
365{
366 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xkb_id);
367 if (!reply || !reply->present) {
368 qCWarning(lcQpaXcb, "XKeyboard extension not present on the X server");
369 return;
370 }
371
372 int wantMajor = 1;
373 int wantMinor = 0;
374 auto xkbQuery = Q_XCB_REPLY(xcb_xkb_use_extension, m_xcbConnection, wantMajor, wantMinor);
375 if (!xkbQuery) {
376 qCWarning(lcQpaXcb, "failed to initialize XKeyboard extension");
377 return;
378 }
379 if (!xkbQuery->supported) {
380 qCWarning(lcQpaXcb, "unsupported XKB version (we want %d.%d, but X server has %d.%d)",
381 wantMajor, wantMinor, xkbQuery->serverMajor, xkbQuery->serverMinor);
382 return;
383 }
384
385 m_hasXkb = true;
386 m_xkbFirstEvent = reply->first_event;
387}
388
389QT_END_NAMESPACE
390
391#include "moc_qxcbconnection_basic.cpp"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define Q_XCB_REPLY(call,...)