1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <QtGui/private/qguiapplication_p.h>
43 #include <QtCore/QDebug>
45 #include "qxcbconnection.h"
46 #include "qxcbkeyboard.h"
47 #include "qxcbscreen.h"
48 #include "qxcbwindow.h"
49 #include "qxcbclipboard.h"
51 #include "qxcbwmsupport.h"
52 #include "qxcbnativeinterface.h"
53 #include "qxcbintegration.h"
55 #include <QtAlgorithms>
56 #include <QSocketNotifier>
57 #include <QAbstractEventDispatcher>
66 #include <xcb/xfixes.h>
70 #include <X11/Xlib-xcb.h>
71 #include <X11/Xlibint.h>
74 #if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
75 #include <X11/extensions/XInput2.h>
76 #include <X11/extensions/XI2proto.h>
80 #include <xcb/render.h>
83 #if defined(XCB_HAS_XCB_GLX)
87 #ifdef XCB_USE_EGL //don't pull in eglext prototypes
94 static int nullErrorHandler(Display *, XErrorEvent *)
100 QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& newScreens,
101 int screenNumber, xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output)
105 name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output),
106 xcb_randr_get_output_info_name_length(output));
108 QByteArray displayName = m_displayName;
109 int dotPos = displayName.lastIndexOf('.');
111 displayName.truncate(dotPos);
112 name = displayName + QLatin1Char('.') + QString::number(screenNumber);
114 foreach (QXcbScreen* scr, m_screens)
115 if (scr->name() == name && scr->root() == xcbScreen->root)
117 QXcbScreen *ret = new QXcbScreen(this, xcbScreen, output, name, screenNumber);
123 \brief Synchronizes the screen list, adds new screens, removes deleted ones
125 void QXcbConnection::updateScreens()
127 xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
128 int screenNumber = 0; // index of this QScreen in QGuiApplication::screens()
129 int xcbScreenNumber = 0; // screen number in the xcb sense
130 QSet<QXcbScreen *> activeScreens;
131 QList<QXcbScreen *> newScreens;
132 QXcbScreen* primaryScreen = NULL;
134 // Each "screen" in xcb terminology is a virtual desktop,
135 // potentially a collection of separate juxtaposed monitors.
136 // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
137 // which will become virtual siblings.
138 xcb_screen_t *xcbScreen = it.data;
139 QList<QPlatformScreen *> siblings;
141 if (has_randr_extension) {
142 xcb_generic_error_t *error = NULL;
143 xcb_randr_get_output_primary_cookie_t primaryCookie =
144 xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root);
145 xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
146 xcb_randr_get_screen_resources_current(xcb_connection(), xcbScreen->root);
147 xcb_randr_get_output_primary_reply_t *primary =
148 xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error);
149 if (!primary || error) {
150 qWarning("QXcbConnection: Failed to get the primary output of the screen");
153 xcb_randr_get_screen_resources_current_reply_t *resources =
154 xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, &error);
155 if (!resources || error) {
156 qWarning("QXcbConnection: Failed to get the screen resources");
159 xcb_timestamp_t timestamp = resources->config_timestamp;
160 outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources);
161 xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(resources);
163 for (int i = 0; i < outputCount; i++) {
164 xcb_randr_get_output_info_reply_t *output =
165 xcb_randr_get_output_info_reply(xcb_connection(),
166 xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL);
171 QString outputName = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output),
172 xcb_randr_get_output_info_name_length(output));
175 if (output->crtc == XCB_NONE) {
177 qDebug("Screen output %s is not connected", qPrintable(outputName));
182 QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen, output);
184 activeScreens << screen;
186 if (!primaryScreen && primary) {
187 if (primary->output == XCB_NONE || outputs[i] == primary->output) {
188 primaryScreen = screen;
189 siblings.prepend(siblings.takeLast());
191 qDebug("Primary output is %d: %s", primary->output, qPrintable(outputName));
202 // If there's no randr extension, or there was some error above, or the screen
203 // doesn't have outputs for some other reason (e.g. on VNC or ssh -X), just assume there is one screen.
204 if (outputCount == 0) {
206 qDebug("Found a screen with zero outputs");
208 QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen);
210 activeScreens << screen;
212 primaryScreen = screen;
215 foreach (QPlatformScreen* s, siblings)
216 ((QXcbScreen*)s)->setVirtualSiblings(siblings);
217 xcb_screen_next(&it);
219 } // for each xcb screen
221 // Now activeScreens is the complete set of screens which are active at this time.
222 // Delete any existing screens which are not in activeScreens
223 for (int i = m_screens.count() - 1; i >= 0; --i) {
224 if (!activeScreens.contains(m_screens[i])) {
225 ((QXcbIntegration*)QGuiApplicationPrivate::platformIntegration())->removeDefaultOpenGLContextInfo(m_screens[i]);
227 m_screens.removeAt(i);
231 // Add any new screens, and make sure the primary screen comes first
232 // since it is used by QGuiApplication::primaryScreen()
233 foreach (QXcbScreen* screen, newScreens) {
234 if (screen == primaryScreen)
235 m_screens.prepend(screen);
237 m_screens.append(screen);
240 // Now that they are in the right order, emit the added signals for new screens only
241 foreach (QXcbScreen* screen, m_screens)
242 if (newScreens.contains(screen))
243 ((QXcbIntegration*)QGuiApplicationPrivate::platformIntegration())->screenAdded(screen);
246 QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char *displayName)
249 , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
250 , m_nativeInterface(nativeInterface)
251 #ifdef XCB_USE_XINPUT2_MAEMO
254 , xfixes_first_event(0)
255 , xrandr_first_event(0)
256 , has_glx_extension(false)
257 , has_shape_extension(false)
258 , has_randr_extension(false)
259 , has_input_shape(false)
262 Display *dpy = XOpenDisplay(m_displayName.constData());
264 m_primaryScreen = DefaultScreen(dpy);
265 m_connection = XGetXCBConnection(dpy);
266 XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
267 XSetErrorHandler(nullErrorHandler);
268 m_xlib_display = dpy;
270 EGLDisplay eglDisplay = eglGetDisplay(dpy);
271 m_egl_display = eglDisplay;
273 eglBindAPI(EGL_OPENGL_ES_API);
274 m_has_egl = eglInitialize(eglDisplay,&major,&minor);
278 m_connection = xcb_connect(m_displayName.constData(), &m_primaryScreen);
279 #endif //XCB_USE_XLIB
281 if (!m_connection || xcb_connection_has_error(m_connection))
282 qFatal("QXcbConnection: Could not connect to display %s", m_displayName.constData());
284 m_reader = new QXcbEventReader(this);
285 connect(m_reader, SIGNAL(eventPending()), this, SLOT(processXcbEvents()), Qt::QueuedConnection);
286 connect(m_reader, SIGNAL(finished()), this, SLOT(processXcbEvents()));
287 if (!m_reader->startThread()) {
288 QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcb_connection()), QSocketNotifier::Read, this);
289 connect(notifier, SIGNAL(activated(int)), this, SLOT(processXcbEvents()));
291 QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher;
292 connect(dispatcher, SIGNAL(aboutToBlock()), this, SLOT(processXcbEvents()));
293 connect(dispatcher, SIGNAL(awake()), this, SLOT(processXcbEvents()));
296 xcb_extension_t *extensions[] = {
297 &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
298 #ifdef XCB_USE_RENDER
301 #ifdef XCB_HAS_XCB_GLX
307 for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it)
308 xcb_prefetch_extension_data (m_connection, *ext_it);
310 m_setup = xcb_get_setup(xcb_connection());
312 initializeAllAtoms();
314 m_time = XCB_CURRENT_TIME;
319 m_connectionEventListener = xcb_generate_id(m_connection);
320 xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
321 m_connectionEventListener, m_screens.at(0)->root(),
322 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
323 m_screens.at(0)->screen()->root_visual, 0, 0);
328 m_xi2Enabled = false;
329 #ifdef XCB_USE_XINPUT2_MAEMO
330 initializeXInput2Maemo();
331 #elif defined(XCB_USE_XINPUT2)
336 m_wmSupport.reset(new QXcbWMSupport(this));
337 m_keyboard = new QXcbKeyboard(this);
338 #ifndef QT_NO_CLIPBOARD
339 m_clipboard = new QXcbClipboard(this);
341 #ifndef QT_NO_DRAGANDDROP
342 m_drag = new QXcbDrag(this);
348 QXcbConnection::~QXcbConnection()
350 #ifndef QT_NO_CLIPBOARD
353 #ifndef QT_NO_DRAGANDDROP
356 // Delete screens in reverse order to avoid crash in case of multiple screens
357 while (!m_screens.isEmpty())
358 delete m_screens.takeLast();
360 #ifdef XCB_USE_XINPUT2_MAEMO
361 finalizeXInput2Maemo();
362 #elif defined(XCB_USE_XINPUT2)
366 if (m_reader->isRunning()) {
367 sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
375 eglTerminate(m_egl_display);
379 XCloseDisplay((Display *)m_xlib_display);
381 xcb_disconnect(xcb_connection());
387 void QXcbConnection::addWindow(xcb_window_t id, QXcbWindow *window)
389 m_mapper.insert(id, window);
392 void QXcbConnection::removeWindow(xcb_window_t id)
397 QXcbWindow *QXcbConnection::platformWindowFromId(xcb_window_t id)
399 return m_mapper.value(id, 0);
402 #define HANDLE_PLATFORM_WINDOW_EVENT(event_t, windowMember, handler) \
404 event_t *e = (event_t *)event; \
405 if (QXcbWindow *platformWindow = platformWindowFromId(e->windowMember)) { \
406 handled = QWindowSystemInterface::handleNativeEvent(platformWindow->window(), m_nativeInterface->genericEventFilterType(), event, &result); \
408 platformWindow->handler(e); \
413 #define HANDLE_KEYBOARD_EVENT(event_t, handler) \
415 event_t *e = (event_t *)event; \
416 if (QXcbWindow *platformWindow = platformWindowFromId(e->event)) { \
417 handled = QWindowSystemInterface::handleNativeEvent(platformWindow->window(), m_nativeInterface->genericEventFilterType(), event, &result); \
419 m_keyboard->handler(platformWindow, e); \
424 //#define XCB_EVENT_DEBUG
426 void printXcbEvent(const char *message, xcb_generic_event_t *event)
428 #ifdef XCB_EVENT_DEBUG
429 #define PRINT_XCB_EVENT(ev) \
431 qDebug("QXcbConnection: %s: %d - %s - sequence: %d", message, int(ev), #ev, event->sequence); \
434 switch (event->response_type & ~0x80) {
435 PRINT_XCB_EVENT(XCB_KEY_PRESS);
436 PRINT_XCB_EVENT(XCB_KEY_RELEASE);
437 PRINT_XCB_EVENT(XCB_BUTTON_PRESS);
438 PRINT_XCB_EVENT(XCB_BUTTON_RELEASE);
439 PRINT_XCB_EVENT(XCB_MOTION_NOTIFY);
440 PRINT_XCB_EVENT(XCB_ENTER_NOTIFY);
441 PRINT_XCB_EVENT(XCB_LEAVE_NOTIFY);
442 PRINT_XCB_EVENT(XCB_FOCUS_IN);
443 PRINT_XCB_EVENT(XCB_FOCUS_OUT);
444 PRINT_XCB_EVENT(XCB_KEYMAP_NOTIFY);
445 PRINT_XCB_EVENT(XCB_EXPOSE);
446 PRINT_XCB_EVENT(XCB_GRAPHICS_EXPOSURE);
447 PRINT_XCB_EVENT(XCB_VISIBILITY_NOTIFY);
448 PRINT_XCB_EVENT(XCB_CREATE_NOTIFY);
449 PRINT_XCB_EVENT(XCB_DESTROY_NOTIFY);
450 PRINT_XCB_EVENT(XCB_UNMAP_NOTIFY);
451 PRINT_XCB_EVENT(XCB_MAP_NOTIFY);
452 PRINT_XCB_EVENT(XCB_MAP_REQUEST);
453 PRINT_XCB_EVENT(XCB_REPARENT_NOTIFY);
454 PRINT_XCB_EVENT(XCB_CONFIGURE_NOTIFY);
455 PRINT_XCB_EVENT(XCB_CONFIGURE_REQUEST);
456 PRINT_XCB_EVENT(XCB_GRAVITY_NOTIFY);
457 PRINT_XCB_EVENT(XCB_RESIZE_REQUEST);
458 PRINT_XCB_EVENT(XCB_CIRCULATE_NOTIFY);
459 PRINT_XCB_EVENT(XCB_CIRCULATE_REQUEST);
460 PRINT_XCB_EVENT(XCB_PROPERTY_NOTIFY);
461 PRINT_XCB_EVENT(XCB_SELECTION_CLEAR);
462 PRINT_XCB_EVENT(XCB_SELECTION_REQUEST);
463 PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY);
464 PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY);
465 PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE);
466 PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY);
468 qDebug("QXcbConnection: %s: unknown event - response_type: %d - sequence: %d", message, int(event->response_type & ~0x80), int(event->sequence));
476 const char *xcb_errors[] =
499 const char *xcb_protocol_request_codes[] =
503 "ChangeWindowAttributes",
504 "GetWindowAttributes",
531 "ChangeActivePointerGrab",
581 "CopyColormapAndFree",
584 "ListInstalledColormaps",
601 "ChangeKeyboardMapping",
602 "GetKeyboardMapping",
603 "ChangeKeyboardControl",
604 "GetKeyboardControl",
606 "ChangePointerControl",
619 "SetModifierMapping",
620 "GetModifierMapping",
625 void QXcbConnection::log(const char *file, int line, int sequence)
627 QMutexLocker locker(&m_callLogMutex);
629 info.sequence = sequence;
636 void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
638 uint clamped_error_code = qMin<uint>(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1);
639 uint clamped_major_code = qMin<uint>(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1);
641 qWarning("QXcbConnection: XCB error: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d",
642 int(error->error_code), xcb_errors[clamped_error_code],
643 int(error->sequence), int(error->resource_id),
644 int(error->major_code), xcb_protocol_request_codes[clamped_major_code],
645 int(error->minor_code));
647 QMutexLocker locker(&m_callLogMutex);
649 for (; i < m_callLog.size(); ++i) {
650 if (m_callLog.at(i).sequence == error->sequence) {
651 qDebug("Caused by: %s:%d", qPrintable(m_callLog.at(i).file), m_callLog.at(i).line);
653 } else if (m_callLog.at(i).sequence > error->sequence) {
654 qDebug("Caused some time before: %s:%d", qPrintable(m_callLog.at(i).file), m_callLog.at(i).line);
656 qDebug("and after: %s:%d", qPrintable(m_callLog.at(i-1).file), m_callLog.at(i-1).line);
660 if (i == m_callLog.size() && !m_callLog.isEmpty())
661 qDebug("Caused some time after: %s:%d", qPrintable(m_callLog.first().file), m_callLog.first().line);
665 void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
669 QMutexLocker locker(&m_callLogMutex);
671 for (; i < m_callLog.size(); ++i)
672 if (m_callLog.at(i).sequence >= event->sequence)
674 m_callLog.remove(0, i);
679 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
680 bool handled = dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), event, &result);
682 uint response_type = event->response_type & ~0x80;
685 switch (response_type) {
687 HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
688 case XCB_BUTTON_PRESS:
689 HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
690 case XCB_BUTTON_RELEASE:
691 HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
692 case XCB_MOTION_NOTIFY:
693 HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
694 case XCB_CONFIGURE_NOTIFY:
695 HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
697 HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
698 case XCB_UNMAP_NOTIFY:
699 HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
700 case XCB_CLIENT_MESSAGE:
701 handleClientMessageEvent((xcb_client_message_event_t *)event);
702 case XCB_ENTER_NOTIFY:
703 HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
704 case XCB_LEAVE_NOTIFY:
705 HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
707 HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
709 HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
711 HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
712 case XCB_KEY_RELEASE:
713 HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
714 case XCB_MAPPING_NOTIFY:
715 m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event);
717 case XCB_SELECTION_REQUEST:
719 xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event;
720 #ifndef QT_NO_DRAGANDDROP
721 if (sr->selection == atom(QXcbAtom::XdndSelection))
722 m_drag->handleSelectionRequest(sr);
726 #ifndef QT_NO_CLIPBOARD
727 m_clipboard->handleSelectionRequest(sr);
732 case XCB_SELECTION_CLEAR:
733 setTime(((xcb_selection_clear_event_t *)event)->time);
734 #ifndef QT_NO_CLIPBOARD
735 m_clipboard->handleSelectionClearRequest((xcb_selection_clear_event_t *)event);
739 case XCB_SELECTION_NOTIFY:
740 setTime(((xcb_selection_notify_event_t *)event)->time);
743 case XCB_PROPERTY_NOTIFY:
744 HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
746 #ifdef XCB_USE_XINPUT2_MAEMO
748 handleGenericEventMaemo((xcb_ge_event_t*)event);
750 #elif defined(XCB_USE_XINPUT2)
753 xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
763 if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
764 setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp);
765 #ifndef QT_NO_CLIPBOARD
766 m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event);
769 } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
771 xcb_randr_screen_change_notify_event_t *change_event = (xcb_randr_screen_change_notify_event_t *)event;
772 foreach (QXcbScreen *s, m_screens) {
773 if (s->root() == change_event->root ) {
774 s->handleScreenChange(change_event);
775 s->updateRefreshRate();
784 // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any.
785 // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events.
787 Display *xdisplay = (Display *)m_xlib_display;
788 XLockDisplay(xdisplay);
789 Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(xdisplay, response_type, 0);
791 XESetWireToEvent(xdisplay, response_type, proc);
793 event->sequence = LastKnownRequestProcessed(m_xlib_display);
794 proc(xdisplay, &dummy, (xEvent*)event);
796 XUnlockDisplay(xdisplay);
801 printXcbEvent("Handled XCB event", event);
803 printXcbEvent("Unhandled XCB event", event);
806 void QXcbConnection::addPeekFunc(PeekFunc f)
808 m_peekFuncs.append(f);
811 QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
812 : m_connection(connection)
813 , m_xcb_poll_for_queued_event(0)
816 m_xcb_poll_for_queued_event = (XcbPollForQueuedEventFunctionPointer)dlsym(RTLD_DEFAULT, "xcb_poll_for_queued_event");
820 if (m_xcb_poll_for_queued_event)
821 qDebug("Using threaded event reader with xcb_poll_for_queued_event");
825 bool QXcbEventReader::startThread()
827 if (m_xcb_poll_for_queued_event) {
835 void QXcbEventReader::run()
837 xcb_generic_event_t *event;
838 while (m_connection && (event = xcb_wait_for_event(m_connection->xcb_connection()))) {
841 while (m_connection && (event = m_xcb_poll_for_queued_event(m_connection->xcb_connection())))
847 for (int i = 0; i < m_events.size(); ++i)
848 free(m_events.at(i));
851 void QXcbEventReader::addEvent(xcb_generic_event_t *event)
853 if ((event->response_type & ~0x80) == XCB_CLIENT_MESSAGE
854 && ((xcb_client_message_event_t *)event)->type == m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION))
859 QXcbEventArray *QXcbEventReader::lock()
862 if (!m_xcb_poll_for_queued_event) {
863 while (xcb_generic_event_t *event = xcb_poll_for_event(m_connection->xcb_connection()))
869 void QXcbEventReader::unlock()
874 void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
876 xcb_client_message_event_t event;
877 memset(&event, 0, sizeof(event));
879 event.response_type = XCB_CLIENT_MESSAGE;
882 event.window = m_connectionEventListener;
883 event.type = atom(a);
884 event.data.data32[0] = id;
886 Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
887 xcb_flush(xcb_connection());
892 class PropertyNotifyEvent {
894 PropertyNotifyEvent(xcb_window_t win, xcb_atom_t property)
895 : window(win), type(XCB_PROPERTY_NOTIFY), atom(property) {}
899 bool checkEvent(xcb_generic_event_t *event) const {
902 if ((event->response_type & ~0x80) != type) {
905 xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
906 if ((pn->window == window) && (pn->atom == atom))
914 xcb_timestamp_t QXcbConnection::getTimestamp()
916 // send a dummy event to myself to get the timestamp from X server.
917 xcb_window_t rootWindow = screens().at(primaryScreen())->root();
918 xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, rootWindow, atom(QXcbAtom::CLIP_TEMPORARY),
919 XCB_ATOM_INTEGER, 32, 0, NULL);
921 connection()->flush();
922 PropertyNotifyEvent checker(rootWindow, atom(QXcbAtom::CLIP_TEMPORARY));
924 xcb_generic_event_t *event = 0;
925 // lets keep this inside a loop to avoid a possible race condition, where
926 // reader thread has not yet had the time to acquire the mutex in order
927 // to add the new set of events to its event queue
929 connection()->sync();
930 if ((event = checkEvent(checker)))
934 xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
935 xcb_timestamp_t timestamp = pn->time;
938 xcb_delete_property(xcb_connection(), rootWindow, atom(QXcbAtom::CLIP_TEMPORARY));
943 void QXcbConnection::processXcbEvents()
945 int connection_error = xcb_connection_has_error(xcb_connection());
946 if (connection_error) {
947 qWarning("The X11 connection broke (error %d). Did the X11 server die?", connection_error);
951 QXcbEventArray *eventqueue = m_reader->lock();
953 for(int i = 0; i < eventqueue->size(); ++i) {
954 xcb_generic_event_t *event = eventqueue->at(i);
957 (*eventqueue)[i] = 0;
959 uint response_type = event->response_type & ~0x80;
961 if (!response_type) {
962 handleXcbError((xcb_generic_error_t *)event);
964 if (response_type == XCB_MOTION_NOTIFY) {
965 // compress multiple motion notify events in a row
966 // to avoid swamping the event queue
967 xcb_generic_event_t *next = eventqueue->value(i+1, 0);
968 if (next && (next->response_type & ~0x80) == XCB_MOTION_NOTIFY)
972 if (response_type == XCB_CONFIGURE_NOTIFY) {
973 // compress multiple configure notify events for the same window
975 for (int j = i; j < eventqueue->size(); ++j) {
976 xcb_generic_event_t *other = eventqueue->at(j);
977 if (other && (other->response_type & ~0x80) == XCB_CONFIGURE_NOTIFY
978 && ((xcb_configure_notify_event_t *)other)->event == ((xcb_configure_notify_event_t *)event)->event)
988 QVector<PeekFunc>::iterator it = m_peekFuncs.begin();
989 while (it != m_peekFuncs.end()) {
990 // These callbacks return true if the event is what they were
991 // waiting for, remove them from the list in that case.
993 it = m_peekFuncs.erase(it);
998 handleXcbEvent(event);
1005 eventqueue->clear();
1009 // Indicate with a null event that the event the callbacks are waiting for
1010 // is not in the queue currently.
1011 Q_FOREACH (PeekFunc f, m_peekFuncs)
1013 m_peekFuncs.clear();
1015 xcb_flush(xcb_connection());
1018 void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *event)
1020 if (event->format != 32)
1023 #ifndef QT_NO_DRAGANDDROP
1024 if (event->type == atom(QXcbAtom::XdndStatus)) {
1025 drag()->handleStatus(event);
1026 } else if (event->type == atom(QXcbAtom::XdndFinished)) {
1027 drag()->handleFinished(event);
1031 QXcbWindow *window = platformWindowFromId(event->window);
1035 window->handleClientMessageEvent(event);
1038 xcb_generic_event_t *QXcbConnection::checkEvent(int type)
1040 QXcbEventArray *eventqueue = m_reader->lock();
1042 for (int i = 0; i < eventqueue->size(); ++i) {
1043 xcb_generic_event_t *event = eventqueue->at(i);
1044 if (event && event->response_type == type) {
1045 (*eventqueue)[i] = 0;
1056 static const char * xcb_atomnames = {
1057 // window-manager <-> client protocols
1059 "WM_DELETE_WINDOW\0"
1062 "_NET_WM_CONTEXT_HELP\0"
1063 "_NET_WM_SYNC_REQUEST\0"
1064 "_NET_WM_SYNC_REQUEST_COUNTER\0"
1066 // ICCCM window state
1070 // Session management
1071 "WM_CLIENT_LEADER\0"
1084 "_QT_CLIPBOARD_SENTINEL\0"
1085 "_QT_SELECTION_SENTINEL\0"
1086 "CLIPBOARD_MANAGER\0"
1088 "RESOURCE_MANAGER\0"
1093 "_QT_INPUT_ENCODING\0"
1095 "_QT_CLOSE_CONNECTION\0"
1100 "ENLIGHTENMENT_DESKTOP\0"
1102 "_SGI_DESKS_MANAGER\0"
1106 "_NET_VIRTUAL_ROOTS\0"
1109 "_NET_MOVERESIZE_WINDOW\0"
1110 "_NET_WM_MOVERESIZE\0"
1113 "_NET_WM_ICON_NAME\0"
1118 "_NET_WM_WINDOW_OPACITY\0"
1121 "_NET_WM_STATE_ABOVE\0"
1122 "_NET_WM_STATE_BELOW\0"
1123 "_NET_WM_STATE_FULLSCREEN\0"
1124 "_NET_WM_STATE_MAXIMIZED_HORZ\0"
1125 "_NET_WM_STATE_MAXIMIZED_VERT\0"
1126 "_NET_WM_STATE_MODAL\0"
1127 "_NET_WM_STATE_STAYS_ON_TOP\0"
1128 "_NET_WM_STATE_DEMANDS_ATTENTION\0"
1130 "_NET_WM_USER_TIME\0"
1131 "_NET_WM_USER_TIME_WINDOW\0"
1132 "_NET_WM_FULL_PLACEMENT\0"
1134 "_NET_WM_WINDOW_TYPE\0"
1135 "_NET_WM_WINDOW_TYPE_DESKTOP\0"
1136 "_NET_WM_WINDOW_TYPE_DOCK\0"
1137 "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
1138 "_NET_WM_WINDOW_TYPE_MENU\0"
1139 "_NET_WM_WINDOW_TYPE_UTILITY\0"
1140 "_NET_WM_WINDOW_TYPE_SPLASH\0"
1141 "_NET_WM_WINDOW_TYPE_DIALOG\0"
1142 "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
1143 "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
1144 "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
1145 "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
1146 "_NET_WM_WINDOW_TYPE_COMBO\0"
1147 "_NET_WM_WINDOW_TYPE_DND\0"
1148 "_NET_WM_WINDOW_TYPE_NORMAL\0"
1149 "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
1151 "_KDE_NET_WM_FRAME_STRUT\0"
1153 "_NET_STARTUP_INFO\0"
1154 "_NET_STARTUP_INFO_BEGIN\0"
1156 "_NET_SUPPORTING_WM_CHECK\0"
1160 "_NET_SYSTEM_TRAY_VISUAL\0"
1162 "_NET_ACTIVE_WINDOW\0"
1187 "XdndActionPrivate\0"
1190 "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
1191 "_MOTIF_DRAG_INITIATOR_INFO\0"
1192 "_MOTIF_DRAG_RECEIVER_INFO\0"
1193 "_MOTIF_DRAG_WINDOW\0"
1194 "_MOTIF_DRAG_TARGETS\0"
1196 "XmTRANSFER_SUCCESS\0"
1197 "XmTRANSFER_FAILURE\0"
1200 "_XKB_RULES_NAMES\0"
1211 "Button Wheel Down\0"
1212 "Button Horiz Wheel Left\0"
1213 "Button Horiz Wheel Right\0"
1214 "Abs MT Position X\0"
1215 "Abs MT Position Y\0"
1216 "Abs MT Touch Major\0"
1217 "Abs MT Touch Minor\0"
1219 "Abs MT Tracking ID\0"
1229 "Wacom Serial IDs\0"
1231 #if XCB_USE_MAEMO_WINDOW_PROPERTIES
1232 "_MEEGOTOUCH_ORIENTATION_ANGLE\0"
1236 xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom)
1238 return m_allAtoms[atom];
1241 void QXcbConnection::initializeAllAtoms() {
1242 const char *names[QXcbAtom::NAtoms];
1243 const char *ptr = xcb_atomnames;
1253 Q_ASSERT(i == QXcbAtom::NPredefinedAtoms);
1255 QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_");
1256 settings_atom_name += m_displayName;
1257 names[i++] = settings_atom_name;
1259 xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms];
1261 Q_ASSERT(i == QXcbAtom::NAtoms);
1262 for (i = 0; i < QXcbAtom::NAtoms; ++i)
1263 cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
1265 for (i = 0; i < QXcbAtom::NAtoms; ++i) {
1266 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0);
1267 m_allAtoms[i] = reply->atom;
1272 xcb_atom_t QXcbConnection::internAtom(const char *name)
1274 if (!name || *name == 0)
1277 xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name);
1278 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0);
1279 int atom = reply->atom;
1284 QByteArray QXcbConnection::atomName(xcb_atom_t atom)
1287 return QByteArray();
1289 xcb_generic_error_t *error = 0;
1290 xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom));
1291 xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error);
1293 qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
1297 QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
1301 return QByteArray();
1304 const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const
1306 xcb_format_iterator_t iterator =
1307 xcb_setup_pixmap_formats_iterator(m_setup);
1309 while (iterator.rem) {
1310 xcb_format_t *format = iterator.data;
1311 if (format->depth == depth)
1313 xcb_format_next(&iterator);
1319 void QXcbConnection::sync()
1321 // from xcb_aux_sync
1322 xcb_get_input_focus_cookie_t cookie = Q_XCB_CALL(xcb_get_input_focus(xcb_connection()));
1323 free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0));
1326 void QXcbConnection::initializeXFixes()
1328 xcb_generic_error_t *error = 0;
1329 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id);
1330 if (!reply || !reply->present)
1333 xfixes_first_event = reply->first_event;
1334 xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection,
1335 XCB_XFIXES_MAJOR_VERSION,
1336 XCB_XFIXES_MINOR_VERSION);
1337 xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection,
1338 xfixes_query_cookie, &error);
1339 if (!xfixes_query || error || xfixes_query->major_version < 2) {
1340 qWarning("QXcbConnection: Failed to initialize XFixes");
1342 xfixes_first_event = 0;
1347 void QXcbConnection::initializeXRender()
1349 #ifdef XCB_USE_RENDER
1350 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_render_id);
1351 if (!reply || !reply->present)
1354 xcb_generic_error_t *error = 0;
1355 xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection,
1356 XCB_RENDER_MAJOR_VERSION,
1357 XCB_RENDER_MINOR_VERSION);
1358 xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection,
1359 xrender_query_cookie, &error);
1360 if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
1361 qWarning("QXcbConnection: Failed to initialize XRender");
1364 free(xrender_query);
1368 void QXcbConnection::initializeGLX()
1370 #ifdef XCB_HAS_XCB_GLX
1371 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_glx_id);
1372 if (!reply || !reply->present)
1375 has_glx_extension = true;
1377 xcb_generic_error_t *error = 0;
1378 xcb_glx_query_version_cookie_t xglx_query_cookie = xcb_glx_query_version(m_connection,
1379 XCB_GLX_MAJOR_VERSION,
1380 XCB_GLX_MINOR_VERSION);
1381 xcb_glx_query_version_reply_t *xglx_query = xcb_glx_query_version_reply(m_connection,
1382 xglx_query_cookie, &error);
1383 if (!xglx_query || error) {
1384 qWarning("QXcbConnection: Failed to initialize GLX");
1386 has_glx_extension = false;
1390 // no way to check, assume GLX is present
1391 has_glx_extension = true;
1395 void QXcbConnection::initializeXRandr()
1397 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_randr_id);
1398 if (!reply || !reply->present)
1401 xrandr_first_event = reply->first_event;
1403 xcb_generic_error_t *error = 0;
1404 xcb_randr_query_version_cookie_t xrandr_query_cookie = xcb_randr_query_version(m_connection,
1405 XCB_RANDR_MAJOR_VERSION,
1406 XCB_RANDR_MINOR_VERSION);
1408 has_randr_extension = true;
1410 xcb_randr_query_version_reply_t *xrandr_query = xcb_randr_query_version_reply(m_connection,
1411 xrandr_query_cookie, &error);
1412 if (!xrandr_query || error || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
1413 qWarning("QXcbConnection: Failed to initialize XRandr");
1415 has_randr_extension = false;
1420 void QXcbConnection::initializeXShape()
1422 const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id);
1423 if (!xshape_reply || !xshape_reply->present)
1426 has_shape_extension = true;
1427 xcb_shape_query_version_cookie_t cookie = xcb_shape_query_version(m_connection);
1428 xcb_shape_query_version_reply_t *shape_query = xcb_shape_query_version_reply(m_connection,
1431 qWarning("QXcbConnection: Failed to initialize SHAPE extension");
1432 } else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) {
1433 // The input shape is the only thing added in SHAPE 1.1
1434 has_input_shape = true;
1439 #if defined(XCB_USE_EGL)
1440 bool QXcbConnection::hasEgl() const
1444 #endif // defined(XCB_USE_EGL)
1446 #if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
1447 // Borrowed from libXi.
1448 int QXcbConnection::xi2CountBits(unsigned char *ptr, int len)
1454 for (i = 0; i < len; i++) {
1464 bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value)
1466 xXIDeviceEvent *xideviceevent = static_cast<xXIDeviceEvent *>(event);
1467 unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1];
1468 unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
1469 FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
1470 int numValuatorValues = xi2CountBits(valuatorsMaskAddr, xideviceevent->valuators_len * 4);
1471 // This relies on all bit being set until a certain number i.e. it doesn't support only bit 0 and 5 being set in the mask.
1472 // Just like the original code, works for now.
1473 if (valuatorNum >= numValuatorValues)
1475 *value = valuatorsValuesAddr[valuatorNum].integral;
1476 *value += ((double)valuatorsValuesAddr[valuatorNum].frac / (1 << 16) / (1 << 16));
1480 bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode)
1482 // xGenericEvent has "extension" on the second byte, xcb_ge_event_t has "pad0".
1483 if (event->pad0 == opCode) {
1484 // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
1485 // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
1486 // Move this data back to have the same layout in memory as it was on the wire
1487 // and allow casting, overwriting the full_sequence field.
1488 memmove((char*) event + 32, (char*) event + 36, event->length * 4);
1493 #endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)